1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security.saml;
21  
22  import org.apache.ws.security.WSConstants;
23  import org.apache.ws.security.WSDataRef;
24  import org.apache.ws.security.WSSConfig;
25  import org.apache.ws.security.WSSecurityEngine;
26  import org.apache.ws.security.WSSecurityEngineResult;
27  import org.apache.ws.security.common.CustomHandler;
28  import org.apache.ws.security.common.KeystoreCallbackHandler;
29  import org.apache.ws.security.common.SAML1CallbackHandler;
30  import org.apache.ws.security.common.SAML2CallbackHandler;
31  import org.apache.ws.security.common.SOAPUtil;
32  import org.apache.ws.security.components.crypto.Crypto;
33  import org.apache.ws.security.components.crypto.CryptoFactory;
34  import org.apache.ws.security.handler.RequestData;
35  import org.apache.ws.security.handler.WSHandlerConstants;
36  import org.apache.ws.security.message.WSSecHeader;
37  import org.apache.ws.security.saml.ext.AssertionWrapper;
38  import org.apache.ws.security.saml.ext.SAMLParms;
39  import org.apache.ws.security.saml.ext.builder.SAML1Constants;
40  import org.apache.ws.security.saml.ext.builder.SAML2Constants;
41  import org.apache.ws.security.util.WSSecurityUtil;
42  
43  import org.w3c.dom.Document;
44  
45  import java.util.List;
46  
47  import javax.security.auth.callback.CallbackHandler;
48  
49  /**
50   * Test-case for sending and processing a signed (sender vouches) SAML Assertion.
51   */
52  public class SamlTokenSVTest extends org.junit.Assert {
53      private static final org.apache.commons.logging.Log LOG = 
54          org.apache.commons.logging.LogFactory.getLog(SamlTokenSVTest.class);
55      private WSSecurityEngine secEngine = new WSSecurityEngine();
56      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
57      private Crypto crypto = null;
58      
59      public SamlTokenSVTest() throws Exception {
60          WSSConfig.init();
61          crypto = CryptoFactory.getInstance("crypto.properties");
62      }
63      
64      /**
65       * Test that creates, sends and processes a signed SAML 1.1 authentication assertion.
66       */
67      @org.junit.Test
68      @SuppressWarnings("unchecked")
69      public void testSAML1AuthnAssertion() throws Exception {
70          SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
71          callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
72          callbackHandler.setConfirmationMethod(SAML1Constants.CONF_SENDER_VOUCHES);
73          callbackHandler.setIssuer("www.example.com");
74          
75          SAMLParms samlParms = new SAMLParms();
76          samlParms.setCallbackHandler(callbackHandler);
77          AssertionWrapper assertion = new AssertionWrapper(samlParms);
78          
79          WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
80          wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
81          
82          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
83          WSSecHeader secHeader = new WSSecHeader();
84          secHeader.insertSecurityHeader(doc);
85          
86          Document signedDoc = 
87              wsSign.build(
88                  doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
89                  "security", secHeader
90              );
91  
92          if (LOG.isDebugEnabled()) {
93              LOG.debug("SAML 1.1 Authn Assertion (sender vouches):");
94              String outputString = 
95                  org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
96              LOG.debug(outputString);
97          }
98          
99          // Test we processed a SAML assertion
100         List<WSSecurityEngineResult> results = verify(signedDoc);
101         WSSecurityEngineResult actionResult =
102             WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
103         AssertionWrapper receivedAssertion = 
104             (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
105         assertTrue(receivedAssertion != null);
106         
107         // Test we processed a signature (SAML assertion + SOAP body)
108         actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
109         assertTrue(actionResult != null);
110         assertFalse(actionResult.isEmpty());
111         final List<WSDataRef> refs =
112             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
113         assertTrue(refs.size() == 2);
114         
115         WSDataRef wsDataRef = (WSDataRef)refs.get(0);
116         String xpath = wsDataRef.getXpath();
117         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
118         
119         wsDataRef = (WSDataRef)refs.get(1);
120         xpath = wsDataRef.getXpath();
121         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml1:Assertion", xpath);
122     }
123     
124     /**
125      * Test that creates, sends and processes a signed SAML 1 authentication assertion, where
126      * the configuration is loaded from a properties file
127      */
128     @org.junit.Test
129     @SuppressWarnings("unchecked")
130     public void testSAML1AuthnAssertionFromProperties() throws Exception {
131         SAMLIssuer saml = SAMLIssuerFactory.getInstance("saml_sv.properties");
132         AssertionWrapper assertion = saml.newAssertion();
133 
134         WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
135         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
136         
137         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
138         WSSecHeader secHeader = new WSSecHeader();
139         secHeader.insertSecurityHeader(doc);
140         
141         Document signedDoc = 
142             wsSign.build(
143                 doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
144                 "security", secHeader
145             );
146 
147         if (LOG.isDebugEnabled()) {
148             LOG.debug("SAML 1.1 Authn Assertion (sender vouches):");
149             String outputString = 
150                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
151             LOG.debug(outputString);
152         }
153         
154         // Test we processed a SAML assertion
155         List<WSSecurityEngineResult> results = verify(signedDoc);
156         WSSecurityEngineResult actionResult =
157             WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
158         AssertionWrapper receivedAssertion = 
159             (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
160         assertTrue(receivedAssertion != null);
161         
162         // Test we processed a signature (SAML assertion + SOAP body)
163         actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
164         assertTrue(actionResult != null);
165         assertFalse(actionResult.isEmpty());
166         final List<WSDataRef> refs =
167             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
168         assertTrue(refs.size() == 2);
169         
170         WSDataRef wsDataRef = (WSDataRef)refs.get(0);
171         String xpath = wsDataRef.getXpath();
172         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
173         
174         wsDataRef = (WSDataRef)refs.get(1);
175         xpath = wsDataRef.getXpath();
176         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml1:Assertion", xpath);
177     }
178     
179     /**
180      * Test that creates, sends and processes a signed SAML 1.1 attribute assertion.
181      */
182     @org.junit.Test
183     @SuppressWarnings("unchecked")
184     public void testSAML1AttrAssertion() throws Exception {
185         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
186         callbackHandler.setStatement(SAML1CallbackHandler.Statement.ATTR);
187         callbackHandler.setConfirmationMethod(SAML1Constants.CONF_SENDER_VOUCHES);
188         callbackHandler.setIssuer("www.example.com");
189         
190         SAMLParms samlParms = new SAMLParms();
191         samlParms.setCallbackHandler(callbackHandler);
192         AssertionWrapper assertion = new AssertionWrapper(samlParms);
193 
194         WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
195         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
196         
197         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
198         WSSecHeader secHeader = new WSSecHeader();
199         secHeader.insertSecurityHeader(doc);
200         
201         Document signedDoc = 
202             wsSign.build(
203                 doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
204                 "security", secHeader
205             );
206 
207         if (LOG.isDebugEnabled()) {
208             LOG.debug("SAML 1.1 Attr Assertion (sender vouches):");
209             String outputString = 
210                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
211             LOG.debug(outputString);
212         }
213         
214         // Test we processed a SAML assertion
215         List<WSSecurityEngineResult> results = verify(signedDoc);
216         WSSecurityEngineResult actionResult =
217             WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
218         AssertionWrapper receivedAssertion = 
219             (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
220         assertTrue(receivedAssertion != null);
221         
222         // Test we processed a signature (SAML assertion + SOAP body)
223         actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
224         assertTrue(actionResult != null);
225         assertFalse(actionResult.isEmpty());
226         final List<WSDataRef> refs =
227             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
228         assertTrue(refs.size() == 2);
229         
230         WSDataRef wsDataRef = (WSDataRef)refs.get(0);
231         String xpath = wsDataRef.getXpath();
232         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
233         
234         wsDataRef = (WSDataRef)refs.get(1);
235         xpath = wsDataRef.getXpath();
236         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml1:Assertion", xpath);
237     }
238     
239     /**
240      * Test that creates, sends and processes a signed SAML 2 authentication assertion.
241      */
242     @org.junit.Test
243     @SuppressWarnings("unchecked")
244     public void testSAML2AuthnAssertion() throws Exception {
245         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
246         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
247         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
248         callbackHandler.setIssuer("www.example.com");
249         
250         SAMLParms samlParms = new SAMLParms();
251         samlParms.setCallbackHandler(callbackHandler);
252         AssertionWrapper assertion = new AssertionWrapper(samlParms);
253         WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
254         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
255         
256         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
257         WSSecHeader secHeader = new WSSecHeader();
258         secHeader.insertSecurityHeader(doc);
259         
260         Document signedDoc = 
261             wsSign.build(
262                 doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
263                 "security", secHeader
264             );
265 
266         if (LOG.isDebugEnabled()) {
267             LOG.debug("SAML 2 Authn Assertion (sender vouches):");
268             String outputString = 
269                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
270             LOG.debug(outputString);
271         }
272         
273         // Test we processed a SAML assertion
274         List<WSSecurityEngineResult> results = verify(signedDoc);
275         WSSecurityEngineResult actionResult =
276             WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
277         AssertionWrapper receivedAssertion = 
278             (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
279         assertTrue(receivedAssertion != null);
280         
281         // Test we processed a signature (SAML assertion + SOAP body)
282         actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
283         assertTrue(actionResult != null);
284         assertFalse(actionResult.isEmpty());
285         final List<WSDataRef> refs =
286             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
287         assertTrue(refs.size() == 2);
288         
289         WSDataRef wsDataRef = (WSDataRef)refs.get(0);
290         String xpath = wsDataRef.getXpath();
291         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
292         
293         wsDataRef = (WSDataRef)refs.get(1);
294         xpath = wsDataRef.getXpath();
295         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml2:Assertion", xpath);
296     }
297     
298     /**
299      * Test that creates, sends and processes a signed SAML 2 attribute assertion.
300      */
301     @org.junit.Test
302     @SuppressWarnings("unchecked")
303     public void testSAML2AttrAssertion() throws Exception {
304         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
305         callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
306         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
307         callbackHandler.setIssuer("www.example.com");
308         
309         SAMLParms samlParms = new SAMLParms();
310         samlParms.setCallbackHandler(callbackHandler);
311         AssertionWrapper assertion = new AssertionWrapper(samlParms);
312 
313         WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
314         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
315         
316         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
317         WSSecHeader secHeader = new WSSecHeader();
318         secHeader.insertSecurityHeader(doc);
319         
320         Document signedDoc = 
321             wsSign.build(
322                 doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
323                 "security", secHeader
324             );
325 
326         if (LOG.isDebugEnabled()) {
327             LOG.debug("SAML 2 Attr Assertion (sender vouches):");
328             String outputString = 
329                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
330             LOG.debug(outputString);
331         }
332         
333         // Test we processed a SAML assertion
334         List<WSSecurityEngineResult> results = verify(signedDoc);
335         WSSecurityEngineResult actionResult =
336             WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
337         AssertionWrapper receivedAssertion = 
338             (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
339         assertTrue(receivedAssertion != null);
340         
341         // Test we processed a signature (SAML assertion + SOAP body)
342         actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
343         assertTrue(actionResult != null);
344         assertFalse(actionResult.isEmpty());
345         final List<WSDataRef> refs =
346             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
347         assertTrue(refs.size() == 2);
348         
349         WSDataRef wsDataRef = (WSDataRef)refs.get(0);
350         String xpath = wsDataRef.getXpath();
351         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
352         
353         wsDataRef = (WSDataRef)refs.get(1);
354         xpath = wsDataRef.getXpath();
355         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml2:Assertion", xpath);
356     }
357     
358     /**
359      * Test the default issuer class as specified in SAMLIssuerFactory. The configuration
360      * file "saml3.saml_sv_noissuer.properties" has no "org.apache.ws.security.saml.issuerClass"
361      * property, and so the default value is used (A bad value was previously used for the
362      * default value).
363      */
364     @org.junit.Test
365     public void testDefaultIssuerClass() throws Exception {
366         SAMLIssuer saml = SAMLIssuerFactory.getInstance("saml_sv_noissuer.properties");
367         AssertionWrapper assertion = saml.newAssertion();
368 
369         WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
370         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
371         
372         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
373 
374         WSSecHeader secHeader = new WSSecHeader();
375         secHeader.insertSecurityHeader(doc);
376         
377         Document signedDoc = 
378             wsSign.build(
379                  doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
380                  "security", secHeader
381              );
382 
383         if (LOG.isDebugEnabled()) {
384             LOG.debug("Signed SAML message (sender vouches):");
385             String outputString = 
386                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
387             LOG.debug(outputString);
388         }
389         
390         List<WSSecurityEngineResult> results = verify(signedDoc);
391         WSSecurityEngineResult actionResult =
392             WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
393         AssertionWrapper receivedAssertion = 
394             (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
395         assertTrue(receivedAssertion != null);
396     }
397     
398     
399     /**
400      * A test for WSS-62: "the crypto file not being retrieved in the doReceiverAction
401      * method for the Saml Signed Token"
402      * 
403      * https://issues.apache.org/jira/browse/WSS-62
404      */
405     @org.junit.Test
406     public void testWSS62() throws Exception {
407         SAMLIssuer saml = SAMLIssuerFactory.getInstance("saml_sv.properties");
408         AssertionWrapper assertion = saml.newAssertion();
409 
410         WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
411         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
412 
413         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
414 
415         WSSecHeader secHeader = new WSSecHeader();
416         secHeader.insertSecurityHeader(doc);
417         
418         Document signedDoc = 
419             wsSign.build(
420                 doc, null, assertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", 
421                 "security", secHeader
422             );
423         //
424         // Now verify it but first call Handler#doReceiverAction
425         //
426         final WSSConfig cfg = WSSConfig.getNewInstance();
427         final RequestData reqData = new RequestData();
428         reqData.setWssConfig(cfg);
429         java.util.Map<String, Object> msgContext = new java.util.HashMap<String, Object>();
430         msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
431         reqData.setMsgContext(msgContext);
432         
433         CustomHandler handler = new CustomHandler();
434         handler.receive(WSConstants.ST_SIGNED, reqData);
435         
436         secEngine.processSecurityHeader(
437             signedDoc, null, callbackHandler, reqData.getSigCrypto(), reqData.getDecCrypto()
438         );
439     }
440     
441     
442     /**
443      * Verifies the soap envelope
444      * <p/>
445      * 
446      * @param envelope 
447      * @throws Exception Thrown when there is a problem in verification
448      */
449     private List<WSSecurityEngineResult> verify(Document doc) throws Exception {
450         List<WSSecurityEngineResult> results = 
451             secEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
452         String outputString = 
453             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
454         assertTrue(outputString.indexOf("counter_port_type") > 0 ? true : false);
455         return results;
456     }
457 
458 }