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.dom.message;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.URLEncoder;
26  import java.nio.charset.StandardCharsets;
27  import java.util.ArrayList;
28  import java.util.Base64;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.UUID;
32  
33  import javax.crypto.KeyGenerator;
34  import javax.crypto.SecretKey;
35  import javax.security.auth.callback.CallbackHandler;
36  
37  import org.apache.wss4j.common.WSEncryptionPart;
38  import org.apache.wss4j.common.WSS4JConstants;
39  import org.apache.wss4j.common.crypto.Crypto;
40  import org.apache.wss4j.common.crypto.CryptoFactory;
41  import org.apache.wss4j.common.ext.Attachment;
42  import org.apache.wss4j.common.util.KeyUtils;
43  import org.apache.wss4j.common.util.SOAPUtil;
44  import org.apache.wss4j.common.util.XMLUtils;
45  import org.apache.wss4j.dom.WSConstants;
46  import org.apache.wss4j.dom.common.CustomHandler;
47  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
48  import org.apache.wss4j.dom.engine.WSSConfig;
49  import org.apache.wss4j.dom.engine.WSSecurityEngine;
50  import org.apache.wss4j.dom.handler.HandlerAction;
51  import org.apache.wss4j.dom.handler.RequestData;
52  import org.apache.wss4j.dom.handler.WSHandlerConstants;
53  import org.apache.wss4j.dom.handler.WSHandlerResult;
54  import org.apache.wss4j.dom.util.WSSecurityUtil;
55  import org.junit.jupiter.api.Test;
56  import org.w3c.dom.Document;
57  import org.w3c.dom.Element;
58  import org.w3c.dom.Node;
59  
60  import static org.junit.jupiter.api.Assertions.assertNotNull;
61  import static org.junit.jupiter.api.Assertions.assertTrue;
62  
63  /**
64   * Test for creating / processing an xop:Include inside a CipherValue Element
65   */
66  public class XOPAttachmentTest {
67  
68      private static final String SOAP_BODY =
69          "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
70          + "<value xmlns=\"\">15</value>"
71          + "</add>";
72  
73      private static final String SOAP_HEADER_MSG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
74          "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
75          "   <soapenv:Header>" +
76          "       <foo:bar1 xmlns:foo=\"urn:foo.bar\" >baz1</foo:bar1>" +
77          "       <foo:foobar xmlns:foo=\"urn:foo.bar\" >baz</foo:foobar>" +
78          "       <foo:bar2 xmlns:foo=\"urn:foo.bar\" >baz2</foo:bar2>" +
79          "   </soapenv:Header>" +
80          "   <soapenv:Body>" +
81          "      <ns1:testMethod xmlns:ns1=\"http://axis/service/security/test6/LogTestService8\"></ns1:testMethod>" +
82          "   </soapenv:Body>" +
83          "</soapenv:Envelope>";
84  
85      private static final org.slf4j.Logger LOG =
86              org.slf4j.LoggerFactory.getLogger(XOPAttachmentTest.class);
87  
88      private WSSecurityEngine secEngine = new WSSecurityEngine();
89      private Crypto crypto;
90  
91      public XOPAttachmentTest() throws Exception {
92          WSSConfig.init();
93          crypto = CryptoFactory.getInstance();
94      }
95  
96      protected byte[] readInputStream(InputStream inputStream) throws IOException {
97          try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
98              int read = 0;
99              byte[] buf = new byte[4096];
100             while ((read = inputStream.read(buf)) != -1) {
101                 byteArrayOutputStream.write(buf, 0, read);
102             }
103             return byteArrayOutputStream.toByteArray();
104         }
105     }
106 
107     // Set up a test to encrypt the SOAP Body + an attachment, which is the same content as
108     // the SOAP Body. Then replace the encrypted SOAP Body with a xop:Include to the attachment,
109     // and modify the request to remove the encryption stuff pointing to the attachment.
110     // (NOTE: This test was before we supported creating requests with xop:Include)
111     @Test
112     public void testManualEncryptedSOAPBody() throws Exception {
113         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
114         WSSecHeader secHeader = new WSSecHeader(doc);
115         secHeader.insertSecurityHeader();
116 
117         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
118         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
119         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
120 
121         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
122         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
123 
124         String attachmentId = UUID.randomUUID().toString();
125         final Attachment attachment = new Attachment();
126         attachment.setId(attachmentId);
127         attachment.setSourceStream(new ByteArrayInputStream(SOAP_BODY.getBytes(StandardCharsets.UTF_8)));
128 
129         AttachmentCallbackHandler attachmentCallbackHandler =
130             new AttachmentCallbackHandler(Collections.singletonList(attachment));
131         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
132         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
133 
134         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
135         SecretKey symmetricKey = keyGen.generateKey();
136         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
137 
138         // Find the SOAP Body + replace with a xop:Include to the attachment!
139         Element soapBody = WSSecurityUtil.findBodyElement(encryptedDoc);
140         assertNotNull(soapBody);
141         Element encryptedData =
142             XMLUtils.getDirectChildElement(soapBody, "EncryptedData", WSConstants.ENC_NS);
143         encryptedData.removeAttributeNS(null, "Type");
144         Element cipherData =
145             XMLUtils.getDirectChildElement(encryptedData, "CipherData", WSConstants.ENC_NS);
146         assertNotNull(cipherData);
147         Element cipherValue =
148             XMLUtils.getDirectChildElement(cipherData, "CipherValue", WSConstants.ENC_NS);
149         assertNotNull(cipherValue);
150 
151         XMLUtils.setNamespace(cipherValue, WSS4JConstants.XOP_NS, "xop");
152 
153         Element cipherValueChild = encryptedDoc.createElementNS(WSConstants.XOP_NS, "Include");
154         cipherValueChild.setAttributeNS(null, "href", "cid:" + encryptedAttachments.get(0).getId());
155         cipherValue.replaceChild(cipherValueChild, cipherValue.getFirstChild());
156 
157         // Remove EncryptedData structure from the security header (which encrypted the attachment
158         // in the first place)
159         Element securityHeader =
160             WSSecurityUtil.findWsseSecurityHeaderBlock(encryptedDoc, encryptedDoc.getDocumentElement(), false);
161         Element encryptedAttachmentData =
162             XMLUtils.getDirectChildElement(securityHeader, "EncryptedData", WSConstants.ENC_NS);
163         assertNotNull(encryptedAttachmentData);
164         String encryptedDataId = encryptedAttachmentData.getAttributeNS(null, "Id");
165         securityHeader.removeChild(encryptedAttachmentData);
166 
167         // Now get EncryptedKey + remove the reference to the EncryptedData above
168         Element encryptedKey =
169             XMLUtils.getDirectChildElement(securityHeader, "EncryptedKey", WSConstants.ENC_NS);
170         assertNotNull(encryptedKey);
171         Element referenceList =
172             XMLUtils.getDirectChildElement(encryptedKey, "ReferenceList", WSConstants.ENC_NS);
173         assertNotNull(referenceList);
174         Node child = referenceList.getFirstChild();
175         while (child != null) {
176             if (child instanceof Element && "DataReference".equals(child.getLocalName())
177                 && WSConstants.ENC_NS.equals(child.getNamespaceURI())) {
178                 String uri = ((Element)child).getAttributeNS(null, "URI");
179                 if (uri.equals("#" + encryptedDataId)) {
180                     referenceList.removeChild(child);
181                     break;
182                 }
183             }
184             child = child.getNextSibling();
185         }
186 
187         if (LOG.isDebugEnabled()) {
188             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
189             LOG.debug(outputString);
190             //System.out.println(outputString);
191         }
192 
193         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
194         verify(encryptedDoc, attachmentCallbackHandler);
195 
196         String processedDoc = XMLUtils.prettyDocumentToString(encryptedDoc);
197         assertTrue(processedDoc.contains(SOAP_BODY));
198     }
199 
200     @Test
201     public void testEncryptedSOAPBody() throws Exception {
202         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
203         WSSecHeader secHeader = new WSSecHeader(doc);
204         secHeader.insertSecurityHeader();
205 
206         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
207         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
208         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
209 
210         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
211         encrypt.setAttachmentCallbackHandler(outboundAttachmentCallback);
212         encrypt.setStoreBytesInAttachment(true);
213 
214         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
215 
216         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
217         SecretKey symmetricKey = keyGen.generateKey();
218         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
219 
220         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
221         assertNotNull(encryptedAttachments);
222         // Should have EncryptedKey + EncryptedData stored in attachments...
223         assertTrue(encryptedAttachments.size() == 2);
224 
225         if (LOG.isDebugEnabled()) {
226             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
227             LOG.debug(outputString);
228             // System.out.println(outputString);
229         }
230 
231         AttachmentCallbackHandler inboundAttachmentCallback =
232             new AttachmentCallbackHandler(encryptedAttachments);
233         verify(encryptedDoc, inboundAttachmentCallback);
234 
235         String processedDoc = XMLUtils.prettyDocumentToString(encryptedDoc);
236         assertTrue(processedDoc.contains(SOAP_BODY));
237     }
238 
239     // See https://issues.apache.org/jira/browse/CXF-8061
240     @Test
241     public void testEncryptedSOAPBodyURLEncoding() throws Exception {
242         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
243         WSSecHeader secHeader = new WSSecHeader(doc);
244         secHeader.insertSecurityHeader();
245 
246         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
247         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
248         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
249 
250         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
251         encrypt.setAttachmentCallbackHandler(outboundAttachmentCallback);
252         encrypt.setStoreBytesInAttachment(true);
253 
254         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
255 
256         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
257         SecretKey symmetricKey = keyGen.generateKey();
258         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
259 
260         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
261         assertNotNull(encryptedAttachments);
262         // Should have EncryptedKey + EncryptedData stored in attachments...
263         assertTrue(encryptedAttachments.size() == 2);
264 
265         // Override the Attachment ID + URL encode something that will break as the Attachment ID if it is not
266         // URL encoded
267         String newId = "http://tempuri.org/1/636966400494014846";
268         String oldId = encryptedAttachments.get(1).getId();
269         encryptedAttachments.get(1).setId(newId);
270         List<Element> xopElements =
271             XMLUtils.findElements(doc.getDocumentElement(), "Include", "http://www.w3.org/2004/08/xop/include");
272         for (Element xop : xopElements) {
273             if (xop.hasAttribute("href") && xop.getAttributeNS(null, "href").equals("cid:" + oldId)) {
274                 xop.setAttributeNS(null, "href", "cid:" + URLEncoder.encode(newId, StandardCharsets.UTF_8.name()));
275             }
276         }
277 
278         if (LOG.isDebugEnabled()) {
279             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
280             LOG.debug(outputString);
281             // System.out.println(outputString);
282         }
283 
284         AttachmentCallbackHandler inboundAttachmentCallback =
285             new AttachmentCallbackHandler(encryptedAttachments);
286         verify(encryptedDoc, inboundAttachmentCallback);
287 
288         String processedDoc = XMLUtils.prettyDocumentToString(encryptedDoc);
289         assertTrue(processedDoc.contains(SOAP_BODY));
290     }
291 
292     // Here we are storing the BinarySecurityToken bytes in an attachment
293     @Test
294     public void testSignedSOAPBody() throws Exception {
295         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
296         WSSecHeader secHeader = new WSSecHeader(doc);
297         secHeader.insertSecurityHeader();
298 
299         WSSecSignature builder = new WSSecSignature(secHeader);
300         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
301         builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
302 
303         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
304         builder.setAttachmentCallbackHandler(outboundAttachmentCallback);
305         builder.setStoreBytesInAttachment(true);
306 
307         Document signedDoc = builder.build(crypto);
308 
309         List<Attachment> signedAttachments = outboundAttachmentCallback.getResponseAttachments();
310         assertNotNull(signedAttachments);
311         assertTrue(signedAttachments.size() == 1);
312 
313         if (LOG.isDebugEnabled()) {
314             LOG.debug("After Signing....");
315             String outputString =
316                 XMLUtils.prettyDocumentToString(signedDoc);
317             LOG.debug(outputString);
318         }
319 
320         AttachmentCallbackHandler inboundAttachmentCallback =
321             new AttachmentCallbackHandler(signedAttachments);
322         verify(signedDoc, inboundAttachmentCallback);
323     }
324 
325     @Test
326     public void testSignedSOAPBodyAndBinarySecurityToken() throws Exception {
327         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
328         WSSecHeader secHeader = new WSSecHeader(doc);
329         secHeader.insertSecurityHeader();
330 
331         WSSecSignature builder = new WSSecSignature(secHeader);
332         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
333         builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
334         builder.setIncludeSignatureToken(true);
335 
336         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
337         builder.setAttachmentCallbackHandler(outboundAttachmentCallback);
338         builder.setStoreBytesInAttachment(true);
339 
340         Document signedDoc = builder.build(crypto);
341 
342         List<Attachment> signedAttachments = outboundAttachmentCallback.getResponseAttachments();
343         assertNotNull(signedAttachments);
344         assertTrue(signedAttachments.size() == 1);
345 
346         if (LOG.isDebugEnabled()) {
347             LOG.debug("After Signing....");
348             String outputString =
349                 XMLUtils.prettyDocumentToString(signedDoc);
350             LOG.debug(outputString);
351         }
352 
353         AttachmentCallbackHandler inboundAttachmentCallback =
354             new AttachmentCallbackHandler(signedAttachments);
355         verify(signedDoc, inboundAttachmentCallback);
356     }
357 
358     @Test
359     public void testEncryptedHeaderAsEncryptedData() throws Exception {
360         Document doc = SOAPUtil.toSOAPPart(SOAP_HEADER_MSG);
361         WSSecHeader secHeader = new WSSecHeader(doc);
362         secHeader.insertSecurityHeader();
363 
364         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
365         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
366         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
367 
368         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
369         encrypt.setAttachmentCallbackHandler(outboundAttachmentCallback);
370         encrypt.setStoreBytesInAttachment(true);
371 
372         WSEncryptionPart encP =
373             new WSEncryptionPart(
374                 "foobar", "urn:foo.bar", "");
375         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
376         encrypt.getParts().add(encP);
377 
378         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
379         SecretKey symmetricKey = keyGen.generateKey();
380         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
381 
382         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
383         assertNotNull(encryptedAttachments);
384         // Should have EncryptedKey + EncryptedData + the header stored in attachments...
385         assertTrue(encryptedAttachments.size() == 3);
386 
387         if (LOG.isDebugEnabled()) {
388             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
389             LOG.debug(outputString);
390             // System.out.println(outputString);
391         }
392 
393         AttachmentCallbackHandler inboundAttachmentCallback =
394             new AttachmentCallbackHandler(encryptedAttachments);
395         verify(encryptedDoc, inboundAttachmentCallback);
396     }
397 
398     @Test
399     public void testEncryptedHeaderasEncryptedHeader() throws Exception {
400         Document doc = SOAPUtil.toSOAPPart(SOAP_HEADER_MSG);
401         WSSecHeader secHeader = new WSSecHeader(doc);
402         secHeader.insertSecurityHeader();
403 
404         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
405         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
406         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
407 
408         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
409         encrypt.setAttachmentCallbackHandler(outboundAttachmentCallback);
410         encrypt.setStoreBytesInAttachment(true);
411 
412         WSEncryptionPart encP =
413             new WSEncryptionPart(
414                 "foobar", "urn:foo.bar", "Header");
415         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
416         encrypt.getParts().add(encP);
417 
418         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
419         SecretKey symmetricKey = keyGen.generateKey();
420         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
421 
422         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
423         assertNotNull(encryptedAttachments);
424         // Should have EncryptedKey + EncryptedData + the header stored in attachments...
425         assertTrue(encryptedAttachments.size() == 3);
426 
427         if (LOG.isDebugEnabled()) {
428             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
429             LOG.debug(outputString);
430         }
431 
432         AttachmentCallbackHandler inboundAttachmentCallback =
433             new AttachmentCallbackHandler(encryptedAttachments);
434         verify(encryptedDoc, inboundAttachmentCallback);
435     }
436 
437     @Test
438     public void testDerivedEncryptedSOAPBody() throws Exception {
439         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
440         WSSecHeader secHeader = new WSSecHeader(doc);
441         secHeader.insertSecurityHeader();
442 
443         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
444 
445         //EncryptedKey
446         WSSecEncryptedKey encrKeyBuilder = new WSSecEncryptedKey(secHeader);
447         encrKeyBuilder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e");
448         encrKeyBuilder.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
449         encrKeyBuilder.setAttachmentCallbackHandler(outboundAttachmentCallback);
450         encrKeyBuilder.setStoreBytesInAttachment(true);
451 
452         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
453         SecretKey symmetricKey = keyGen.generateKey();
454         encrKeyBuilder.prepare(crypto, symmetricKey);
455 
456         //Key information from the EncryptedKey
457         byte[] ek = symmetricKey.getEncoded();
458         String tokenIdentifier = encrKeyBuilder.getId();
459 
460         //Derived key encryption
461         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt(secHeader);
462         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
463         encrBuilder.setTokenIdentifier(tokenIdentifier);
464         encrBuilder.setAttachmentCallbackHandler(outboundAttachmentCallback);
465         encrBuilder.setStoreBytesInAttachment(true);
466         Document encryptedDoc = encrBuilder.build(ek);
467 
468         encrKeyBuilder.prependToHeader();
469         encrKeyBuilder.prependBSTElementToHeader();
470 
471         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
472         assertNotNull(encryptedAttachments);
473         // Should have EncryptedKey + EncryptedData stored in attachments...
474         assertTrue(encryptedAttachments.size() == 2);
475 
476         if (LOG.isDebugEnabled()) {
477             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
478             LOG.debug(outputString);
479             // System.out.println(outputString);
480         }
481 
482         AttachmentCallbackHandler inboundAttachmentCallback =
483             new AttachmentCallbackHandler(encryptedAttachments);
484         verify(encryptedDoc, inboundAttachmentCallback);
485 
486         String processedDoc = XMLUtils.prettyDocumentToString(encryptedDoc);
487         assertTrue(processedDoc.contains(SOAP_BODY));
488     }
489 
490     @Test
491     public void testDerivedSignedSOAPBody() throws Exception {
492         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
493         WSSecHeader secHeader = new WSSecHeader(doc);
494         secHeader.insertSecurityHeader();
495 
496         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
497 
498         //EncryptedKey
499         WSSecEncryptedKey encrKeyBuilder = new WSSecEncryptedKey(secHeader);
500         encrKeyBuilder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e");
501         encrKeyBuilder.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
502         encrKeyBuilder.setAttachmentCallbackHandler(outboundAttachmentCallback);
503         encrKeyBuilder.setStoreBytesInAttachment(true);
504 
505         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
506         SecretKey symmetricKey = keyGen.generateKey();
507         encrKeyBuilder.prepare(crypto, symmetricKey);
508 
509         //Key information from the EncryptedKey
510         byte[] ek = symmetricKey.getEncoded();
511         String tokenIdentifier = encrKeyBuilder.getId();
512 
513         //Derived key encryption
514         WSSecDKSign sigBuilder = new WSSecDKSign(secHeader);
515         sigBuilder.setTokenIdentifier(tokenIdentifier);
516         sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
517         sigBuilder.setAttachmentCallbackHandler(outboundAttachmentCallback);
518         sigBuilder.setStoreBytesInAttachment(true);
519         Document signedDoc = sigBuilder.build(ek);
520 
521         encrKeyBuilder.prependToHeader();
522         encrKeyBuilder.prependBSTElementToHeader();
523 
524         List<Attachment> signedAttachments = outboundAttachmentCallback.getResponseAttachments();
525         assertNotNull(signedAttachments);
526         assertTrue(signedAttachments.size() == 1);
527 
528         if (LOG.isDebugEnabled()) {
529             String outputString = XMLUtils.prettyDocumentToString(signedDoc);
530             LOG.debug(outputString);
531         }
532 
533         AttachmentCallbackHandler inboundAttachmentCallback =
534             new AttachmentCallbackHandler(signedAttachments);
535         verify(signedDoc, inboundAttachmentCallback);
536 
537         String processedDoc = XMLUtils.prettyDocumentToString(signedDoc);
538         assertTrue(processedDoc.contains(SOAP_BODY));
539     }
540 
541     @Test
542     public void testSignedEncryptedSOAPBody() throws Exception {
543         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
544         WSSecHeader secHeader = new WSSecHeader(doc);
545         secHeader.insertSecurityHeader();
546 
547         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
548 
549         WSSecSignature builder = new WSSecSignature(secHeader);
550         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
551         builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
552 
553         builder.setAttachmentCallbackHandler(outboundAttachmentCallback);
554         builder.setStoreBytesInAttachment(true);
555         builder.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
556         builder.build(crypto);
557 
558         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
559         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
560         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
561 
562         encrypt.setAttachmentCallbackHandler(outboundAttachmentCallback);
563         encrypt.setStoreBytesInAttachment(true);
564         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
565 
566         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
567         SecretKey symmetricKey = keyGen.generateKey();
568         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
569 
570         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
571         assertNotNull(encryptedAttachments);
572         assertTrue(encryptedAttachments.size() == 3);
573 
574         if (LOG.isDebugEnabled()) {
575             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
576             LOG.debug(outputString);
577             // System.out.println(outputString);
578         }
579 
580         AttachmentCallbackHandler inboundAttachmentCallback =
581             new AttachmentCallbackHandler(encryptedAttachments);
582         verify(encryptedDoc, inboundAttachmentCallback);
583 
584         String processedDoc = XMLUtils.prettyDocumentToString(encryptedDoc);
585         assertTrue(processedDoc.contains(SOAP_BODY));
586     }
587 
588     @Test
589     public void testSignedEncryptedSOAPBodyViaHandler() throws Exception {
590         final WSSConfig cfg = WSSConfig.getNewInstance();
591         final RequestData reqData = new RequestData();
592         reqData.setWssConfig(cfg);
593         reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
594 
595         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
596         reqData.setAttachmentCallbackHandler(outboundAttachmentCallback);
597 
598 
599         java.util.Map<String, Object> config = new java.util.TreeMap<>();
600         config.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
601         config.put(WSHandlerConstants.ENC_PROP_FILE, "crypto.properties");
602         config.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
603         config.put("password", "security");
604         config.put(WSHandlerConstants.STORE_BYTES_IN_ATTACHMENT, "true");
605         reqData.setMsgContext(config);
606 
607         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
608         CustomHandler handler = new CustomHandler();
609         List<HandlerAction> actions = new ArrayList<>();
610         actions.add(new HandlerAction(WSConstants.SIGN));
611         actions.add(new HandlerAction(WSConstants.ENCR));
612 
613         handler.send(
614             doc,
615             reqData,
616             actions,
617             true
618         );
619         String outputString =
620             XMLUtils.prettyDocumentToString(doc);
621         if (LOG.isDebugEnabled()) {
622             LOG.debug("Signed message:");
623             LOG.debug(outputString);
624         }
625 
626         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
627         assertNotNull(encryptedAttachments);
628         assertTrue(encryptedAttachments.size() == 3);
629 
630         AttachmentCallbackHandler inboundAttachmentCallback =
631             new AttachmentCallbackHandler(encryptedAttachments);
632         verify(doc, inboundAttachmentCallback);
633 
634         String processedDoc = XMLUtils.prettyDocumentToString(doc);
635         assertTrue(processedDoc.contains(SOAP_BODY));
636     }
637 
638     @Test
639     public void testEncryptedSignedSOAPBodyViaHandler() throws Exception {
640         final WSSConfig cfg = WSSConfig.getNewInstance();
641         final RequestData reqData = new RequestData();
642         reqData.setWssConfig(cfg);
643         reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
644 
645         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
646         reqData.setAttachmentCallbackHandler(outboundAttachmentCallback);
647 
648 
649         java.util.Map<String, Object> config = new java.util.TreeMap<>();
650         config.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
651         config.put(WSHandlerConstants.ENC_PROP_FILE, "crypto.properties");
652         config.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
653         config.put("password", "security");
654         config.put(WSHandlerConstants.STORE_BYTES_IN_ATTACHMENT, "true");
655         reqData.setMsgContext(config);
656 
657         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
658         CustomHandler handler = new CustomHandler();
659         List<HandlerAction> actions = new ArrayList<>();
660         actions.add(new HandlerAction(WSConstants.ENCR));
661         actions.add(new HandlerAction(WSConstants.SIGN));
662 
663         handler.send(
664             doc,
665             reqData,
666             actions,
667             true
668         );
669         String outputString =
670             XMLUtils.prettyDocumentToString(doc);
671         if (LOG.isDebugEnabled()) {
672             LOG.debug("Signed message:");
673             LOG.debug(outputString);
674         }
675 
676         List<Attachment> encryptedAttachments = outboundAttachmentCallback.getResponseAttachments();
677         assertNotNull(encryptedAttachments);
678         assertTrue(encryptedAttachments.isEmpty());
679 
680         AttachmentCallbackHandler inboundAttachmentCallback =
681             new AttachmentCallbackHandler(encryptedAttachments);
682         verify(doc, inboundAttachmentCallback);
683 
684         String processedDoc = XMLUtils.prettyDocumentToString(doc);
685         assertTrue(processedDoc.contains(SOAP_BODY));
686     }
687 
688     // We can't put the SignatureValue bytes into an attachment as we are using the JSR-105 API, but for now
689     // let's test that we can process such messages
690     @Test
691     public void testSignatureValueInAttachment() throws Exception {
692         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
693         WSSecHeader secHeader = new WSSecHeader(doc);
694         secHeader.insertSecurityHeader();
695 
696         WSSecSignature builder = new WSSecSignature(secHeader);
697         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
698         builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
699 
700         AttachmentCallbackHandler outboundAttachmentCallback = new AttachmentCallbackHandler();
701         builder.setAttachmentCallbackHandler(outboundAttachmentCallback);
702         builder.setStoreBytesInAttachment(true);
703 
704         Document signedDoc = builder.build(crypto);
705 
706         List<Attachment> signedAttachments = outboundAttachmentCallback.getResponseAttachments();
707         assertNotNull(signedAttachments);
708         assertTrue(signedAttachments.size() == 1);
709 
710         // Find the SignatureValue + replace with a xop:Include to the attachment!
711         Element signatureValue = XMLUtils.findElement(signedDoc.getDocumentElement(), "SignatureValue", WSConstants.SIG_NS);
712         assertNotNull(signatureValue);
713 
714         String attachmentId = UUID.randomUUID().toString();
715         final Attachment attachment = new Attachment();
716         attachment.setId(attachmentId);
717         byte[] decodedBytes = Base64.getMimeDecoder().decode(signatureValue.getTextContent());
718         attachment.setSourceStream(new ByteArrayInputStream(decodedBytes));
719         signedAttachments.add(attachment);
720 
721         XMLUtils.setNamespace(signatureValue, WSS4JConstants.XOP_NS, "xop");
722 
723         Element signatureValueChild = signedDoc.createElementNS(WSConstants.XOP_NS, "xop:Include");
724         signatureValueChild.setAttributeNS(null, "href", "cid:" + attachmentId);
725         signatureValue.replaceChild(signatureValueChild, signatureValue.getFirstChild());
726 
727         if (LOG.isDebugEnabled()) {
728             LOG.debug("After Signing....");
729             String outputString =
730                 XMLUtils.prettyDocumentToString(signedDoc);
731             LOG.debug(outputString);
732         }
733 
734         AttachmentCallbackHandler inboundAttachmentCallback =
735             new AttachmentCallbackHandler(signedAttachments);
736         verify(signedDoc, inboundAttachmentCallback);
737     }
738 
739     /**
740      * Verifies the soap envelope.
741      * This method verifies all the signature generated.
742      *
743      * @throws Exception Thrown when there is a problem in verification
744      */
745     private WSHandlerResult verify(Document doc, CallbackHandler attachmentCallbackHandler) throws Exception { //NOPMD
746         RequestData requestData = new RequestData();
747         requestData.setAttachmentCallbackHandler(attachmentCallbackHandler);
748         requestData.setSigVerCrypto(crypto);
749         requestData.setDecCrypto(crypto);
750         requestData.setCallbackHandler(new KeystoreCallbackHandler());
751         requestData.setExpandXopInclude(true);
752         return secEngine.processSecurityHeader(doc, requestData);
753     }
754 }