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.io.PushbackInputStream;
26  import java.nio.charset.StandardCharsets;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.UUID;
34  
35  import javax.crypto.KeyGenerator;
36  import javax.crypto.SecretKey;
37  import javax.security.auth.callback.Callback;
38  import javax.security.auth.callback.CallbackHandler;
39  import javax.security.auth.callback.UnsupportedCallbackException;
40  
41  import org.apache.wss4j.common.WSEncryptionPart;
42  import org.apache.wss4j.common.crypto.Crypto;
43  import org.apache.wss4j.common.crypto.CryptoFactory;
44  import org.apache.wss4j.common.ext.Attachment;
45  import org.apache.wss4j.common.ext.AttachmentRequestCallback;
46  import org.apache.wss4j.common.ext.AttachmentResultCallback;
47  import org.apache.wss4j.common.ext.WSSecurityException;
48  import org.apache.wss4j.common.util.AttachmentUtils;
49  import org.apache.wss4j.common.util.KeyUtils;
50  import org.apache.wss4j.common.util.SOAPUtil;
51  import org.apache.wss4j.common.util.XMLUtils;
52  import org.apache.wss4j.dom.WSConstants;
53  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
54  import org.apache.wss4j.dom.engine.WSSConfig;
55  import org.apache.wss4j.dom.engine.WSSecurityEngine;
56  import org.apache.wss4j.dom.handler.RequestData;
57  import org.apache.wss4j.dom.handler.WSHandlerResult;
58  import org.junit.jupiter.api.Test;
59  import org.w3c.dom.Document;
60  import org.w3c.dom.Element;
61  import org.w3c.dom.NodeList;
62  
63  import static org.junit.jupiter.api.Assertions.assertEquals;
64  import static org.junit.jupiter.api.Assertions.assertFalse;
65  import static org.junit.jupiter.api.Assertions.assertTrue;
66  import static org.junit.jupiter.api.Assertions.fail;
67  import static org.junit.jupiter.api.Assumptions.assumeFalse;
68  
69  public class AttachmentTest {
70  
71      private static final org.slf4j.Logger LOG =
72              org.slf4j.LoggerFactory.getLogger(AttachmentTest.class);
73  
74      private WSSecurityEngine secEngine = new WSSecurityEngine();
75      private Crypto crypto;
76  
77      private boolean isIBMJdK = System.getProperty("java.vendor").contains("IBM");
78  
79      public AttachmentTest() throws Exception {
80          WSSConfig.init();
81          crypto = CryptoFactory.getInstance();
82      }
83  
84      protected Map<String, String> getHeaders(String attachmentId) {
85          Map<String, String> headers = new HashMap<>();
86          headers.put(AttachmentUtils.MIME_HEADER_CONTENT_DESCRIPTION, "Attachment");
87          headers.put(AttachmentUtils.MIME_HEADER_CONTENT_DISPOSITION, "attachment; filename=\"fname.ext\"");
88          headers.put(AttachmentUtils.MIME_HEADER_CONTENT_ID, "<attachment=" + attachmentId + ">");
89          headers.put(AttachmentUtils.MIME_HEADER_CONTENT_LOCATION, "http://ws.apache.org");
90          headers.put(AttachmentUtils.MIME_HEADER_CONTENT_TYPE, "text/xml; charset=UTF-8");
91          headers.put("TestHeader", "testHeaderValue");
92          return headers;
93      }
94  
95      protected byte[] readInputStream(InputStream inputStream) throws IOException {
96          try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
97              int read = 0;
98              byte[] buf = new byte[4096];
99              while ((read = inputStream.read(buf)) != -1) {
100                 byteArrayOutputStream.write(buf, 0, read);
101             }
102             return byteArrayOutputStream.toByteArray();
103         }
104     }
105 
106     @Test
107     public void testXMLAttachmentContentSignature() throws Exception {
108         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
109         WSSecHeader secHeader = new WSSecHeader(doc);
110         secHeader.insertSecurityHeader();
111 
112         WSSecSignature builder = new WSSecSignature(secHeader);
113         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
114 
115         builder.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
116         builder.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
117 
118         final String attachmentId = UUID.randomUUID().toString();
119         final Attachment attachment = new Attachment();
120         attachment.setMimeType("text/xml");
121         attachment.addHeaders(getHeaders(attachmentId));
122         attachment.setId(attachmentId);
123         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
124 
125         AttachmentCallbackHandler attachmentCallbackHandler =
126             new AttachmentCallbackHandler(Collections.singletonList(attachment));
127         builder.setAttachmentCallbackHandler(attachmentCallbackHandler);
128 
129         LOG.info("Before Signing....");
130         Document signedDoc = builder.build(crypto);
131 
132         if (LOG.isDebugEnabled()) {
133             LOG.debug("After Signing....");
134             String outputString = XMLUtils.prettyDocumentToString(signedDoc);
135             LOG.debug(outputString);
136         }
137 
138         NodeList sigReferences = signedDoc.getElementsByTagNameNS(WSConstants.SIG_NS, "Reference");
139         assertEquals(2, sigReferences.getLength());
140 
141         attachmentCallbackHandler =
142             new AttachmentCallbackHandler(Collections.singletonList(attachment));
143         verify(signedDoc, attachmentCallbackHandler);
144 
145         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
146         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
147         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
148         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
149         assertEquals("text/xml", responseAttachment.getMimeType());
150     }
151 
152     @Test
153     public void testInvalidXMLAttachmentContentSignature() throws Exception {
154         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
155         WSSecHeader secHeader = new WSSecHeader(doc);
156         secHeader.insertSecurityHeader();
157 
158         WSSecSignature builder = new WSSecSignature(secHeader);
159         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
160 
161         builder.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
162         builder.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
163 
164         final String attachmentId = UUID.randomUUID().toString();
165         final Attachment attachment = new Attachment();
166         attachment.setMimeType("text/xml");
167         attachment.addHeaders(getHeaders(attachmentId));
168         attachment.setId(attachmentId);
169         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
170 
171         AttachmentCallbackHandler attachmentCallbackHandler =
172             new AttachmentCallbackHandler(Collections.singletonList(attachment));
173         builder.setAttachmentCallbackHandler(attachmentCallbackHandler);
174 
175         LOG.info("Before Signing....");
176         Document signedDoc = builder.build(crypto);
177 
178         if (LOG.isDebugEnabled()) {
179             LOG.debug("After Signing....");
180             String outputString = XMLUtils.prettyDocumentToString(signedDoc);
181             LOG.debug(outputString);
182         }
183 
184         try {
185             verify(signedDoc, new CallbackHandler() {
186                 @Override
187                 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
188                     if (callbacks[0] instanceof AttachmentRequestCallback) {
189                         AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
190 
191                         if (!attachment.getId().equals(attachmentRequestCallback.getAttachmentId())) {
192                             throw new RuntimeException("wrong attachment requested");
193                         }
194 
195                         List<Attachment> attachments = new ArrayList<>();
196                         attachment.setSourceStream(new ByteArrayInputStream(
197                                 SOAPUtil.SAMPLE_SOAP_MSG.replace("15", "16").getBytes(StandardCharsets.UTF_8)));
198                         attachments.add(attachment);
199                         attachmentRequestCallback.setAttachments(attachments);
200                     }
201                 }
202             });
203             fail();
204         } catch (WSSecurityException e) {
205             assertEquals(e.getMessage(), "The signature or decryption was invalid");
206         }
207     }
208 
209     @Test
210     public void testXMLAttachmentCompleteSignature() throws Exception {
211         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
212         WSSecHeader secHeader = new WSSecHeader(doc);
213         secHeader.insertSecurityHeader();
214 
215         WSSecSignature builder = new WSSecSignature(secHeader);
216         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
217 
218         builder.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
219         builder.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
220 
221         final String attachmentId = UUID.randomUUID().toString();
222         final Attachment attachment = new Attachment();
223         attachment.setMimeType("text/xml");
224         attachment.addHeaders(getHeaders(attachmentId));
225         attachment.setId(attachmentId);
226         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
227 
228         AttachmentCallbackHandler attachmentCallbackHandler =
229             new AttachmentCallbackHandler(Collections.singletonList(attachment));
230         builder.setAttachmentCallbackHandler(attachmentCallbackHandler);
231 
232         LOG.info("Before Signing....");
233         Document signedDoc = builder.build(crypto);
234 
235         if (LOG.isDebugEnabled()) {
236             LOG.debug("After Signing....");
237             String outputString = XMLUtils.prettyDocumentToString(signedDoc);
238             LOG.debug(outputString);
239         }
240 
241         NodeList sigReferences = signedDoc.getElementsByTagNameNS(WSConstants.SIG_NS, "Reference");
242         assertEquals(2, sigReferences.getLength());
243 
244         attachmentCallbackHandler =
245             new AttachmentCallbackHandler(Collections.singletonList(attachment));
246         verify(signedDoc, attachmentCallbackHandler);
247 
248         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
249         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
250         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
251         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
252         assertEquals("text/xml", responseAttachment.getMimeType());
253     }
254 
255     @Test
256     public void testInvalidXMLAttachmentCompleteSignature() throws Exception {
257         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
258         WSSecHeader secHeader = new WSSecHeader(doc);
259         secHeader.insertSecurityHeader();
260 
261         WSSecSignature builder = new WSSecSignature(secHeader);
262         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
263 
264         builder.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
265         builder.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
266 
267         final String attachmentId = UUID.randomUUID().toString();
268         final Attachment attachment = new Attachment();
269         attachment.setMimeType("text/xml");
270         attachment.addHeaders(getHeaders(attachmentId));
271         attachment.setId(attachmentId);
272         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
273 
274         AttachmentCallbackHandler attachmentCallbackHandler =
275             new AttachmentCallbackHandler(Collections.singletonList(attachment));
276         builder.setAttachmentCallbackHandler(attachmentCallbackHandler);
277 
278         LOG.info("Before Signing....");
279         Document signedDoc = builder.build(crypto);
280 
281         if (LOG.isDebugEnabled()) {
282             LOG.debug("After Signing....");
283             String outputString = XMLUtils.prettyDocumentToString(signedDoc);
284             LOG.debug(outputString);
285         }
286 
287         try {
288             attachment.addHeader(AttachmentUtils.MIME_HEADER_CONTENT_DESCRIPTION, "Kaputt");
289             verify(signedDoc, new CallbackHandler() {
290                 @Override
291                 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
292                     if (callbacks[0] instanceof AttachmentRequestCallback) {
293                         AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
294 
295                         if (!attachment.getId().equals(attachmentRequestCallback.getAttachmentId())) {
296                             throw new RuntimeException("wrong attachment requested");
297                         }
298 
299                         List<Attachment> attachments = new ArrayList<>();
300                         attachments.add(attachment);
301                         attachmentRequestCallback.setAttachments(attachments);
302                     }
303                 }
304             });
305             fail();
306         } catch (WSSecurityException e) {
307             assertEquals(e.getMessage(), "The signature or decryption was invalid");
308         }
309     }
310 
311     @Test
312     public void testMultipleAttachmentCompleteSignature() throws Exception {
313         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
314         WSSecHeader secHeader = new WSSecHeader(doc);
315         secHeader.insertSecurityHeader();
316 
317         WSSecSignature builder = new WSSecSignature(secHeader);
318         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
319 
320         builder.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
321         builder.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
322 
323         final String attachment1Id = UUID.randomUUID().toString();
324         final Attachment[] attachment = new Attachment[2];
325         attachment[0] = new Attachment();
326         attachment[0].setMimeType("text/xml");
327         attachment[0].addHeaders(getHeaders(attachment1Id));
328         attachment[0].setId(attachment1Id);
329         attachment[0].setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
330 
331         final String attachment2Id = UUID.randomUUID().toString();
332         attachment[1] = new Attachment();
333         attachment[1].setMimeType("text/plain");
334         attachment[1].addHeaders(getHeaders(attachment2Id));
335         attachment[1].setId(attachment2Id);
336         attachment[1].setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
337 
338         AttachmentCallbackHandler attachmentCallbackHandler =
339             new AttachmentCallbackHandler(Arrays.asList(attachment));
340         builder.setAttachmentCallbackHandler(attachmentCallbackHandler);
341 
342         LOG.info("Before Signing....");
343         Document signedDoc = builder.build(crypto);
344 
345         if (LOG.isDebugEnabled()) {
346             LOG.debug("After Signing....");
347             String outputString = XMLUtils.prettyDocumentToString(signedDoc);
348             LOG.debug(outputString);
349         }
350 
351         NodeList sigReferences = signedDoc.getElementsByTagNameNS(WSConstants.SIG_NS, "Reference");
352         assertEquals(3, sigReferences.getLength());
353 
354         attachmentCallbackHandler = new AttachmentCallbackHandler(Arrays.asList(attachment));
355         verify(signedDoc, attachmentCallbackHandler);
356 
357         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
358         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
359         byte[] attachment1Bytes = readInputStream(responseAttachment.getSourceStream());
360         assertTrue(Arrays.equals(attachment1Bytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
361         assertEquals("text/xml", responseAttachment.getMimeType());
362 
363         responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(1);
364         byte[] attachment2Bytes = readInputStream(responseAttachment.getSourceStream());
365         assertTrue(Arrays.equals(attachment2Bytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
366         assertEquals("text/plain", responseAttachment.getMimeType());
367     }
368 
369     @Test
370     public void testXMLAttachmentContentEncryption() throws Exception {
371         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
372         WSSecHeader secHeader = new WSSecHeader(doc);
373         secHeader.insertSecurityHeader();
374 
375         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
376         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
377         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
378 
379         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
380         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
381 
382         String attachmentId = UUID.randomUUID().toString();
383         final Attachment attachment = new Attachment();
384         attachment.setMimeType("text/xml");
385         attachment.addHeaders(getHeaders(attachmentId));
386         attachment.setId(attachmentId);
387         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
388 
389         AttachmentCallbackHandler attachmentCallbackHandler =
390             new AttachmentCallbackHandler(Collections.singletonList(attachment));
391         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
392         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
393 
394         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
395         SecretKey symmetricKey = keyGen.generateKey();
396         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
397 
398         if (LOG.isDebugEnabled()) {
399             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
400             LOG.debug(outputString);
401         }
402 
403         NodeList references = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "DataReference");
404         assertEquals(2, references.getLength());
405         NodeList cipherReferences = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "CipherReference");
406         assertEquals(1, cipherReferences.getLength());
407         NodeList encDatas = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "EncryptedData");
408         assertEquals(2, encDatas.getLength());
409 
410         NodeList securityHeaderElement = doc.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
411         assertEquals(1, securityHeaderElement.getLength());
412         NodeList childs = securityHeaderElement.item(0).getChildNodes();
413         assertEquals(2, childs.getLength());
414         assertEquals(childs.item(0).getLocalName(), "EncryptedKey");
415         assertEquals(childs.item(1).getLocalName(), "EncryptedData");
416 
417         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
418         verify(encryptedDoc, attachmentCallbackHandler);
419 
420         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
421         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
422         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
423         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
424         assertEquals("text/xml", responseAttachment.getMimeType());
425 
426         Map<String, String> attHeaders = responseAttachment.getHeaders();
427         assertEquals(6, attHeaders.size());
428     }
429 
430     @Test
431     public void testXMLAttachmentContentEncryptionGCM() throws Exception {
432         assumeFalse(isIBMJdK);
433 
434         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
435         WSSecHeader secHeader = new WSSecHeader(doc);
436         secHeader.insertSecurityHeader();
437 
438         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
439         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
440         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
441         encrypt.setSymmetricEncAlgorithm(WSConstants.AES_128_GCM);
442 
443         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
444         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
445 
446         String attachmentId = UUID.randomUUID().toString();
447         final Attachment attachment = new Attachment();
448         attachment.setMimeType("text/xml");
449         attachment.addHeaders(getHeaders(attachmentId));
450         attachment.setId(attachmentId);
451         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
452 
453         AttachmentCallbackHandler attachmentCallbackHandler =
454             new AttachmentCallbackHandler(Collections.singletonList(attachment));
455         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
456         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
457 
458         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128_GCM);
459         SecretKey symmetricKey = keyGen.generateKey();
460         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
461 
462         if (LOG.isDebugEnabled()) {
463             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
464             LOG.debug(outputString);
465         }
466 
467         NodeList references = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "DataReference");
468         assertEquals(2, references.getLength());
469         NodeList cipherReferences = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "CipherReference");
470         assertEquals(1, cipherReferences.getLength());
471         NodeList encDatas = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "EncryptedData");
472         assertEquals(2, encDatas.getLength());
473 
474         NodeList securityHeaderElement = doc.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
475         assertEquals(1, securityHeaderElement.getLength());
476         NodeList childs = securityHeaderElement.item(0).getChildNodes();
477         assertEquals(2, childs.getLength());
478         assertEquals(childs.item(0).getLocalName(), "EncryptedKey");
479         assertEquals(childs.item(1).getLocalName(), "EncryptedData");
480 
481         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
482         verify(encryptedDoc, attachmentCallbackHandler);
483 
484         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
485         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
486         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
487         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
488         assertEquals("text/xml", responseAttachment.getMimeType());
489 
490         Map<String, String> attHeaders = responseAttachment.getHeaders();
491         assertEquals(6, attHeaders.size());
492     }
493 
494     @Test
495     public void testInvalidXMLAttachmentContentEncryption() throws Exception {
496         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
497         WSSecHeader secHeader = new WSSecHeader(doc);
498         secHeader.insertSecurityHeader();
499 
500         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
501         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
502         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
503 
504         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
505         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
506 
507         String attachmentId = UUID.randomUUID().toString();
508         final Attachment attachment = new Attachment();
509         attachment.setMimeType("text/xml");
510         attachment.addHeaders(getHeaders(attachmentId));
511         attachment.setId(attachmentId);
512         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
513 
514         AttachmentCallbackHandler attachmentCallbackHandler =
515             new AttachmentCallbackHandler(Collections.singletonList(attachment));
516         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
517         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
518 
519         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
520         SecretKey symmetricKey = keyGen.generateKey();
521         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
522 
523         if (LOG.isDebugEnabled()) {
524             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
525             LOG.debug(outputString);
526         }
527 
528         final PushbackInputStream pis =
529             new PushbackInputStream(encryptedAttachments.get(0).getSourceStream(), 1);
530         pis.unread('K');
531         encryptedAttachments.get(0).setSourceStream(pis);
532 
533         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
534         verify(encryptedDoc, attachmentCallbackHandler);
535 
536         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
537         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
538         // Different behaviour here for different JDKs...
539         try {
540             byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
541             assertFalse(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
542             assertEquals("text/xml", responseAttachment.getMimeType());
543 
544             Map<String, String> attHeaders = responseAttachment.getHeaders();
545             assertEquals(6, attHeaders.size());
546         } catch (IOException ex) { //NOPMD
547             // expected
548         }
549     }
550 
551     @Test
552     public void testXMLAttachmentContentEncryptionExternalReferenceList() throws Exception {
553         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
554         WSSecHeader secHeader = new WSSecHeader(doc);
555         secHeader.insertSecurityHeader();
556 
557         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
558         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
559         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
560 
561         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
562         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
563 
564         String attachmentId = UUID.randomUUID().toString();
565         final Attachment attachment = new Attachment();
566         attachment.setMimeType("text/xml");
567         attachment.addHeaders(getHeaders(attachmentId));
568         attachment.setId(attachmentId);
569         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
570 
571         AttachmentCallbackHandler attachmentCallbackHandler =
572             new AttachmentCallbackHandler(Collections.singletonList(attachment));
573         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
574         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
575 
576         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
577         SecretKey symmetricKey = keyGen.generateKey();
578         encrypt.prepare(crypto, symmetricKey);
579         Element refs = encrypt.encrypt(symmetricKey);
580         encrypt.addAttachmentEncryptedDataElements();
581         encrypt.addExternalRefElement(refs);
582         encrypt.prependToHeader();
583 
584         if (LOG.isDebugEnabled()) {
585             String outputString = XMLUtils.prettyDocumentToString(doc);
586             LOG.debug(outputString);
587         }
588 
589         NodeList references = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "DataReference");
590         assertEquals(2, references.getLength());
591         NodeList cipherReferences = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "CipherReference");
592         assertEquals(1, cipherReferences.getLength());
593         NodeList encDatas = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "EncryptedData");
594         assertEquals(2, encDatas.getLength());
595 
596         NodeList securityHeaderElement = doc.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
597         assertEquals(1, securityHeaderElement.getLength());
598         NodeList childs = securityHeaderElement.item(0).getChildNodes();
599         assertEquals(3, childs.getLength());
600         assertEquals(childs.item(0).getLocalName(), "EncryptedKey");
601         assertEquals(childs.item(1).getLocalName(), "ReferenceList");
602         assertEquals(childs.item(2).getLocalName(), "EncryptedData");
603 
604         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
605         verify(doc, attachmentCallbackHandler);
606 
607         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
608         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
609 
610         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
611         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
612         assertEquals("text/xml", responseAttachment.getMimeType());
613 
614         Map<String, String> attHeaders = responseAttachment.getHeaders();
615         assertEquals(6, attHeaders.size());
616     }
617 
618     @Test
619     public void testXMLAttachmentContentEncryptionNoReference() throws Exception {
620         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
621         WSSecHeader secHeader = new WSSecHeader(doc);
622         secHeader.insertSecurityHeader();
623 
624         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
625         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
626         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
627 
628         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
629         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Content"));
630 
631         String attachmentId = UUID.randomUUID().toString();
632         final Attachment attachment = new Attachment();
633         attachment.setMimeType("text/xml");
634         attachment.addHeaders(getHeaders(attachmentId));
635         attachment.setId(attachmentId);
636         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
637 
638         AttachmentCallbackHandler attachmentCallbackHandler =
639             new AttachmentCallbackHandler(Collections.singletonList(attachment));
640         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
641         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
642 
643         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
644         SecretKey symmetricKey = keyGen.generateKey();
645         encrypt.prepare(crypto, symmetricKey);
646         encrypt.encrypt(symmetricKey);
647         encrypt.addAttachmentEncryptedDataElements();
648         //encrypt.addExternalRefElement(refs);
649         encrypt.prependToHeader();
650 
651         if (LOG.isDebugEnabled()) {
652             String outputString = XMLUtils.prettyDocumentToString(doc);
653             LOG.debug(outputString);
654         }
655 
656         NodeList references = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "DataReference");
657         assertEquals(0, references.getLength());
658         NodeList cipherReferences = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "CipherReference");
659         assertEquals(1, cipherReferences.getLength());
660         NodeList encDatas = doc.getElementsByTagNameNS(WSConstants.ENC_NS, "EncryptedData");
661         assertEquals(2, encDatas.getLength());
662 
663         NodeList securityHeaderElement = doc.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
664         assertEquals(1, securityHeaderElement.getLength());
665         NodeList childs = securityHeaderElement.item(0).getChildNodes();
666         assertEquals(2, childs.getLength());
667         assertEquals(childs.item(0).getLocalName(), "EncryptedKey");
668         assertEquals(childs.item(1).getLocalName(), "EncryptedData");
669 
670         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
671         verify(doc, attachmentCallbackHandler);
672 
673         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
674         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
675 
676         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
677         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
678         assertEquals("text/xml", responseAttachment.getMimeType());
679 
680         Map<String, String> attHeaders = responseAttachment.getHeaders();
681         assertEquals(6, attHeaders.size());
682     }
683 
684     @Test
685     public void testXMLAttachmentCompleteEncryption() throws Exception {
686         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
687         WSSecHeader secHeader = new WSSecHeader(doc);
688         secHeader.insertSecurityHeader();
689 
690         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
691         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
692         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
693 
694         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
695         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
696 
697         String attachmentId = UUID.randomUUID().toString();
698         final Attachment attachment = new Attachment();
699         attachment.setMimeType("text/xml");
700         attachment.addHeaders(getHeaders(attachmentId));
701         attachment.setId(attachmentId);
702         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
703 
704         AttachmentCallbackHandler attachmentCallbackHandler =
705             new AttachmentCallbackHandler(Collections.singletonList(attachment));
706         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
707         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
708 
709         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
710         SecretKey symmetricKey = keyGen.generateKey();
711         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
712 
713         assertEquals(1, encryptedAttachments.get(0).getHeaders().size());
714 
715         if (LOG.isDebugEnabled()) {
716             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
717             LOG.debug(outputString);
718         }
719 
720         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
721         verify(encryptedDoc, attachmentCallbackHandler);
722 
723         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
724         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
725 
726         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
727         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
728         assertEquals("text/xml", responseAttachment.getMimeType());
729 
730         Map<String, String> attHeaders = responseAttachment.getHeaders();
731         assertEquals(6, attHeaders.size());
732     }
733 
734     @Test
735     public void testInvalidXMLAttachmentCompleteEncryption() throws Exception {
736         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
737         WSSecHeader secHeader = new WSSecHeader(doc);
738         secHeader.insertSecurityHeader();
739 
740         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
741         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
742         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
743 
744         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
745         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
746 
747         String attachmentId = UUID.randomUUID().toString();
748         final Attachment attachment = new Attachment();
749         attachment.setMimeType("text/xml");
750         attachment.addHeaders(getHeaders(attachmentId));
751         attachment.setId(attachmentId);
752         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
753 
754         AttachmentCallbackHandler attachmentCallbackHandler =
755             new AttachmentCallbackHandler(Collections.singletonList(attachment));
756         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
757         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
758 
759         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
760         SecretKey symmetricKey = keyGen.generateKey();
761         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
762 
763         if (LOG.isDebugEnabled()) {
764             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
765             LOG.debug(outputString);
766         }
767 
768         try {
769             final PushbackInputStream pis =
770                 new PushbackInputStream(encryptedAttachments.get(0).getSourceStream(), 1);
771             pis.unread('K');
772             encryptedAttachments.get(0).setSourceStream(pis);
773 
774             attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
775             verify(encryptedDoc, attachmentCallbackHandler);
776         } catch (WSSecurityException e) {
777             // assertEquals(e.getMessage(), "The signature or decryption was invalid");
778             return;
779         }
780 
781         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
782         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
783         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
784         assertFalse(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
785         assertEquals("text/xml", responseAttachment.getMimeType());
786     }
787 
788     @Test
789     public void testMultipleAttachmentCompleteEncryption() throws Exception {
790         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
791         WSSecHeader secHeader = new WSSecHeader(doc);
792         secHeader.insertSecurityHeader();
793 
794         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
795         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
796         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
797 
798         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
799         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
800 
801         final String attachment1Id = UUID.randomUUID().toString();
802         final Attachment[] attachment = new Attachment[2];
803         attachment[0] = new Attachment();
804         attachment[0].setMimeType("text/xml");
805         attachment[0].addHeaders(getHeaders(attachment1Id));
806         attachment[0].setId(attachment1Id);
807         attachment[0].setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
808 
809         final String attachment2Id = UUID.randomUUID().toString();
810         attachment[1] = new Attachment();
811         attachment[1].setMimeType("text/plain");
812         attachment[1].addHeaders(getHeaders(attachment2Id));
813         attachment[1].setId(attachment2Id);
814         attachment[1].setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
815 
816         AttachmentCallbackHandler attachmentCallbackHandler =
817             new AttachmentCallbackHandler(Arrays.asList(attachment));
818         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
819         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
820 
821         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
822         SecretKey symmetricKey = keyGen.generateKey();
823         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
824 
825         if (LOG.isDebugEnabled()) {
826             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
827             LOG.debug(outputString);
828         }
829 
830         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
831         verify(encryptedDoc, attachmentCallbackHandler);
832 
833         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
834         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(0);
835 
836         byte[] attachment1Bytes = readInputStream(responseAttachment.getSourceStream());
837         assertTrue(Arrays.equals(attachment1Bytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
838         assertEquals("text/xml", responseAttachment.getMimeType());
839         Map<String, String> att1Headers = responseAttachment.getHeaders();
840         assertEquals(6, att1Headers.size());
841 
842         responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(1);
843         byte[] attachment2Bytes = readInputStream(responseAttachment.getSourceStream());
844         assertTrue(Arrays.equals(attachment2Bytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
845         assertEquals("text/plain", responseAttachment.getMimeType());
846 
847         Map<String, String> att2Headers = responseAttachment.getHeaders();
848         assertEquals(6, att2Headers.size());
849     }
850 
851     @Test
852     public void testXMLAttachmentCmplSignCmplEnc() throws Exception {
853         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
854 
855         WSSecHeader secHeader = new WSSecHeader(doc);
856         secHeader.insertSecurityHeader();
857 
858         WSSecSignature signature = new WSSecSignature(secHeader);
859         signature.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
860 
861         signature.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
862         signature.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
863 
864         String attachmentId = UUID.randomUUID().toString();
865         final Attachment attachment = new Attachment();
866         attachment.setMimeType("text/xml");
867         attachment.addHeaders(getHeaders(attachmentId));
868         attachment.setId(attachmentId);
869         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
870 
871         AttachmentCallbackHandler attachmentCallbackHandler =
872             new AttachmentCallbackHandler(Collections.singletonList(attachment));
873         signature.setAttachmentCallbackHandler(attachmentCallbackHandler);
874 
875         doc = signature.build(crypto);
876 
877         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
878         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
879         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
880         encrypt.getParts().addAll(signature.getParts());
881 
882         attachmentCallbackHandler =
883             new AttachmentCallbackHandler(Collections.singletonList(attachment));
884         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
885         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
886 
887         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
888         SecretKey symmetricKey = keyGen.generateKey();
889         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
890 
891         if (LOG.isDebugEnabled()) {
892             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
893             LOG.debug(outputString);
894         }
895 
896         NodeList securityHeaderElement = doc.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
897         assertEquals(1, securityHeaderElement.getLength());
898         NodeList childs = securityHeaderElement.item(0).getChildNodes();
899         assertEquals(3, childs.getLength());
900         assertEquals(childs.item(0).getLocalName(), "EncryptedKey");
901         assertEquals(childs.item(1).getLocalName(), "EncryptedData");
902         assertEquals(childs.item(2).getLocalName(), "Signature");
903 
904         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
905         verify(encryptedDoc, attachmentCallbackHandler);
906 
907         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
908         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(1);
909 
910         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
911         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
912         assertEquals("text/xml", responseAttachment.getMimeType());
913 
914         Map<String, String> attHeaders = responseAttachment.getHeaders();
915         assertEquals(6, attHeaders.size());
916     }
917 
918     @Test
919     public void testInvalidXMLAttachmentCmplSignCmplEnc() throws Exception {
920         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
921 
922         WSSecHeader secHeader = new WSSecHeader(doc);
923         secHeader.insertSecurityHeader();
924 
925         WSSecSignature signature = new WSSecSignature(secHeader);
926         signature.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
927 
928         signature.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
929         signature.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
930 
931         String attachmentId = UUID.randomUUID().toString();
932         final Attachment[] attachment = new Attachment[1];
933         attachment[0] = new Attachment();
934         attachment[0].setMimeType("text/xml");
935         attachment[0].addHeaders(getHeaders(attachmentId));
936         attachment[0].setId(attachmentId);
937         attachment[0].setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
938 
939         signature.setAttachmentCallbackHandler(new CallbackHandler() {
940             @Override
941             public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
942                 if (callbacks[0] instanceof AttachmentRequestCallback) {
943                     AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
944 
945                     List<Attachment> attachments = new ArrayList<>();
946                     attachments.add(attachment[0]);
947                     attachmentRequestCallback.setAttachments(attachments);
948                 } else {
949                     AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callbacks[0];
950                     attachment[0] = attachmentResultCallback.getAttachment();
951                 }
952             }
953         });
954 
955         doc = signature.build(crypto);
956 
957         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
958         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
959         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
960         encrypt.getParts().addAll(signature.getParts());
961 
962         encrypt.setAttachmentCallbackHandler(new CallbackHandler() {
963             @Override
964             public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
965                 if (callbacks[0] instanceof AttachmentRequestCallback) {
966                     AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
967 
968                     List<Attachment> attachments = new ArrayList<>();
969                     attachments.add(attachment[0]);
970                     attachmentRequestCallback.setAttachments(attachments);
971                 } else {
972                     AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callbacks[0];
973                     attachment[0] = attachmentResultCallback.getAttachment();
974                 }
975             }
976         });
977 
978         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
979         SecretKey symmetricKey = keyGen.generateKey();
980         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
981 
982         if (LOG.isDebugEnabled()) {
983             String outputString = XMLUtils.prettyDocumentToString(encryptedDoc);
984             LOG.debug(outputString);
985         }
986 
987         try {
988             verify(encryptedDoc, new CallbackHandler() {
989                 @Override
990                 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
991                     if (callbacks[0] instanceof AttachmentRequestCallback) {
992                         AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
993 
994                         if (!attachment[0].getId().equals(attachmentRequestCallback.getAttachmentId())) {
995                             throw new RuntimeException("wrong attachment requested");
996                         }
997 
998                         List<Attachment> attachments = new ArrayList<>();
999                         attachments.add(attachment[0]);
1000 
1001                         if (attachment[0].getHeaders().size() == 6) {
1002                             //signature callback
1003                             attachment[0].addHeader(AttachmentUtils.MIME_HEADER_CONTENT_DESCRIPTION, "Kaputt");
1004                         }
1005 
1006                         attachmentRequestCallback.setAttachments(attachments);
1007                     } else {
1008                         AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callbacks[0];
1009                         attachment[0] = attachmentResultCallback.getAttachment();
1010                     }
1011                 }
1012             });
1013             fail();
1014         } catch (WSSecurityException e) {
1015             assertEquals(e.getMessage(), "The signature or decryption was invalid");
1016         }
1017     }
1018 
1019     @Test
1020     public void testXMLAttachmentCmplEncCmplSign() throws Exception {
1021         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
1022 
1023         WSSecHeader secHeader = new WSSecHeader(doc);
1024         secHeader.insertSecurityHeader();
1025 
1026         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
1027         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
1028         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
1029 
1030         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
1031         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
1032 
1033         String attachmentId = UUID.randomUUID().toString();
1034         final Attachment attachment = new Attachment();
1035         attachment.setMimeType("text/xml");
1036         attachment.addHeaders(getHeaders(attachmentId));
1037         attachment.setId(attachmentId);
1038         attachment.setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
1039 
1040         AttachmentCallbackHandler attachmentCallbackHandler =
1041             new AttachmentCallbackHandler(Collections.singletonList(attachment));
1042         encrypt.setAttachmentCallbackHandler(attachmentCallbackHandler);
1043         List<Attachment> encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
1044 
1045         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
1046         SecretKey symmetricKey = keyGen.generateKey();
1047         doc = encrypt.build(crypto, symmetricKey);
1048 
1049         WSSecSignature signature = new WSSecSignature(secHeader);
1050         signature.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
1051         signature.getParts().addAll(encrypt.getParts());
1052 
1053         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
1054         signature.setAttachmentCallbackHandler(attachmentCallbackHandler);
1055         encryptedAttachments = attachmentCallbackHandler.getResponseAttachments();
1056 
1057         doc = signature.build(crypto);
1058 
1059         if (LOG.isDebugEnabled()) {
1060             String outputString = XMLUtils.prettyDocumentToString(doc);
1061             LOG.debug(outputString);
1062         }
1063 
1064         NodeList securityHeaderElement = doc.getElementsByTagNameNS(WSConstants.WSSE_NS, "Security");
1065         assertEquals(1, securityHeaderElement.getLength());
1066         NodeList childs = securityHeaderElement.item(0).getChildNodes();
1067         assertEquals(3, childs.getLength());
1068         assertEquals(childs.item(0).getLocalName(), "Signature");
1069         assertEquals(childs.item(1).getLocalName(), "EncryptedKey");
1070         assertEquals(childs.item(2).getLocalName(), "EncryptedData");
1071 
1072         attachmentCallbackHandler = new AttachmentCallbackHandler(encryptedAttachments);
1073         verify(doc, attachmentCallbackHandler);
1074 
1075         assertFalse(attachmentCallbackHandler.getResponseAttachments().isEmpty());
1076         Attachment responseAttachment = attachmentCallbackHandler.getResponseAttachments().get(1);
1077 
1078         byte[] attachmentBytes = readInputStream(responseAttachment.getSourceStream());
1079         assertTrue(Arrays.equals(attachmentBytes, SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
1080         assertEquals("text/xml", responseAttachment.getMimeType());
1081 
1082         Map<String, String> attHeaders = responseAttachment.getHeaders();
1083         assertEquals(6, attHeaders.size());
1084     }
1085 
1086     @Test
1087     public void testInvalidXMLAttachmentCmplEncCmplSign() throws Exception {
1088         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
1089 
1090         WSSecHeader secHeader = new WSSecHeader(doc);
1091         secHeader.insertSecurityHeader();
1092 
1093         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
1094         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
1095         encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
1096 
1097         encrypt.getParts().add(new WSEncryptionPart("Body", "http://schemas.xmlsoap.org/soap/envelope/", "Content"));
1098         encrypt.getParts().add(new WSEncryptionPart("cid:Attachments", "Element"));
1099 
1100         String attachmentId = UUID.randomUUID().toString();
1101         final Attachment[] attachment = new Attachment[1];
1102         attachment[0] = new Attachment();
1103         attachment[0].setMimeType("text/xml");
1104         attachment[0].addHeaders(getHeaders(attachmentId));
1105         attachment[0].setId(attachmentId);
1106         attachment[0].setSourceStream(new ByteArrayInputStream(SOAPUtil.SAMPLE_SOAP_MSG.getBytes(StandardCharsets.UTF_8)));
1107 
1108         encrypt.setAttachmentCallbackHandler(new CallbackHandler() {
1109             @Override
1110             public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
1111                 if (callbacks[0] instanceof AttachmentRequestCallback) {
1112                     AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
1113 
1114                     List<Attachment> attachments = new ArrayList<>();
1115                     attachments.add(attachment[0]);
1116                     attachmentRequestCallback.setAttachments(attachments);
1117                 } else {
1118                     AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callbacks[0];
1119                     attachment[0] = attachmentResultCallback.getAttachment();
1120                 }
1121             }
1122         });
1123 
1124         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
1125         SecretKey symmetricKey = keyGen.generateKey();
1126         doc = encrypt.build(crypto, symmetricKey);
1127 
1128         WSSecSignature signature = new WSSecSignature(secHeader);
1129         signature.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
1130         signature.getParts().addAll(encrypt.getParts());
1131 
1132         signature.setAttachmentCallbackHandler(new CallbackHandler() {
1133             @Override
1134             public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
1135                 if (callbacks[0] instanceof AttachmentRequestCallback) {
1136                     AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
1137 
1138                     List<Attachment> attachments = new ArrayList<>();
1139                     attachments.add(attachment[0]);
1140                     attachmentRequestCallback.setAttachments(attachments);
1141                 } else {
1142                     AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callbacks[0];
1143                     attachment[0] = attachmentResultCallback.getAttachment();
1144                 }
1145             }
1146         });
1147 
1148         doc = signature.build(crypto);
1149 
1150         if (LOG.isDebugEnabled()) {
1151             String outputString = XMLUtils.prettyDocumentToString(doc);
1152             LOG.debug(outputString);
1153         }
1154 
1155         final PushbackInputStream pis = new PushbackInputStream(attachment[0].getSourceStream(), 1);
1156         pis.unread('K');
1157         attachment[0].setSourceStream(pis);
1158         try {
1159             verify(doc, new CallbackHandler() {
1160                 @Override
1161                 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
1162                     if (callbacks[0] instanceof AttachmentRequestCallback) {
1163                         AttachmentRequestCallback attachmentRequestCallback = (AttachmentRequestCallback) callbacks[0];
1164 
1165                         if (!attachment[0].getId().equals(attachmentRequestCallback.getAttachmentId())) {
1166                             throw new RuntimeException("wrong attachment requested");
1167                         }
1168 
1169                         List<Attachment> attachmentList = new ArrayList<>();
1170                         attachmentList.add(attachment[0]);
1171                         attachmentRequestCallback.setAttachments(attachmentList);
1172 
1173                     } else {
1174                         AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callbacks[0];
1175                         attachment[0] = attachmentResultCallback.getAttachment();
1176                     }
1177                 }
1178             });
1179             fail();
1180         } catch (WSSecurityException e) {
1181             assertEquals(e.getMessage(), "The signature or decryption was invalid");
1182         }
1183     }
1184 
1185     /**
1186      * Verifies the soap envelope.
1187      * This method verifies all the signature generated.
1188      *
1189      * @throws Exception Thrown when there is a problem in verification
1190      */
1191     private WSHandlerResult verify(Document doc, CallbackHandler attachmentCallbackHandler) throws Exception {
1192         RequestData requestData = new RequestData();
1193         requestData.setAttachmentCallbackHandler(attachmentCallbackHandler);
1194         requestData.setSigVerCrypto(crypto);
1195         requestData.setDecCrypto(crypto);
1196         requestData.setCallbackHandler(new KeystoreCallbackHandler());
1197         return secEngine.processSecurityHeader(doc, requestData);
1198     }
1199 }