View Javadoc
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.wss4j.dom.saml;
21  
22  import org.apache.wss4j.common.saml.SamlAssertionWrapper;
23  import org.apache.wss4j.common.util.SOAPUtil;
24  import org.apache.wss4j.dom.WSConstants;
25  import org.apache.wss4j.dom.WSDataRef;
26  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
27  import org.apache.wss4j.dom.common.SAML1CallbackHandler;
28  import org.apache.wss4j.dom.common.SAML2CallbackHandler;
29  
30  import org.apache.wss4j.dom.engine.WSSConfig;
31  import org.apache.wss4j.dom.engine.WSSecurityEngine;
32  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
33  import org.apache.wss4j.dom.handler.RequestData;
34  import org.apache.wss4j.dom.handler.WSHandlerResult;
35  import org.apache.wss4j.common.bsp.BSPRule;
36  import org.apache.wss4j.common.crypto.Crypto;
37  import org.apache.wss4j.common.crypto.CryptoFactory;
38  import org.apache.wss4j.common.crypto.CryptoType;
39  import org.apache.wss4j.common.crypto.Merlin;
40  import org.apache.wss4j.common.saml.SAMLCallback;
41  import org.apache.wss4j.common.saml.SAMLUtil;
42  import org.apache.wss4j.common.saml.bean.KeyInfoBean.CERT_IDENTIFIER;
43  import org.apache.wss4j.common.saml.builder.SAML1Constants;
44  import org.apache.wss4j.common.saml.builder.SAML2Constants;
45  import org.apache.wss4j.common.util.Loader;
46  import org.apache.wss4j.common.util.XMLUtils;
47  import org.apache.wss4j.dom.message.WSSecHeader;
48  import org.apache.wss4j.dom.util.WSSecurityUtil;
49  
50  import org.junit.jupiter.api.Test;
51  import org.w3c.dom.Document;
52  import org.w3c.dom.Element;
53  
54  import javax.security.auth.callback.CallbackHandler;
55  import javax.xml.crypto.XMLStructure;
56  import javax.xml.crypto.dom.DOMCryptoContext;
57  import javax.xml.crypto.dom.DOMStructure;
58  import javax.xml.crypto.dsig.XMLSignatureFactory;
59  import javax.xml.crypto.dsig.keyinfo.KeyInfo;
60  import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
61  import javax.xml.crypto.dsig.keyinfo.KeyValue;
62  import javax.xml.crypto.dsig.keyinfo.X509Data;
63  import javax.xml.parsers.DocumentBuilder;
64  import javax.xml.parsers.DocumentBuilderFactory;
65  
66  import java.io.InputStream;
67  import java.security.KeyStore;
68  import java.security.Principal;
69  import java.security.cert.X509Certificate;
70  import java.util.ArrayList;
71  import java.util.Arrays;
72  import java.util.Collections;
73  import java.util.List;
74  
75  import static org.junit.jupiter.api.Assertions.assertEquals;
76  import static org.junit.jupiter.api.Assertions.assertFalse;
77  import static org.junit.jupiter.api.Assertions.assertNotNull;
78  import static org.junit.jupiter.api.Assertions.assertTrue;
79  
80  /**
81   * Test-case for sending and processing a signed (holder-of-key) SAML Assertion. These tests
82   * also cover the case of using the credential information in the SAML Subject to sign the
83   * SOAP body.
84   */
85  public class SignedSamlTokenHOKTest {
86      private static final org.slf4j.Logger LOG =
87          org.slf4j.LoggerFactory.getLogger(SignedSamlTokenHOKTest.class);
88      private WSSecurityEngine secEngine = new WSSecurityEngine();
89      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
90      private Crypto trustCrypto;
91      private Crypto issuerCrypto;
92      private Crypto userCrypto = CryptoFactory.getInstance("wss40.properties");
93  
94      public SignedSamlTokenHOKTest() throws Exception {
95          WSSConfig.init();
96          // Load the issuer keystore
97          issuerCrypto = new Merlin();
98          KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
99          ClassLoader loader = Loader.getClassLoader(SignedSamlTokenHOKTest.class);
100         InputStream input = Merlin.loadInputStream(loader, "keys/wss40_server.jks");
101         keyStore.load(input, "security".toCharArray());
102         input.close();
103         ((Merlin)issuerCrypto).setKeyStore(keyStore);
104 
105         // Load the server truststore
106         trustCrypto = new Merlin();
107         KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
108         input = Merlin.loadInputStream(loader, "keys/wss40CA.jks");
109         trustStore.load(input, "security".toCharArray());
110         input.close();
111         ((Merlin)trustCrypto).setTrustStore(trustStore);
112     }
113 
114     /**
115      * Test that creates, sends and processes a signed SAML 1.1 authentication assertion.
116      */
117     @Test
118     @SuppressWarnings("unchecked")
119     public void testSAML1AuthnAssertion() throws Exception {
120         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
121         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
122         callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
123         callbackHandler.setIssuer("www.example.com");
124 
125         SAMLCallback samlCallback = new SAMLCallback();
126         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
127         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
128 
129         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
130 
131         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
132         WSSecHeader secHeader = new WSSecHeader(doc);
133         secHeader.insertSecurityHeader();
134 
135         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
136         wsSign.setUserInfo("wss40", "security");
137         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
138         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
139         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
140 
141         Document signedDoc =
142             wsSign.build(userCrypto, samlAssertion, null, null, null);
143 
144         String outputString =
145             XMLUtils.prettyDocumentToString(signedDoc);
146         if (LOG.isDebugEnabled()) {
147             LOG.debug("Signed SAML 1.1 Authn Assertion (key holder):");
148             LOG.debug(outputString);
149         }
150         assertTrue(outputString.contains("http://www.w3.org/2001/04/xmlenc#sha256"));
151         assertTrue(outputString.contains("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"));
152 
153         WSHandlerResult results = verify(signedDoc, trustCrypto);
154 
155         // Test we processed a SAML assertion
156         WSSecurityEngineResult actionResult =
157             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
158         SamlAssertionWrapper receivedSamlAssertion =
159             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
160         assertNotNull(receivedSamlAssertion);
161         assertTrue(receivedSamlAssertion.isSigned());
162         assertNotNull(receivedSamlAssertion.getSignatureValue());
163 
164         // Test we have a WSDataRef for the signed SAML token as well
165         List<WSDataRef> refs =
166             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
167         assertTrue(refs.size() == 1);
168 
169         WSDataRef wsDataRef = refs.get(0);
170         String xpath = wsDataRef.getXpath();
171         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml1:Assertion", xpath);
172 
173         // Test we processed a signature (SOAP body)
174         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
175         assertNotNull(actionResult);
176         assertFalse(actionResult.isEmpty());
177         refs = (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
178         assertTrue(refs.size() == 1);
179 
180         wsDataRef = refs.get(0);
181         xpath = wsDataRef.getXpath();
182         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
183     }
184 
185     /**
186      * Test that creates, sends and processes a signed SAML 1.1 attribute assertion.
187      */
188     @Test
189     public void testSAML1AttrAssertion() throws Exception {
190         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
191         callbackHandler.setStatement(SAML1CallbackHandler.Statement.ATTR);
192         callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
193         callbackHandler.setIssuer("www.example.com");
194 
195         SAMLCallback samlCallback = new SAMLCallback();
196         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
197         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
198 
199         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
200         byte[] ephemeralKey = callbackHandler.getEphemeralKey();
201 
202         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
203         WSSecHeader secHeader = new WSSecHeader(doc);
204         secHeader.insertSecurityHeader();
205 
206         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
207         wsSign.setUserInfo("wss40", "security");
208         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
209         wsSign.setSignatureAlgorithm(WSConstants.HMAC_SHA256);
210         wsSign.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
211         wsSign.setSecretKey(ephemeralKey);
212 
213         Document signedDoc =
214             wsSign.build(userCrypto, samlAssertion, null, null, null);
215 
216         String outputString =
217             XMLUtils.prettyDocumentToString(signedDoc);
218         if (LOG.isDebugEnabled()) {
219             LOG.debug("Signed SAML 1.1 Attr Assertion (key holder):");
220             LOG.debug(outputString);
221         }
222 
223         /* https://issues.apache.org/jira/browse/WSS-265 */
224         WSHandlerResult results = verify(signedDoc, trustCrypto);
225 
226         // Test we processed a SAML assertion
227         WSSecurityEngineResult actionResult =
228             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
229         SamlAssertionWrapper receivedAssertion =
230             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
231         assertNotNull(receivedAssertion);
232         assertTrue(receivedAssertion.isSigned());
233 
234         // Test we processed a signature (SOAP body)
235         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
236         assertNotNull(actionResult);
237         assertFalse(actionResult.isEmpty());
238         @SuppressWarnings("unchecked")
239         final List<WSDataRef> refs =
240             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
241         assertTrue(refs.size() == 1);
242 
243         WSDataRef wsDataRef = refs.get(0);
244         String xpath = wsDataRef.getXpath();
245         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
246     }
247 
248     /**
249      * Test that creates, sends and processes a signed SAML 2 authentication assertion.
250      */
251     @Test
252     @SuppressWarnings("unchecked")
253     public void testSAML2AuthnAssertion() throws Exception {
254         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
255         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
256         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
257         callbackHandler.setIssuer("www.example.com");
258 
259         SAMLCallback samlCallback = new SAMLCallback();
260         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
261         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
262 
263         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
264 
265         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
266         WSSecHeader secHeader = new WSSecHeader(doc);
267         secHeader.insertSecurityHeader();
268 
269         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
270         wsSign.setUserInfo("wss40", "security");
271         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
272         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
273         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
274 
275         Document signedDoc =
276             wsSign.build(userCrypto, samlAssertion, null, null, null);
277 
278         String outputString =
279             XMLUtils.prettyDocumentToString(signedDoc);
280         if (LOG.isDebugEnabled()) {
281             LOG.debug("Signed SAML 2 Authn Assertion (key holder):");
282             LOG.debug(outputString);
283         }
284         assertTrue(outputString.contains("http://www.w3.org/2001/04/xmlenc#sha256"));
285         assertTrue(outputString.contains("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"));
286 
287         WSHandlerResult results = verify(signedDoc, trustCrypto);
288 
289         // Test we processed a SAML assertion
290         WSSecurityEngineResult actionResult =
291             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
292         SamlAssertionWrapper receivedSamlAssertion =
293             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
294         assertNotNull(receivedSamlAssertion);
295         assertTrue(receivedSamlAssertion.isSigned());
296 
297         // Test we processed a signature (SOAP body)
298         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
299         assertNotNull(actionResult);
300         assertFalse(actionResult.isEmpty());
301         final List<WSDataRef> refs =
302             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
303         assertTrue(refs.size() == 1);
304 
305         WSDataRef wsDataRef = refs.get(0);
306         String xpath = wsDataRef.getXpath();
307         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
308     }
309 
310     /**
311      * Test that creates, sends and processes a signed SAML 2 attribute assertion.
312      */
313     @Test
314     public void testSAML2AttrAssertion() throws Exception {
315         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
316         callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
317         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
318         callbackHandler.setIssuer("www.example.com");
319 
320         SAMLCallback samlCallback = new SAMLCallback();
321         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
322         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
323 
324         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
325         byte[] ephemeralKey = callbackHandler.getEphemeralKey();
326 
327         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
328         WSSecHeader secHeader = new WSSecHeader(doc);
329         secHeader.insertSecurityHeader();
330 
331         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
332         wsSign.setUserInfo("wss40", "security");
333         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
334         wsSign.setSignatureAlgorithm(WSConstants.HMAC_SHA256);
335         wsSign.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
336         wsSign.setSecretKey(ephemeralKey);
337 
338         Document signedDoc =
339             wsSign.build(userCrypto, samlAssertion, null, null, null);
340 
341         String outputString =
342             XMLUtils.prettyDocumentToString(signedDoc);
343         if (LOG.isDebugEnabled()) {
344             LOG.debug("Signed SAML 2 Attr Assertion (key holder):");
345             LOG.debug(outputString);
346         }
347 
348         /* https://issues.apache.org/jira/browse/WSS-265 */
349         WSHandlerResult results = verify(signedDoc, trustCrypto);
350 
351         // Test we processed a SAML assertion
352         WSSecurityEngineResult actionResult =
353             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
354         SamlAssertionWrapper receivedAssertion =
355             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
356         assertNotNull(receivedAssertion);
357         assertTrue(receivedAssertion.isSigned());
358 
359         // Test we processed a signature (SOAP body)
360         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
361         assertNotNull(actionResult);
362         assertFalse(actionResult.isEmpty());
363         @SuppressWarnings("unchecked")
364         final List<WSDataRef> refs =
365             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
366         assertTrue(refs.size() == 1);
367 
368         WSDataRef wsDataRef = refs.get(0);
369         String xpath = wsDataRef.getXpath();
370         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
371     }
372 
373     /**
374      * Test that creates, sends and processes a signed SAML 1.1 authentication assertion,
375      * where the subject cert is referenced using IssuerSerial
376      */
377     @Test
378     @SuppressWarnings("unchecked")
379     public void testSAML1AuthnAssertionIssuerSerial() throws Exception {
380         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
381         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
382         callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
383         callbackHandler.setCertIdentifier(CERT_IDENTIFIER.X509_ISSUER_SERIAL);
384         callbackHandler.setIssuer("www.example.com");
385 
386         SAMLCallback samlCallback = new SAMLCallback();
387         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
388         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
389 
390         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
391 
392         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
393         WSSecHeader secHeader = new WSSecHeader(doc);
394         secHeader.insertSecurityHeader();
395 
396         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
397         wsSign.setUserInfo("wss40", "security");
398         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
399         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
400         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
401 
402         Document signedDoc =
403             wsSign.build(userCrypto, samlAssertion, null, null, null);
404 
405         String outputString =
406             XMLUtils.prettyDocumentToString(signedDoc);
407         if (LOG.isDebugEnabled()) {
408             LOG.debug("SAML 1.1 Authn Assertion Issuer Serial (holder-of-key):");
409             LOG.debug(outputString);
410         }
411         assertTrue(outputString.contains("X509IssuerSerial"));
412 
413         WSHandlerResult results = verify(signedDoc, userCrypto);
414         WSSecurityEngineResult actionResult =
415             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
416         SamlAssertionWrapper receivedSamlAssertion =
417             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
418         assertNotNull(receivedSamlAssertion);
419         assertTrue(receivedSamlAssertion.isSigned());
420 
421         // Test we processed a signature (SOAP body)
422         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
423         assertNotNull(actionResult);
424         assertFalse(actionResult.isEmpty());
425         final List<WSDataRef> refs =
426             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
427         assertTrue(refs.size() == 1);
428 
429         WSDataRef wsDataRef = refs.get(0);
430         String xpath = wsDataRef.getXpath();
431         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
432     }
433 
434     /**
435      * Test that creates, sends and processes a signed SAML 1.1 authentication assertion,
436      * where the subject cert is referenced using a Key Value
437      */
438     @Test
439     @SuppressWarnings("unchecked")
440     public void testSAML1AuthnAssertionKeyValue() throws Exception {
441         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
442         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
443         callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
444         callbackHandler.setCertIdentifier(CERT_IDENTIFIER.KEY_VALUE);
445         callbackHandler.setIssuer("www.example.com");
446 
447         SAMLCallback samlCallback = new SAMLCallback();
448         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
449         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
450 
451         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
452 
453         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
454         WSSecHeader secHeader = new WSSecHeader(doc);
455         secHeader.insertSecurityHeader();
456 
457         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
458         wsSign.setUserInfo("wss40", "security");
459         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
460         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
461         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
462 
463         Document signedDoc =
464             wsSign.build(userCrypto, samlAssertion, null, null, null);
465 
466         String outputString =
467             XMLUtils.prettyDocumentToString(signedDoc);
468         if (LOG.isDebugEnabled()) {
469             LOG.debug("SAML 1.1 Authn Assertion Key Value (holder-of-key):");
470             LOG.debug(outputString);
471         }
472         assertTrue(outputString.contains("KeyValue"));
473 
474         WSHandlerResult results = verify(signedDoc, userCrypto);
475         WSSecurityEngineResult actionResult =
476             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
477         SamlAssertionWrapper receivedSamlAssertion =
478             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
479         assertNotNull(receivedSamlAssertion);
480         assert receivedSamlAssertion.isSigned();
481 
482         // Test we processed a signature (SOAP body)
483         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
484         assertNotNull(actionResult);
485         assertFalse(actionResult.isEmpty());
486         final List<WSDataRef> refs =
487             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
488         assertTrue(refs.size() == 1);
489 
490         WSDataRef wsDataRef = refs.get(0);
491         String xpath = wsDataRef.getXpath();
492         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
493     }
494 
495     /**
496      * Test that creates, sends and processes a signed SAML 2 authentication assertion,
497      * where the subject cert is referenced using a Key Value
498      */
499     @Test
500     @SuppressWarnings("unchecked")
501     public void testSAML2AuthnAssertionKeyValue() throws Exception {
502         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
503         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
504         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
505         callbackHandler.setCertIdentifier(CERT_IDENTIFIER.KEY_VALUE);
506         callbackHandler.setIssuer("www.example.com");
507 
508         SAMLCallback samlCallback = new SAMLCallback();
509         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
510         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
511 
512         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
513 
514         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
515         WSSecHeader secHeader = new WSSecHeader(doc);
516         secHeader.insertSecurityHeader();
517 
518         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
519         wsSign.setUserInfo("wss40", "security");
520         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
521         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
522         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
523 
524         Document signedDoc =
525             wsSign.build(userCrypto, samlAssertion, null, null, null);
526 
527         String outputString =
528             XMLUtils.prettyDocumentToString(signedDoc);
529         if (LOG.isDebugEnabled()) {
530             LOG.debug("SAML 2 Authn Assertion Key Value (holder-of-key):");
531             LOG.debug(outputString);
532         }
533         assertTrue(outputString.contains("KeyValue"));
534 
535         WSHandlerResult results = verify(signedDoc, userCrypto);
536         WSSecurityEngineResult actionResult =
537             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
538         SamlAssertionWrapper receivedSamlAssertion =
539             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
540         assertNotNull(receivedSamlAssertion);
541         assertTrue(receivedSamlAssertion.isSigned());
542 
543         // Test we processed a signature (SOAP body)
544         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
545         assertNotNull(actionResult);
546         assertFalse(actionResult.isEmpty());
547         final List<WSDataRef> refs =
548             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
549         assertTrue(refs.size() == 1);
550 
551         WSDataRef wsDataRef = refs.get(0);
552         String xpath = wsDataRef.getXpath();
553         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
554     }
555 
556     /**
557      * Test that creates, sends and processes a signed SAML 1.1 authentication assertion.
558      * The difference is that we don't trust the user signature, but as we trust the
559      * signature of the issuer, we have (indirect) trust.
560      */
561     @Test
562     @SuppressWarnings("unchecked")
563     public void testSAML1AuthnAssertionTrust() throws Exception {
564         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
565         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
566         callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
567         callbackHandler.setIssuer("www.example.com");
568         Crypto crypto = CryptoFactory.getInstance("crypto.properties");
569         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
570         cryptoType.setAlias("16c73ab6-b892-458f-abf5-2f875f74882e");
571         X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
572         callbackHandler.setCerts(certs);
573 
574         SAMLCallback samlCallback = new SAMLCallback();
575         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
576         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
577 
578         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
579 
580         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
581         WSSecHeader secHeader = new WSSecHeader(doc);
582         secHeader.insertSecurityHeader();
583 
584         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
585         wsSign.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
586         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
587         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
588         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
589 
590         Document signedDoc =
591             wsSign.build(crypto, samlAssertion, null, null, null);
592 
593         String outputString =
594             XMLUtils.prettyDocumentToString(signedDoc);
595         if (LOG.isDebugEnabled()) {
596             LOG.debug("Signed SAML 1.1 Authn Assertion (key holder):");
597             LOG.debug(outputString);
598         }
599 
600         WSHandlerResult results = verify(signedDoc, trustCrypto);
601 
602         // Test we processed a SAML assertion
603         WSSecurityEngineResult actionResult =
604             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
605         SamlAssertionWrapper receivedSamlAssertion =
606             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
607         assertNotNull(receivedSamlAssertion);
608         assertTrue(receivedSamlAssertion.isSigned());
609 
610         // Test we processed a signature (SOAP body)
611         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
612         assertNotNull(actionResult);
613         assertFalse(actionResult.isEmpty());
614         final List<WSDataRef> refs =
615             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
616         assertTrue(refs.size() == 1);
617 
618         WSDataRef wsDataRef = refs.get(0);
619         String xpath = wsDataRef.getXpath();
620         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
621     }
622 
623     @Test
624     @org.junit.jupiter.api.Disabled
625     public void testSAML2Advice() throws Exception {
626         // Create a signed "Advice" Element first
627         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
628         callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
629         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
630         callbackHandler.setIssuer("www.example.com");
631 
632         SAMLCallback samlCallback = new SAMLCallback();
633         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
634         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
635 
636         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
637 
638         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
639         Element adviceElement = samlAssertion.toDOM(doc);
640 
641         // Now create a SAML Assertion that uses the signed advice Element
642         callbackHandler = new SAML2CallbackHandler();
643         callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
644         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
645         callbackHandler.setIssuer("www.example.com");
646         callbackHandler.setAssertionAdviceElement(adviceElement);
647 
648         samlCallback = new SAMLCallback();
649         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
650         samlAssertion = new SamlAssertionWrapper(samlCallback);
651 
652         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
653 
654         byte[] ephemeralKey = callbackHandler.getEphemeralKey();
655 
656         WSSecHeader secHeader = new WSSecHeader(doc);
657         secHeader.insertSecurityHeader();
658 
659         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
660         wsSign.setUserInfo("wss40", "security");
661         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
662         wsSign.setSignatureAlgorithm(WSConstants.HMAC_SHA256);
663         wsSign.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
664         wsSign.setSecretKey(ephemeralKey);
665 
666         Document signedDoc =
667             wsSign.build(userCrypto, samlAssertion, null, null, null);
668 
669         String outputString =
670             XMLUtils.prettyDocumentToString(signedDoc);
671         if (LOG.isDebugEnabled()) {
672             LOG.debug("Signed SAML 2 Attr Assertion (key holder):");
673             LOG.debug(outputString);
674         }
675         System.out.println(outputString);
676 
677         /* https://issues.apache.org/jira/browse/WSS-265 */
678         WSHandlerResult results = verify(signedDoc, trustCrypto);
679 
680         // Test we processed a SAML assertion
681         WSSecurityEngineResult actionResult =
682             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
683         SamlAssertionWrapper receivedAssertion =
684             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
685         assertNotNull(receivedAssertion);
686         assertTrue(receivedAssertion.isSigned());
687 
688         // Test we processed a signature (SOAP body)
689         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
690         assertNotNull(actionResult);
691         assertFalse(actionResult.isEmpty());
692         @SuppressWarnings("unchecked")
693         final List<WSDataRef> refs =
694             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
695         assertTrue(refs.size() == 1);
696 
697         WSDataRef wsDataRef = refs.get(0);
698         String xpath = wsDataRef.getXpath();
699         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
700     }
701 
702     // Add both the X509Data and KeyValue for both the Subject + Signature KeyInfo
703     @Test
704     @SuppressWarnings("unchecked")
705     public void testX509DataAndKeyValue() throws Exception {
706         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
707         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
708         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
709         callbackHandler.setIssuer("www.example.com");
710 
711         // Create the KeyInfo
712         DocumentBuilderFactory docBuilderFactory =
713             DocumentBuilderFactory.newInstance();
714         docBuilderFactory.setNamespaceAware(true);
715         DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
716         Document keyInfoDoc = docBuilder.newDocument();
717 
718         Crypto crypto = CryptoFactory.getInstance("wss40.properties");
719         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
720         cryptoType.setAlias("wss40");
721         X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
722         java.security.PublicKey publicKey = certs[0].getPublicKey();
723 
724         KeyInfoFactory keyInfoFactory =
725             XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig").getKeyInfoFactory();
726 
727         // X.509
728         X509Data x509Data = keyInfoFactory.newX509Data(Collections.singletonList(certs[0]));
729 
730         // KeyValue
731         KeyValue keyValue = keyInfoFactory.newKeyValue(publicKey);
732         List<? extends XMLStructure> keyInfoContent = Arrays.asList(x509Data, keyValue);
733         KeyInfo keyInfo = keyInfoFactory.newKeyInfo(keyInfoContent, null);
734 
735         // Marshal the KeyInfo to DOM
736         Element parent = keyInfoDoc.createElement("temp");
737         DOMCryptoContext cryptoContext = new DOMCryptoContext() { };
738         cryptoContext.putNamespacePrefix(WSConstants.SIG_NS, WSConstants.SIG_PREFIX);
739         keyInfo.marshal(new DOMStructure(parent), cryptoContext);
740 
741         Element keyInfoElement = (Element)parent.getFirstChild();
742 
743         callbackHandler.setKeyInfoElement(keyInfoElement);
744 
745         SAMLCallback samlCallback = new SAMLCallback();
746         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
747         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
748 
749         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
750 
751         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
752         WSSecHeader secHeader = new WSSecHeader(doc);
753         secHeader.insertSecurityHeader();
754 
755         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
756         wsSign.setUserInfo("wss40", "security");
757         wsSign.setCustomKeyInfoElement(keyInfoElement);
758 
759         Document signedDoc =
760             wsSign.build(userCrypto, samlAssertion, null, null, null);
761 
762         String outputString =
763             XMLUtils.prettyDocumentToString(signedDoc);
764         if (LOG.isDebugEnabled()) {
765             LOG.debug("Signed SAML 2 Authn Assertion (key holder):");
766             LOG.debug(outputString);
767         }
768 
769         RequestData data = new RequestData();
770         data.setSigVerCrypto(userCrypto);
771 
772         List<BSPRule> ignoredRules = new ArrayList<>();
773         ignoredRules.add(BSPRule.R5417);
774         ignoredRules.add(BSPRule.R5402);
775         data.setIgnoredBSPRules(ignoredRules);
776 
777         Element securityHeader = WSSecurityUtil.getSecurityHeader(signedDoc, null);
778         WSHandlerResult results =
779             secEngine.processSecurityHeader(securityHeader, data);
780 
781         // Test we processed a SAML assertion
782         WSSecurityEngineResult actionResult =
783             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
784         SamlAssertionWrapper receivedSamlAssertion =
785             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
786         assertNotNull(receivedSamlAssertion);
787         assertTrue(receivedSamlAssertion.isSigned());
788 
789         // Test we processed a signature (SOAP body)
790         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
791         assertNotNull(actionResult);
792         assertFalse(actionResult.isEmpty());
793         final List<WSDataRef> refs =
794             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
795         assertTrue(refs.size() == 1);
796 
797         WSDataRef wsDataRef = refs.get(0);
798         String xpath = wsDataRef.getXpath();
799         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
800     }
801 
802     @Test
803     @SuppressWarnings("unchecked")
804     public void testSAML2SubjectWithComment() throws Exception {
805         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
806         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
807         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
808         callbackHandler.setIssuer("www.example.com");
809         String principal = "uid=joe,ou=people<!---->o=example.com";
810         callbackHandler.setSubjectName(principal);
811 
812         SAMLCallback samlCallback = new SAMLCallback();
813         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
814         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
815 
816         samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
817 
818         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
819         WSSecHeader secHeader = new WSSecHeader(doc);
820         secHeader.insertSecurityHeader();
821 
822         WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
823         wsSign.setUserInfo("wss40", "security");
824         wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
825         wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
826         wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
827 
828         Document signedDoc =
829             wsSign.build(userCrypto, samlAssertion, null, null, null);
830 
831         String outputString =
832             XMLUtils.prettyDocumentToString(signedDoc);
833         if (LOG.isDebugEnabled()) {
834             LOG.debug("Signed SAML 2 Authn Assertion (key holder):");
835             LOG.debug(outputString);
836         }
837         assertTrue(outputString.contains("http://www.w3.org/2001/04/xmlenc#sha256"));
838         assertTrue(outputString.contains("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"));
839 
840         WSHandlerResult results = verify(signedDoc, trustCrypto);
841 
842         // Test we processed a SAML assertion
843         WSSecurityEngineResult actionResult =
844             results.getActionResults().get(WSConstants.ST_SIGNED).get(0);
845         SamlAssertionWrapper receivedSamlAssertion =
846             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
847         assertNotNull(receivedSamlAssertion);
848         assertTrue(receivedSamlAssertion.isSigned());
849 
850         // Test we processed a signature (SOAP body)
851         actionResult = results.getActionResults().get(WSConstants.SIGN).get(0);
852         assertNotNull(actionResult);
853         assertFalse(actionResult.isEmpty());
854         final List<WSDataRef> refs =
855             (List<WSDataRef>) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
856         assertTrue(refs.size() == 1);
857 
858         WSDataRef wsDataRef = refs.get(0);
859         String xpath = wsDataRef.getXpath();
860         assertEquals("/SOAP-ENV:Envelope/SOAP-ENV:Body", xpath);
861 
862         Principal receivedPrincipal = (Principal)actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
863         assertEquals(principal, receivedPrincipal.getName());
864     }
865 
866     /**
867      * Verifies the soap envelope
868      *
869      * @param doc
870      * @throws Exception Thrown when there is a problem in verification
871      */
872     private WSHandlerResult verify(Document doc, Crypto sigCrypto) throws Exception {
873         WSHandlerResult results =
874             secEngine.processSecurityHeader(
875                 doc, null, callbackHandler, sigCrypto, userCrypto
876             );
877         String outputString =
878             XMLUtils.prettyDocumentToString(doc);
879         assertTrue(outputString.indexOf("counter_port_type") > 0 ? true : false);
880         return results;
881     }
882 
883 }