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.message;
21  
22  import org.apache.wss4j.common.util.SOAPUtil;
23  import org.apache.wss4j.dom.WSConstants;
24  import org.apache.wss4j.dom.WSDataRef;
25  import org.apache.wss4j.dom.common.CustomHandler;
26  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
27  
28  import org.apache.wss4j.dom.engine.WSSConfig;
29  import org.apache.wss4j.dom.engine.WSSecurityEngine;
30  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
31  import org.apache.wss4j.dom.handler.HandlerAction;
32  import org.apache.wss4j.dom.handler.RequestData;
33  import org.apache.wss4j.dom.handler.WSHandlerConstants;
34  import org.apache.wss4j.dom.handler.WSHandlerResult;
35  
36  import org.junit.jupiter.api.Test;
37  import org.apache.wss4j.common.WSEncryptionPart;
38  import org.apache.wss4j.common.crypto.Crypto;
39  import org.apache.wss4j.common.crypto.CryptoFactory;
40  import org.apache.wss4j.common.util.KeyUtils;
41  import org.apache.wss4j.common.util.XMLUtils;
42  import org.w3c.dom.Document;
43  
44  import javax.crypto.KeyGenerator;
45  import javax.crypto.SecretKey;
46  import javax.security.auth.callback.CallbackHandler;
47  import javax.xml.crypto.dsig.SignatureMethod;
48  
49  import java.util.List;
50  import java.util.ArrayList;
51  
52  import static org.junit.jupiter.api.Assertions.assertEquals;
53  import static org.junit.jupiter.api.Assertions.assertNotNull;
54  import static org.junit.jupiter.api.Assertions.assertNull;
55  import static org.junit.jupiter.api.Assertions.assertTrue;
56  
57  /**
58   * A set of tests for combined signature/encryption, verification/decryption.
59   */
60  public class SignatureEncryptionTest {
61      private static final org.slf4j.Logger LOG =
62          org.slf4j.LoggerFactory.getLogger(SignatureEncryptionTest.class);
63      private static final String SOAPMSG =
64          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
65          + "<SOAP-ENV:Envelope "
66          +   "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
67          +   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
68          +   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
69          +   "<SOAP-ENV:Body>"
70          +       "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
71          +           "<value xmlns=\"http://blah.com\">15</value>"
72          +       "</add>"
73          +   "</SOAP-ENV:Body>"
74          + "</SOAP-ENV:Envelope>";
75      public static final String SAMPLE_SOAP12_FAULT_MSG =
76          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
77          + "<s:Envelope "
78          +   "xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" "
79          +   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
80          +   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
81          +   "<s:Body>"
82          +   "<Fault xmlns=\"http://www.w3.org/2003/05/soap-envelope\"><Code><Value>Receiver</Value></Code><Reason>"
83          +   "<Text xml:lang=\"en\">Error Message.</Text></Reason></Fault>"
84          +   "</s:Body>"
85          + "</s:Envelope>";
86  
87      private WSSecurityEngine secEngine = new WSSecurityEngine();
88      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
89  
90      private Crypto crypto;
91  
92      public SignatureEncryptionTest() throws Exception {
93          crypto = CryptoFactory.getInstance("wss40.properties");
94          WSSConfig.init();
95      }
96  
97      /**
98       * Test that encrypts and then signs a WS-Security envelope, then performs
99       * verification and decryption <p/>
100      *
101      * @throws Exception
102      *             Thrown when there is any problem in signing, encryption,
103      *             decryption, or verification
104      */
105     @Test
106     public void testEncryptionSigning() throws Exception {
107         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
108         WSSecHeader secHeader = new WSSecHeader(doc);
109         secHeader.insertSecurityHeader();
110 
111         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
112         WSSecSignature sign = new WSSecSignature(secHeader);
113         encrypt.setUserInfo("wss40");
114         sign.setUserInfo("wss40", "security");
115         LOG.info("Before Encryption....");
116 
117         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
118         SecretKey symmetricKey = keyGen.generateKey();
119         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
120 
121         if (LOG.isDebugEnabled()) {
122             LOG.debug("After Encryption....");
123             String outputString =
124                 XMLUtils.prettyDocumentToString(encryptedDoc);
125             LOG.debug(outputString);
126         }
127 
128         Document encryptedSignedDoc = sign.build(crypto);
129 
130         if (LOG.isDebugEnabled()) {
131             LOG.debug("After Signing....");
132             String outputString =
133                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
134             LOG.debug(outputString);
135         }
136         verify(encryptedSignedDoc);
137     }
138 
139 
140     /**
141      * Test that encrypts and then signs a WS-Security envelope (including the
142      * encrypted element), then performs verification and decryption <p/>
143      *
144      * @throws Exception
145      *             Thrown when there is any problem in signing, encryption,
146      *             decryption, or verification
147      */
148     @SuppressWarnings("unchecked")
149     @Test
150     public void testEncryptionElementSigning() throws Exception {
151         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
152         WSSecHeader secHeader = new WSSecHeader(doc);
153         secHeader.insertSecurityHeader();
154 
155         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
156         WSSecSignature sign = new WSSecSignature(secHeader);
157         encrypt.setUserInfo("wss40");
158         sign.setUserInfo("wss40", "security");
159         LOG.info("Before Encryption....");
160 
161         WSEncryptionPart part =
162             new WSEncryptionPart(
163                     "add",
164                     "http://ws.apache.org/counter/counter_port_type",
165                     "Element");
166         encrypt.getParts().add(part);
167 
168         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
169         SecretKey symmetricKey = keyGen.generateKey();
170         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
171 
172         if (LOG.isDebugEnabled()) {
173             LOG.debug("After Encryption....");
174             String outputString =
175                 XMLUtils.prettyDocumentToString(encryptedDoc);
176             LOG.debug(outputString);
177         }
178 
179         WSEncryptionPart signPart =
180             new WSEncryptionPart(
181                     WSConstants.ENC_DATA_LN,
182                     WSConstants.ENC_NS,
183                     "Element");
184         sign.getParts().add(signPart);
185 
186         Document encryptedSignedDoc = sign.build(crypto);
187 
188         if (LOG.isDebugEnabled()) {
189             LOG.debug("After Signing....");
190             String outputString =
191                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
192             LOG.debug(outputString);
193         }
194 
195         WSHandlerResult results = verify(encryptedSignedDoc);
196 
197         List<WSSecurityEngineResult> sigSecEngResults =
198             results.getActionResults().get(WSConstants.SIGN);
199 
200         List<WSSecurityEngineResult> encSecEngResults =
201             results.getActionResults().get(WSConstants.ENCR);
202 
203         assertEquals(1, sigSecEngResults.size());
204         assertEquals(1, encSecEngResults.size());
205 
206         List<WSDataRef> sigDataRefs =
207             (List<WSDataRef>)(sigSecEngResults.get(0)).get(
208                 WSSecurityEngineResult.TAG_DATA_REF_URIS
209             );
210 
211         List<WSDataRef> encDataRefs =
212             (List<WSDataRef>)(encSecEngResults.get(0)).get(
213                 WSSecurityEngineResult.TAG_DATA_REF_URIS
214             );
215 
216         assertNotNull(sigDataRefs);
217         assertNotNull(encDataRefs);
218         assertEquals(1, sigDataRefs.size());
219         assertEquals(1, encDataRefs.size());
220 
221         assertNull(sigDataRefs.get(0)
222                 .getProtectedElement().getAttributeNodeNS(WSConstants.WSU_NS, "Id"));
223 
224         assertTrue(sigDataRefs.get(0).getWsuId().contains(
225                 encDataRefs.get(0).getWsuId()));
226     }
227 
228 
229     /**
230      * Test that signs and then encrypts a WS-Security envelope, then performs
231      * decryption and verification <p/>
232      *
233      * @throws Exception
234      *             Thrown when there is any problem in signing, encryption,
235      *             decryption, or verification
236      */
237     @Test
238     public void testSigningEncryption() throws Exception {
239         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
240         WSSecHeader secHeader = new WSSecHeader(doc);
241         secHeader.insertSecurityHeader();
242 
243         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
244         WSSecSignature sign = new WSSecSignature(secHeader);
245         encrypt.setUserInfo("wss40");
246         sign.setUserInfo("wss40", "security");
247         LOG.info("Before Encryption....");
248 
249         sign.build(crypto);
250 
251         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
252         SecretKey symmetricKey = keyGen.generateKey();
253         Document encryptedSignedDoc = encrypt.build(crypto, symmetricKey);
254         LOG.info("After Encryption....");
255         verify(encryptedSignedDoc);
256     }
257 
258 
259     /**
260      * Test that signs a SOAP Body, and then encrypts some data inside the SOAP Body.
261      * As the encryption adds a wsu:Id to the encrypted element, this test checks that
262      * verification still works ok.
263      */
264     @Test
265     public void testWSS198() throws Exception {
266         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
267         WSSecHeader secHeader = new WSSecHeader(doc);
268         secHeader.insertSecurityHeader();
269 
270         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
271         WSSecSignature sign = new WSSecSignature(secHeader);
272         encrypt.setUserInfo("wss40");
273         sign.setUserInfo("wss40", "security");
274         LOG.info("Before Encryption....");
275 
276         WSEncryptionPart encP =
277             new WSEncryptionPart(
278                 "add",
279                 "http://ws.apache.org/counter/counter_port_type",
280                 "");
281         encrypt.getParts().add(encP);
282 
283         sign.build(crypto);
284 
285         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
286         SecretKey symmetricKey = keyGen.generateKey();
287         Document encryptedSignedDoc = encrypt.build(crypto, symmetricKey);
288         LOG.info("WSS198");
289         if (LOG.isDebugEnabled()) {
290             String outputString =
291                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
292             LOG.debug(outputString);
293         }
294         verify(encryptedSignedDoc);
295     }
296 
297     /**
298      * Test that first signs, then encrypts a WS-Security envelope.
299      * The test uses the IssuerSerial key identifier to get the keys for
300      * signature and encryption. Encryption uses 3DES.
301      * <p/>
302      *
303      * @throws Exception Thrown when there is any problem in signing, encryption,
304      *                   decryption, or verification
305      */
306     @Test
307     public void testSigningEncryptionIS3DES() throws Exception {
308         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
309         WSSecHeader secHeader = new WSSecHeader(doc);
310         secHeader.insertSecurityHeader();
311 
312         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
313         encrypt.setUserInfo("wss40");
314         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
315         encrypt.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
316 
317         WSSecSignature sign = new WSSecSignature(secHeader);
318         sign.setUserInfo("wss40", "security");
319         sign.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
320 
321         LOG.info("Before Sign/Encryption....");
322 
323         sign.build(crypto);
324 
325         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.TRIPLE_DES);
326         SecretKey symmetricKey = keyGen.generateKey();
327         Document encryptedSignedDoc = encrypt.build(crypto, symmetricKey);
328         if (LOG.isDebugEnabled()) {
329             LOG.debug("Signed and encrypted message with IssuerSerial key identifier (both), 3DES:");
330             String outputString =
331                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
332             LOG.debug(outputString);
333         }
334 
335         LOG.info("After Sign/Encryption....");
336         verify(encryptedSignedDoc);
337     }
338 
339     /**
340      * Test signature created using an encrypted key
341      * SOAP Body is signed and encrypted. In the encryption, The ReferenceList element is
342      * put into the Encrypted Key, as a child of the EncryptedKey. Signature is created
343      * using the encrypted key.
344      */
345     @Test
346     public void testEncryptedKeySignature() throws Exception {
347         Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
348         LOG.info("Before Sign/Encryption....");
349 
350         WSSecHeader secHeader = new WSSecHeader(doc);
351         secHeader.insertSecurityHeader();
352 
353         WSSecEncryptedKey encrKey = new WSSecEncryptedKey(secHeader);
354         encrKey.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
355         encrKey.setUserInfo("wss40", "security");
356 
357         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_192);
358         SecretKey symmetricKey = keyGen.generateKey();
359         encrKey.prepare(crypto, symmetricKey);
360 
361         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
362         encrypt.setEncKeyId(encrKey.getId());
363         encrypt.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
364         encrypt.setEncryptSymmKey(false);
365         encrypt.setEncryptedKeyElement(encrKey.getEncryptedKeyElement());
366 
367         WSSecSignature sign = new WSSecSignature(secHeader);
368         sign.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
369         sign.setCustomTokenId(encrKey.getId());
370         sign.setSecretKey(symmetricKey.getEncoded());
371         sign.setCustomTokenValueType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
372         sign.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
373 
374         sign.build(crypto);
375         Document encryptedSignedDoc = encrypt.build(crypto, symmetricKey);
376 
377         if (LOG.isDebugEnabled()) {
378             LOG.debug("Signed and encrypted message with IssuerSerial key identifier (both), 3DES:");
379             String outputString =
380                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
381             LOG.debug(outputString);
382         }
383 
384         LOG.info("After Sign/Encryption....");
385         verify(encryptedSignedDoc);
386     }
387 
388     @Test
389     public void testEncryptionSigningHandler() throws Exception {
390         final WSSConfig cfg = WSSConfig.getNewInstance();
391         final RequestData reqData = new RequestData();
392         reqData.setWssConfig(cfg);
393         java.util.Map<String, Object> messageContext = new java.util.TreeMap<>();
394         messageContext.put(WSHandlerConstants.PW_CALLBACK_REF, new KeystoreCallbackHandler());
395         messageContext.put(WSHandlerConstants.ENC_PROP_REF_ID, "" + crypto.hashCode());
396         messageContext.put(WSHandlerConstants.SIG_PROP_REF_ID, "" + crypto.hashCode());
397         messageContext.put("" + crypto.hashCode(), crypto);
398         reqData.setMsgContext(messageContext);
399         reqData.setUsername("wss40");
400 
401         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
402         CustomHandler handler = new CustomHandler();
403         List<HandlerAction> handlerActions = new ArrayList<>();
404         HandlerAction action = new HandlerAction(WSConstants.ENCR);
405         handlerActions.add(action);
406         action = new HandlerAction(WSConstants.SIGN);
407         handlerActions.add(action);
408 
409         handler.send(
410             doc,
411             reqData,
412             handlerActions,
413             true
414         );
415 
416         String outputString =
417             XMLUtils.prettyDocumentToString(doc);
418         if (LOG.isDebugEnabled()) {
419             LOG.debug(outputString);
420         }
421 
422         List<Integer> receivingActions = new ArrayList<>();
423         receivingActions.add(WSConstants.ENCR);
424         receivingActions.add(WSConstants.SIGN);
425         messageContext.put(WSHandlerConstants.DEC_PROP_REF_ID, "" + crypto.hashCode());
426         messageContext.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, "" + crypto.hashCode());
427         handler.receive(receivingActions, reqData);
428 
429         WSSecurityEngine newEngine = new WSSecurityEngine();
430         newEngine.processSecurityHeader(doc, reqData);
431     }
432 
433     @Test
434     public void testSigningEncryptionHandler() throws Exception {
435         final WSSConfig cfg = WSSConfig.getNewInstance();
436         final RequestData reqData = new RequestData();
437         reqData.setWssConfig(cfg);
438         java.util.Map<String, Object> messageContext = new java.util.TreeMap<>();
439         messageContext.put(WSHandlerConstants.PW_CALLBACK_REF, new KeystoreCallbackHandler());
440         messageContext.put(WSHandlerConstants.ENC_PROP_REF_ID, "" + crypto.hashCode());
441         messageContext.put(WSHandlerConstants.SIG_PROP_REF_ID, "" + crypto.hashCode());
442         messageContext.put("" + crypto.hashCode(), crypto);
443         reqData.setMsgContext(messageContext);
444         reqData.setUsername("wss40");
445 
446         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
447         CustomHandler handler = new CustomHandler();
448         List<HandlerAction> handlerActions = new ArrayList<>();
449         HandlerAction action = new HandlerAction(WSConstants.SIGN);
450         handlerActions.add(action);
451         action = new HandlerAction(WSConstants.ENCR);
452         handlerActions.add(action);
453 
454         handler.send(
455             doc,
456             reqData,
457             handlerActions,
458             true
459         );
460 
461         String outputString =
462             XMLUtils.prettyDocumentToString(doc);
463         if (LOG.isDebugEnabled()) {
464             LOG.debug(outputString);
465         }
466 
467         List<Integer> receivingActions = new ArrayList<>();
468         receivingActions.add(WSConstants.SIGN);
469         receivingActions.add(WSConstants.ENCR);
470         messageContext.put(WSHandlerConstants.DEC_PROP_REF_ID, "" + crypto.hashCode());
471         messageContext.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, "" + crypto.hashCode());
472         handler.receive(receivingActions, reqData);
473 
474         WSSecurityEngine newEngine = new WSSecurityEngine();
475         newEngine.processSecurityHeader(doc, reqData);
476     }
477 
478     @Test
479     public void testSigningEncryptionSOAP12Fault() throws Exception {
480         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
481         WSSecHeader secHeader = new WSSecHeader(doc);
482         secHeader.insertSecurityHeader();
483 
484         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
485         WSSecSignature sign = new WSSecSignature(secHeader);
486         encrypt.setUserInfo("wss40");
487         sign.setUserInfo("wss40", "security");
488         LOG.info("Before Encryption....");
489 
490         sign.build(crypto);
491 
492         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
493         SecretKey symmetricKey = keyGen.generateKey();
494         Document encryptedSignedDoc = encrypt.build(crypto, symmetricKey);
495 
496         LOG.info("After Encryption....");
497         verify(encryptedSignedDoc);
498     }
499 
500     /**
501      * Verifies the soap envelope <p/>
502      *
503      * @param envelope
504      *
505      * @return the <code>WSSecurityEngineResult</code>s from processing
506      *
507      * @throws Exception
508      *             Thrown when there is a problem in verification
509      */
510     private WSHandlerResult verify(Document doc) throws Exception {
511         WSHandlerResult resultList =
512             secEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
513         if (LOG.isDebugEnabled()) {
514             String outputString =
515                 XMLUtils.prettyDocumentToString(doc);
516             LOG.debug(outputString);
517         }
518 
519         return resultList;
520     }
521 
522 }