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  package org.apache.wss4j.stax.test.saml;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.security.cert.X509Certificate;
24  
25  import javax.xml.stream.XMLStreamReader;
26  import javax.xml.transform.dom.DOMSource;
27  import javax.xml.transform.stream.StreamResult;
28  
29  import org.apache.wss4j.common.WSEncryptionPart;
30  import org.apache.wss4j.common.crypto.Crypto;
31  import org.apache.wss4j.common.crypto.CryptoFactory;
32  import org.apache.wss4j.common.crypto.CryptoType;
33  import org.apache.wss4j.common.ext.WSSecurityException;
34  import org.apache.wss4j.common.saml.SAMLCallback;
35  import org.apache.wss4j.common.saml.SAMLUtil;
36  import org.apache.wss4j.common.saml.SamlAssertionWrapper;
37  import org.apache.wss4j.common.saml.builder.SAML1Constants;
38  import org.apache.wss4j.common.token.SecurityTokenReference;
39  import org.apache.wss4j.common.util.SOAPUtil;
40  import org.apache.wss4j.dom.WSConstants;
41  import org.apache.wss4j.dom.engine.WSSConfig;
42  import org.apache.wss4j.dom.message.WSSecDKSign;
43  import org.apache.wss4j.dom.message.WSSecHeader;
44  import org.apache.wss4j.dom.util.WSSecurityUtil;
45  import org.apache.wss4j.stax.ext.WSSConstants;
46  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
47  import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
48  import org.apache.wss4j.stax.setup.InboundWSSec;
49  import org.apache.wss4j.stax.setup.WSSec;
50  import org.apache.wss4j.stax.test.AbstractTestBase;
51  import org.apache.wss4j.stax.test.CallbackHandlerImpl;
52  import org.apache.wss4j.stax.test.utils.StAX2DOM;
53  import org.junit.jupiter.api.Test;
54  import org.w3c.dom.Document;
55  import org.w3c.dom.Element;
56  import org.w3c.dom.NodeList;
57  
58  import static org.junit.jupiter.api.Assertions.assertEquals;
59  
60  public class SamlTokenDerivedTest extends AbstractTestBase {
61  
62      /**
63       * Test that creates, sends and processes a signed SAML 1.1 authentication assertion
64       * using a derived key.
65       */
66      @Test
67      public void testSAML1AuthnAssertionDerivedInbound() throws Exception {
68  
69          ByteArrayOutputStream baos = new ByteArrayOutputStream();
70          {
71              //
72              // Create a SAML Assertion + STR, and add both to the security header
73              //
74              SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
75              callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
76              callbackHandler.setConfirmationMethod(SAML1Constants.CONF_SENDER_VOUCHES);
77              callbackHandler.setIssuer("www.example.com");
78  
79              SAMLCallback samlCallback = new SAMLCallback();
80              SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
81              SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
82  
83              Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
84              WSSecHeader secHeader = new WSSecHeader(doc);
85              secHeader.insertSecurityHeader();
86  
87              SecurityTokenReference secRefSaml =
88                      createSamlSTR(doc, samlAssertion, WSSConfig.getNewInstance());
89              Element samlTokenElement = samlAssertion.toDOM(doc);
90              Element secRefElement = secRefSaml.getElement();
91              secHeader.getSecurityHeaderElement().appendChild(samlTokenElement);
92              secHeader.getSecurityHeaderElement().appendChild(secRefElement);
93  
94              //
95              // Create a Derived Key object for signature
96              //
97              WSSecDKSign sigBuilder = createDKSign(doc, secRefSaml, secHeader);
98              Crypto crypto = CryptoFactory.getInstance("transmitter-crypto.properties");
99              java.security.Key key =
100                 crypto.getPrivateKey("transmitter", "default");
101             Document securedDocument = sigBuilder.build(key.getEncoded());
102 
103             //todo remove the following lines when the header ordering no longer does matter...
104             /*Node firstChild = secHeader.getSecurityHeader().getFirstChild();
105             secHeader.getSecurityHeader().insertBefore(secRefElement, firstChild);
106             secHeader.getSecurityHeader().insertBefore(samlTokenElement, secRefElement);*/
107 
108             javax.xml.transform.Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
109             transformer.transform(new DOMSource(securedDocument), new StreamResult(baos));
110         }
111 
112         {
113             WSSSecurityProperties securityProperties = new WSSSecurityProperties();
114             securityProperties.loadSignatureVerificationKeystore(this.getClass().getClassLoader().getResource("receiver.jks"), "default".toCharArray());
115             securityProperties.loadDecryptionKeystore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
116             securityProperties.setCallbackHandler(new CallbackHandlerImpl());
117             InboundWSSec wsSecIn = WSSec.getInboundWSSec(securityProperties);
118 
119             WSSecurityEventConstants.Event[] securityEvents = new WSSecurityEventConstants.Event[]{
120                     WSSecurityEventConstants.AlgorithmSuite,
121                     WSSecurityEventConstants.AlgorithmSuite,
122                     WSSecurityEventConstants.AlgorithmSuite,
123                     WSSecurityEventConstants.AlgorithmSuite,
124                     WSSecurityEventConstants.AlgorithmSuite,
125                     WSSecurityEventConstants.AlgorithmSuite,
126                     WSSecurityEventConstants.AlgorithmSuite,
127                     WSSecurityEventConstants.AlgorithmSuite,
128                     WSSecurityEventConstants.X509Token,
129                     WSSecurityEventConstants.SAML_TOKEN,
130                     WSSecurityEventConstants.SignatureValue,
131                     WSSecurityEventConstants.SignedElement,
132                     WSSecurityEventConstants.SIGNED_PART,
133                     WSSecurityEventConstants.OPERATION,
134             };
135             TestSecurityEventListener testSecurityEventListener = new TestSecurityEventListener(securityEvents);
136 
137             XMLStreamReader xmlStreamReader = wsSecIn.processInMessage(xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())), null, testSecurityEventListener);
138 
139             Document document = StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader);
140 
141             //header element must still be there
142             NodeList nodeList = document.getElementsByTagNameNS(WSSConstants.TAG_dsig_Signature.getNamespaceURI(), WSSConstants.TAG_dsig_Signature.getLocalPart());
143             assertEquals(nodeList.getLength(), 1);
144             assertEquals(nodeList.item(0).getParentNode().getLocalName(), WSSConstants.TAG_WSSE_SECURITY.getLocalPart());
145 
146             testSecurityEventListener.compare();
147         }
148     }
149 
150     /**
151      * Create a SecurityTokenReference to a SAML Assertion
152      */
153     private SecurityTokenReference createSamlSTR(
154             Document doc,
155             SamlAssertionWrapper samlAssertion,
156             WSSConfig wssConfig
157     ) {
158         SecurityTokenReference secRefSaml = new SecurityTokenReference(doc);
159         String secRefID = wssConfig.getIdAllocator().createSecureId("STRSAMLId-", secRefSaml);
160         secRefSaml.setID(secRefID);
161 
162         org.apache.wss4j.common.token.Reference ref =
163                 new org.apache.wss4j.common.token.Reference(doc);
164         ref.setURI("#" + samlAssertion.getId());
165         ref.setValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
166         secRefSaml.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
167         secRefSaml.setReference(ref);
168 
169         return secRefSaml;
170     }
171 
172     /**
173      * Create a WSSecDKSign object, that signs the SOAP Body as well as the SAML Assertion
174      * via a STR Transform.
175      */
176     private WSSecDKSign createDKSign(
177             Document doc,
178             SecurityTokenReference secRefSaml,
179             WSSecHeader secHeader
180     ) throws WSSecurityException {
181         SecurityTokenReference secToken = new SecurityTokenReference(doc);
182         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
183         cryptoType.setAlias("transmitter");
184         Crypto crypto = CryptoFactory.getInstance("transmitter-crypto.properties");
185         X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
186         secToken.setKeyIdentifierThumb(certs[0]);
187 
188         WSSecDKSign sigBuilder = new WSSecDKSign(secHeader);
189         sigBuilder.setStrElem(secToken.getElement());
190         sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
191 
192         String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
193         WSEncryptionPart encP =
194                 new WSEncryptionPart(
195                         WSConstants.ELEM_BODY,
196                         soapNamespace,
197                         "Content"
198                 );
199         sigBuilder.getParts().add(encP);
200 
201         encP = new WSEncryptionPart("STRTransform", "", "Element");
202         encP.setId(secRefSaml.getID());
203         encP.setElement(secRefSaml.getElement());
204         sigBuilder.getParts().add(encP);
205 
206         return sigBuilder;
207     }
208 }