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;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.InputStream;
24  import java.nio.charset.StandardCharsets;
25  import java.security.Key;
26  import java.security.NoSuchAlgorithmException;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import javax.crypto.KeyGenerator;
31  import javax.crypto.SecretKey;
32  import javax.xml.namespace.QName;
33  import javax.xml.stream.XMLStreamReader;
34  import javax.xml.stream.XMLStreamWriter;
35  import javax.xml.transform.dom.DOMSource;
36  import javax.xml.transform.stream.StreamResult;
37  
38  import org.apache.wss4j.common.WSEncryptionPart;
39  import org.apache.wss4j.common.crypto.Crypto;
40  import org.apache.wss4j.common.crypto.CryptoFactory;
41  import org.apache.wss4j.common.ext.WSSecurityException;
42  import org.apache.wss4j.common.util.KeyUtils;
43  import org.apache.wss4j.dom.WSConstants;
44  import org.apache.wss4j.dom.handler.WSHandlerConstants;
45  import org.apache.wss4j.dom.message.WSSecEncrypt;
46  import org.apache.wss4j.dom.message.WSSecHeader;
47  import org.apache.wss4j.dom.message.WSSecSignature;
48  import org.apache.wss4j.stax.ext.WSSConstants;
49  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
50  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
51  import org.apache.wss4j.stax.setup.OutboundWSSec;
52  import org.apache.wss4j.stax.setup.WSSec;
53  import org.apache.wss4j.stax.test.utils.XmlReaderToWriter;
54  import org.apache.xml.security.exceptions.XMLSecurityException;
55  import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
56  import org.apache.xml.security.stax.ext.SecurePart;
57  import org.apache.xml.security.stax.impl.OutboundSecurityContextImpl;
58  import org.apache.xml.security.stax.impl.securityToken.GenericOutboundSecurityToken;
59  import org.apache.xml.security.stax.impl.util.IDGenerator;
60  import org.apache.xml.security.stax.securityEvent.SecurityEvent;
61  import org.apache.xml.security.stax.securityToken.OutboundSecurityToken;
62  import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
63  import org.junit.jupiter.api.Test;
64  import org.w3c.dom.Document;
65  import org.w3c.dom.Element;
66  import org.w3c.dom.NodeList;
67  
68  import static org.junit.jupiter.api.Assertions.assertEquals;
69  
70  public class SignatureEncryptionTest extends AbstractTestBase {
71  
72      @Test
73      public void testSignatureEncryptionOutbound() throws Exception {
74  
75          ByteArrayOutputStream baos;
76          {
77              WSSSecurityProperties securityProperties = new WSSSecurityProperties();
78              List<WSSConstants.Action> actions = new ArrayList<>();
79              actions.add(WSSConstants.SIGNATURE);
80              actions.add(WSSConstants.ENCRYPTION);
81              actions.add(WSSConstants.TIMESTAMP);
82              securityProperties.setActions(actions);
83              securityProperties.loadEncryptionKeystore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
84              securityProperties.setEncryptionUser("receiver");
85              securityProperties.loadSignatureKeyStore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
86              securityProperties.setSignatureUser("transmitter");
87              securityProperties.setCallbackHandler(new CallbackHandlerImpl());
88  
89              securityProperties.addSignaturePart(
90                  new SecurePart(new QName(WSSConstants.NS_WSU10, "Timestamp"), SecurePart.Modifier.Element)
91              );
92              securityProperties.addSignaturePart(
93                  new SecurePart(new QName(WSSConstants.NS_SOAP11, "Body"), SecurePart.Modifier.Element)
94              );
95  
96              InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
97              baos = doOutboundSecurity(securityProperties, sourceDocument);
98  
99              documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray()));
100         }
101 
102         //done encryption; now test decryption:
103         {
104             String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.ENCRYPTION + " " + WSHandlerConstants.TIMESTAMP;
105             doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action);
106         }
107     }
108 
109     @Test
110     public void testEncryptionSymmetricOutbound() throws Exception {
111 
112         ByteArrayOutputStream baos;
113         {
114             WSSSecurityProperties securityProperties = new WSSSecurityProperties();
115             List<WSSConstants.Action> actions = new ArrayList<>();
116             actions.add(WSSConstants.ENCRYPTION);
117             actions.add(WSSConstants.TIMESTAMP);
118             securityProperties.setActions(actions);
119             securityProperties.loadEncryptionKeystore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
120             securityProperties.setEncryptionUser("receiver");
121 
122             OutboundWSSec wsSecOut = WSSec.getOutboundWSSec(securityProperties);
123 
124             // Symmetric Key
125             String keyAlgorithm =
126                 JCEAlgorithmMapper.getJCEKeyAlgorithmFromURI(WSSConstants.NS_XENC_AES128);
127             KeyGenerator keyGen;
128             try {
129                 keyGen = KeyGenerator.getInstance(keyAlgorithm);
130             } catch (NoSuchAlgorithmException e) {
131                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
132             }
133             int keyLength = JCEAlgorithmMapper.getKeyLengthFromURI(WSSConstants.NS_XENC_AES128);
134             keyGen.init(keyLength);
135 
136             final Key symmetricKey = keyGen.generateKey();
137 
138             final String ekId = IDGenerator.generateID(null);
139 
140             final GenericOutboundSecurityToken encryptedKeySecurityToken =
141                 new GenericOutboundSecurityToken(ekId, WSSecurityTokenConstants.EncryptedKeyToken, symmetricKey);
142 
143             final SecurityTokenProvider<OutboundSecurityToken> encryptedKeySecurityTokenProvider =
144                     new SecurityTokenProvider<OutboundSecurityToken>() {
145 
146                 @Override
147                 public OutboundSecurityToken getSecurityToken() throws XMLSecurityException {
148                     return encryptedKeySecurityToken;
149                 }
150 
151                 @Override
152                 public String getId() {
153                     return ekId;
154                 }
155             };
156 
157             final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl();
158             outboundSecurityContext.putList(SecurityEvent.class, new ArrayList<SecurityEvent>());
159 
160             // Save Token on the security context
161             outboundSecurityContext.registerSecurityTokenProvider(encryptedKeySecurityTokenProvider.getId(), encryptedKeySecurityTokenProvider);
162             outboundSecurityContext.put(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION, encryptedKeySecurityTokenProvider.getId());
163 
164             InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
165 
166             baos = new ByteArrayOutputStream();
167             XMLStreamWriter xmlStreamWriter =
168                 wsSecOut.processOutMessage(baos, StandardCharsets.UTF_8.name(), outboundSecurityContext);
169             XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(sourceDocument);
170             XmlReaderToWriter.writeAll(xmlStreamReader, xmlStreamWriter);
171             xmlStreamWriter.close();
172 
173             documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray()));
174         }
175 
176         //done encryption; now test decryption:
177         {
178             String action = WSHandlerConstants.ENCRYPTION + " " + WSHandlerConstants.TIMESTAMP;
179             doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action);
180         }
181     }
182 
183     @Test
184     public void testSignatureEncryptionSymmetricOutbound() throws Exception {
185 
186         ByteArrayOutputStream baos;
187         {
188             WSSSecurityProperties securityProperties = new WSSSecurityProperties();
189             List<WSSConstants.Action> actions = new ArrayList<>();
190             actions.add(WSSConstants.SIGNATURE);
191             actions.add(WSSConstants.ENCRYPTION);
192             actions.add(WSSConstants.TIMESTAMP);
193             securityProperties.setActions(actions);
194             securityProperties.loadEncryptionKeystore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
195             securityProperties.setEncryptionUser("receiver");
196 
197             securityProperties.loadSignatureKeyStore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
198             securityProperties.setSignatureUser("transmitter");
199             securityProperties.setCallbackHandler(new CallbackHandlerImpl());
200 
201             securityProperties.setSignatureAlgorithm(WSSConstants.NS_XMLDSIG_HMACSHA1);
202             securityProperties.setSignatureKeyIdentifier(
203                 WSSecurityTokenConstants.KeyIdentifier_EncryptedKey
204             );
205 
206             securityProperties.addSignaturePart(
207                 new SecurePart(new QName(WSSConstants.NS_WSU10, "Timestamp"), SecurePart.Modifier.Element)
208             );
209             securityProperties.addSignaturePart(
210                 new SecurePart(new QName(WSSConstants.NS_SOAP11, "Body"), SecurePart.Modifier.Element)
211             );
212 
213             OutboundWSSec wsSecOut = WSSec.getOutboundWSSec(securityProperties);
214 
215             // Symmetric Key
216             String keyAlgorithm =
217                 JCEAlgorithmMapper.getJCEKeyAlgorithmFromURI(WSSConstants.NS_XENC_AES128);
218             KeyGenerator keyGen;
219             try {
220                 keyGen = KeyGenerator.getInstance(keyAlgorithm);
221             } catch (NoSuchAlgorithmException e) {
222                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
223             }
224             int keyLength = JCEAlgorithmMapper.getKeyLengthFromURI(WSSConstants.NS_XENC_AES128);
225             keyGen.init(keyLength);
226 
227             final Key symmetricKey = keyGen.generateKey();
228 
229             final String ekId = IDGenerator.generateID(null);
230 
231             final GenericOutboundSecurityToken encryptedKeySecurityToken =
232                 new GenericOutboundSecurityToken(ekId, WSSecurityTokenConstants.EncryptedKeyToken, symmetricKey);
233 
234             final SecurityTokenProvider<OutboundSecurityToken> encryptedKeySecurityTokenProvider =
235                     new SecurityTokenProvider<OutboundSecurityToken>() {
236 
237                 @Override
238                 public OutboundSecurityToken getSecurityToken() throws XMLSecurityException {
239                     return encryptedKeySecurityToken;
240                 }
241 
242                 @Override
243                 public String getId() {
244                     return ekId;
245                 }
246             };
247 
248             final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl();
249             outboundSecurityContext.putList(SecurityEvent.class, new ArrayList<SecurityEvent>());
250 
251             // Save Token on the security context
252             outboundSecurityContext.registerSecurityTokenProvider(encryptedKeySecurityTokenProvider.getId(), encryptedKeySecurityTokenProvider);
253             outboundSecurityContext.put(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION, encryptedKeySecurityTokenProvider.getId());
254             outboundSecurityContext.put(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_SIGNATURE, encryptedKeySecurityTokenProvider.getId());
255 
256             InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
257 
258             baos = new ByteArrayOutputStream();
259             XMLStreamWriter xmlStreamWriter =
260                 wsSecOut.processOutMessage(baos, StandardCharsets.UTF_8.name(), outboundSecurityContext);
261             XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(sourceDocument);
262             XmlReaderToWriter.writeAll(xmlStreamReader, xmlStreamWriter);
263             xmlStreamWriter.close();
264 
265             Document document = documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray()));
266 
267             NodeList securityHeaderElement = document.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
268             assertEquals(1, securityHeaderElement.getLength());
269             NodeList childs = securityHeaderElement.item(0).getChildNodes();
270 
271             assertEquals(childs.getLength(), 4);
272             assertEquals(childs.item(0).getLocalName(), "Timestamp");
273             assertEquals(childs.item(1).getLocalName(), "EncryptedKey");
274             assertEquals(childs.item(2).getLocalName(), "ReferenceList");
275             assertEquals(childs.item(3).getLocalName(), "Signature");
276         }
277 
278         //done encryption; now test decryption:
279         {
280             String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.ENCRYPTION + " " + WSHandlerConstants.TIMESTAMP;
281             doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action);
282         }
283     }
284 
285     @Test
286     public void testEncryptionSignatureSymmetricOutbound() throws Exception {
287 
288         ByteArrayOutputStream baos;
289         {
290             WSSSecurityProperties securityProperties = new WSSSecurityProperties();
291             List<WSSConstants.Action> actions = new ArrayList<>();
292             actions.add(WSSConstants.ENCRYPTION);
293             actions.add(WSSConstants.SIGNATURE);
294             actions.add(WSSConstants.TIMESTAMP);
295             securityProperties.setActions(actions);
296             securityProperties.loadEncryptionKeystore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
297             securityProperties.setEncryptionUser("receiver");
298 
299             securityProperties.loadSignatureKeyStore(this.getClass().getClassLoader().getResource("transmitter.jks"), "default".toCharArray());
300             securityProperties.setSignatureUser("transmitter");
301             securityProperties.setCallbackHandler(new CallbackHandlerImpl());
302 
303             securityProperties.setSignatureAlgorithm(WSSConstants.NS_XMLDSIG_HMACSHA1);
304             securityProperties.setSignatureKeyIdentifier(
305                     WSSecurityTokenConstants.KeyIdentifier_EncryptedKey
306             );
307 
308             securityProperties.addSignaturePart(
309                     new SecurePart(new QName(WSSConstants.NS_WSU10, "Timestamp"), SecurePart.Modifier.Element)
310             );
311             securityProperties.addSignaturePart(
312                     new SecurePart(new QName(WSSConstants.NS_SOAP11, "Body"), SecurePart.Modifier.Element)
313             );
314 
315             OutboundWSSec wsSecOut = WSSec.getOutboundWSSec(securityProperties);
316 
317             // Symmetric Key
318             String keyAlgorithm =
319                     JCEAlgorithmMapper.getJCEKeyAlgorithmFromURI(WSSConstants.NS_XENC_AES128);
320             KeyGenerator keyGen;
321             try {
322                 keyGen = KeyGenerator.getInstance(keyAlgorithm);
323             } catch (NoSuchAlgorithmException e) {
324                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
325             }
326             int keyLength = JCEAlgorithmMapper.getKeyLengthFromURI(WSSConstants.NS_XENC_AES128);
327             keyGen.init(keyLength);
328 
329             final Key symmetricKey = keyGen.generateKey();
330 
331             final String ekId = IDGenerator.generateID(null);
332 
333             final GenericOutboundSecurityToken encryptedKeySecurityToken =
334                     new GenericOutboundSecurityToken(ekId, WSSecurityTokenConstants.EncryptedKeyToken, symmetricKey);
335 
336             final SecurityTokenProvider<OutboundSecurityToken> encryptedKeySecurityTokenProvider =
337                     new SecurityTokenProvider<OutboundSecurityToken>() {
338 
339                         @Override
340                         public OutboundSecurityToken getSecurityToken() throws XMLSecurityException {
341                             return encryptedKeySecurityToken;
342                         }
343 
344                         @Override
345                         public String getId() {
346                             return ekId;
347                         }
348                     };
349 
350             final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl();
351             outboundSecurityContext.putList(SecurityEvent.class, new ArrayList<SecurityEvent>());
352 
353             // Save Token on the security context
354             outboundSecurityContext.registerSecurityTokenProvider(encryptedKeySecurityTokenProvider.getId(), encryptedKeySecurityTokenProvider);
355             outboundSecurityContext.put(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION, encryptedKeySecurityTokenProvider.getId());
356             outboundSecurityContext.put(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_SIGNATURE, encryptedKeySecurityTokenProvider.getId());
357 
358             InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
359 
360             baos = new ByteArrayOutputStream();
361             XMLStreamWriter xmlStreamWriter =
362                     wsSecOut.processOutMessage(baos, StandardCharsets.UTF_8.name(), outboundSecurityContext);
363             XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(sourceDocument);
364             XmlReaderToWriter.writeAll(xmlStreamReader, xmlStreamWriter);
365             xmlStreamWriter.close();
366 
367             Document document = documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray()));
368 
369             NodeList securityHeaderElement = document.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
370             assertEquals(1, securityHeaderElement.getLength());
371             NodeList childs = securityHeaderElement.item(0).getChildNodes();
372 
373             assertEquals(childs.getLength(), 4);
374             assertEquals(childs.item(0).getLocalName(), "Timestamp");
375             assertEquals(childs.item(1).getLocalName(), "EncryptedKey");
376             assertEquals(childs.item(2).getLocalName(), "Signature");
377             assertEquals(childs.item(3).getLocalName(), "ReferenceList");
378         }
379 
380         //done encryption; now test decryption:
381         {
382             String action = WSHandlerConstants.ENCRYPTION + " " + WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.TIMESTAMP;
383             doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action);
384         }
385     }
386 
387     @Test
388     public void testEncryptedDataTokenSecurityHeaderWithoutReferenceInbound() throws Exception {
389         ByteArrayOutputStream baos = new ByteArrayOutputStream();
390         {
391             InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
392 
393             Document doc = documentBuilderFactory.newDocumentBuilder().parse(sourceDocument);
394 
395             WSSecHeader secHeader = new WSSecHeader(doc);
396             secHeader.insertSecurityHeader();
397 
398             WSSecSignature sign = new WSSecSignature(secHeader);
399             sign.setUserInfo("transmitter", "default");
400             sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
401 
402             Crypto crypto = CryptoFactory.getInstance("transmitter-crypto.properties");
403 
404             sign.build( crypto);
405 
406             WSSecEncrypt builder = new WSSecEncrypt(secHeader);
407             builder.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
408             builder.setUserInfo("receiver");
409 
410             KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
411             SecretKey symmetricKey = keyGen.generateKey();
412             builder.prepare(crypto, symmetricKey);
413 
414             WSEncryptionPart bst = new WSEncryptionPart("BinarySecurityToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Element");
415             WSEncryptionPart def = new WSEncryptionPart("definitions", "http://schemas.xmlsoap.org/wsdl/", "Element");
416             List<WSEncryptionPart> encryptionParts = new ArrayList<>();
417             encryptionParts.add(bst);
418             encryptionParts.add(def);
419             Element ref = builder.encryptForRef(null, encryptionParts, symmetricKey);
420             ref.removeChild(ref.getElementsByTagNameNS("http://www.w3.org/2001/04/xmlenc#", "DataReference").item(0));
421             builder.addExternalRefElement(ref);
422             builder.prependToHeader();
423 
424             javax.xml.transform.Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
425             transformer.transform(new DOMSource(doc), new StreamResult(baos));
426         }
427 
428         //done encryption; now test decryption:
429         {
430             WSSSecurityProperties securityProperties = new WSSSecurityProperties();
431             securityProperties.loadDecryptionKeystore(this.getClass().getClassLoader().getResource("receiver.jks"), "default".toCharArray());
432             securityProperties.setCallbackHandler(new CallbackHandlerImpl());
433             Document document = doInboundSecurity(securityProperties, xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())));
434 
435             //no encrypted content
436             NodeList nodeList = document.getElementsByTagNameNS(WSSConstants.TAG_xenc_EncryptedData.getNamespaceURI(), WSSConstants.TAG_xenc_EncryptedData.getLocalPart());
437             assertEquals(nodeList.getLength(), 0);
438         }
439     }
440 }