View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.dom.message;
21  
22  import java.text.MessageFormat;
23  import java.util.Map;
24  import java.util.ResourceBundle;
25  import java.util.TreeMap;
26  
27  import javax.crypto.KeyGenerator;
28  import javax.crypto.SecretKey;
29  import javax.security.auth.callback.CallbackHandler;
30  
31  import org.apache.wss4j.common.util.SOAPUtil;
32  import org.apache.wss4j.dom.WSConstants;
33  import org.apache.wss4j.dom.common.CustomHandler;
34  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
35  
36  import org.apache.wss4j.dom.engine.WSSConfig;
37  import org.apache.wss4j.dom.engine.WSSecurityEngine;
38  import org.apache.wss4j.common.crypto.Crypto;
39  import org.apache.wss4j.common.crypto.CryptoFactory;
40  import org.apache.wss4j.common.ext.WSSecurityException;
41  import org.apache.wss4j.common.util.KeyUtils;
42  import org.apache.wss4j.common.util.XMLUtils;
43  import org.apache.wss4j.dom.handler.RequestData;
44  import org.apache.wss4j.dom.handler.WSHandlerConstants;
45  import org.apache.wss4j.dom.handler.WSHandlerResult;
46  import org.apache.wss4j.dom.util.WSSecurityUtil;
47  
48  import org.junit.jupiter.api.Test;
49  import org.w3c.dom.Document;
50  import org.w3c.dom.Element;
51  
52  import static org.junit.jupiter.api.Assertions.assertEquals;
53  import static org.junit.jupiter.api.Assertions.assertTrue;
54  import static org.junit.jupiter.api.Assertions.fail;
55  
56  /**
57   * Tests for the WSHandlerConstants.REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS option.
58   * This test verifies some wrapping techniques are properly handled when the afore
59   * mentioned option is on.
60   */
61  public class RequireSignedEncryptedDataElementsTest {
62      private static final org.slf4j.Logger LOG =
63          org.slf4j.LoggerFactory.getLogger(RequireSignedEncryptedDataElementsTest.class);
64      private static ResourceBundle resources = ResourceBundle.getBundle("messages.wss4j_errors");
65      private static final String SOAPMSG =
66          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
67          + "<SOAP-ENV:Envelope "
68          +   "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
69          +   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
70          +   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
71          +   "<SOAP-ENV:Body>"
72          +       "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
73          +           "<value xmlns=\"http://blah.com\">15</value>"
74          +       "</add>"
75          +   "</SOAP-ENV:Body>"
76          + "</SOAP-ENV:Envelope>";
77      private WSSecurityEngine secEngine = new WSSecurityEngine();
78      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
79      private Crypto crypto;
80  
81      public RequireSignedEncryptedDataElementsTest() throws Exception {
82          crypto = CryptoFactory.getInstance();
83          WSSConfig.init();
84      }
85  
86      @Test
87      public void testEncryptedKeyRefAndDuplicatedEncDataInWsseHeader() throws Exception {
88          Document encryptedSignedDoc = getRequestDocument();
89          RequestData reqData = getRequestData(true);
90          verify(encryptedSignedDoc, reqData);
91  
92          encryptedSignedDoc = getRequestDocument();
93          reqData = getRequestData(false);
94          TestMessageTransformer.duplicateEncryptedDataInWsseHeader(encryptedSignedDoc.getDocumentElement(), false);
95          verify(encryptedSignedDoc, reqData);
96  
97          encryptedSignedDoc = getRequestDocument();
98          reqData = getRequestData(true);
99          Element newEncData = TestMessageTransformer.duplicateEncryptedDataInWsseHeader(encryptedSignedDoc.getDocumentElement(), false);
100         try {
101             verify(encryptedSignedDoc, reqData);
102             fail("WSSecurityException expected");
103         } catch (WSSecurityException e) {
104             checkFailure(newEncData, e);
105         }
106     }
107 
108     @Test
109     public void testEncryptedKeyRefAndDuplicatedEncDataInWsseWrapperHeader() throws Exception {
110         Document encryptedSignedDoc = getRequestDocument();
111         RequestData reqData = getRequestData(true);
112         verify(encryptedSignedDoc, reqData);
113 
114         encryptedSignedDoc = getRequestDocument();
115         reqData = getRequestData(false);
116         TestMessageTransformer.duplicateEncryptedDataInWsseWrapperHeader(encryptedSignedDoc.getDocumentElement(), false);
117         verify(encryptedSignedDoc, reqData);
118 
119         encryptedSignedDoc = getRequestDocument();
120         reqData = getRequestData(true);
121         Element newEncData = TestMessageTransformer.duplicateEncryptedDataInWsseWrapperHeader(encryptedSignedDoc.getDocumentElement(), false);
122         try {
123             verify(encryptedSignedDoc, reqData);
124             fail("WSSecurityException expected");
125         } catch (WSSecurityException e) {
126             checkFailure(newEncData, e);
127         }
128     }
129 
130     @Test
131     public void testEncryptedKeyRefAndDuplicatedEncDataInExternalWrapperElement() throws Exception {
132         Document encryptedSignedDoc = getRequestDocument();
133         RequestData reqData = getRequestData(true);
134         verify(encryptedSignedDoc, reqData);
135 
136         encryptedSignedDoc = getRequestDocument();
137         reqData = getRequestData(false);
138         TestMessageTransformer.duplicateEncryptedDataInExternalWrapperElement(encryptedSignedDoc.getDocumentElement(), false);
139         verify(encryptedSignedDoc, reqData);
140 
141         encryptedSignedDoc = getRequestDocument();
142         reqData = getRequestData(true);
143         Element newEncData = TestMessageTransformer.duplicateEncryptedDataInExternalWrapperElement(encryptedSignedDoc.getDocumentElement(), false);
144         try {
145             verify(encryptedSignedDoc, reqData);
146             fail("WSSecurityException expected");
147         } catch (WSSecurityException e) {
148             checkFailure(newEncData, e);
149         }
150     }
151 
152     @Test
153     public void testReferenceListAndDuplicatedEncDataInWsseHeader() throws Exception {
154         Document encryptedSignedDoc = getRequestDocument();
155         RequestData reqData = getRequestData(true);
156         verify(encryptedSignedDoc, reqData);
157 
158         encryptedSignedDoc = getRequestDocument();
159         reqData = getRequestData(false);
160         TestMessageTransformer.duplicateEncryptedDataInWsseHeader(encryptedSignedDoc.getDocumentElement(), true);
161         verify(encryptedSignedDoc, reqData);
162 
163         encryptedSignedDoc = getRequestDocument();
164         reqData = getRequestData(true);
165         Element newEncData = TestMessageTransformer.duplicateEncryptedDataInWsseHeader(encryptedSignedDoc.getDocumentElement(), true);
166         try {
167             verify(encryptedSignedDoc, reqData);
168             fail("WSSecurityException expected");
169         } catch (WSSecurityException e) {
170             checkFailure(newEncData, e);
171         }
172     }
173 
174     @Test
175     public void testReferenceListAndDuplicatedEncDataInWsseWrapperHeader() throws Exception {
176         Document encryptedSignedDoc = getRequestDocument();
177         RequestData reqData = getRequestData(true);
178         verify(encryptedSignedDoc, reqData);
179 
180         encryptedSignedDoc = getRequestDocument();
181         reqData = getRequestData(false);
182         TestMessageTransformer.duplicateEncryptedDataInWsseWrapperHeader(encryptedSignedDoc.getDocumentElement(), true);
183         verify(encryptedSignedDoc, reqData);
184 
185         encryptedSignedDoc = getRequestDocument();
186         reqData = getRequestData(true);
187         Element newEncData = TestMessageTransformer.duplicateEncryptedDataInWsseWrapperHeader(encryptedSignedDoc.getDocumentElement(), true);
188         try {
189             verify(encryptedSignedDoc, reqData);
190             fail("WSSecurityException expected");
191         } catch (WSSecurityException e) {
192             checkFailure(newEncData, e);
193         }
194     }
195 
196     @Test
197     public void testReferenceListAndDuplicatedEncDataInExternalWrapperElement() throws Exception {
198         Document encryptedSignedDoc = getRequestDocument();
199         RequestData reqData = getRequestData(true);
200         verify(encryptedSignedDoc, reqData);
201 
202         encryptedSignedDoc = getRequestDocument();
203         reqData = getRequestData(false);
204         TestMessageTransformer.duplicateEncryptedDataInExternalWrapperElement(encryptedSignedDoc.getDocumentElement(), true);
205         verify(encryptedSignedDoc, reqData);
206 
207         encryptedSignedDoc = getRequestDocument();
208         reqData = getRequestData(true);
209         Element newEncData = TestMessageTransformer.duplicateEncryptedDataInExternalWrapperElement(encryptedSignedDoc.getDocumentElement(), true);
210         try {
211             verify(encryptedSignedDoc, reqData);
212             fail("WSSecurityException expected");
213         } catch (WSSecurityException e) {
214             checkFailure(newEncData, e);
215         }
216     }
217 
218     @Test
219     public void testAdditionalEncryptedDataWithEmbeddedEncryptedKeyInWsseHeader() throws Exception {
220         Document encryptedSignedDoc = getRequestDocument();
221         RequestData reqData = getRequestData(true);
222         verify(encryptedSignedDoc, reqData);
223 
224         encryptedSignedDoc = getRequestDocument();
225         reqData = getRequestData(true);
226         Element newEncData = TestMessageTransformer.addEncryptedDataWithEmbeddedEncryptedKeyInWsseHeader(encryptedSignedDoc.getDocumentElement());
227         try {
228             verify(encryptedSignedDoc, reqData);
229             fail("WSSecurityException expected");
230         } catch (WSSecurityException e) {
231             checkFailure(newEncData, e);
232         }
233     }
234 
235     @Test
236     public void testEncryptedKeyRefAndDuplicatedEncDataInWsseWrapperBody() throws Exception {
237         Document encryptedSignedDoc = getRequestDocumentEncryptionFirst();
238         RequestData reqData = getRequestData(false);
239         TestMessageTransformer.duplicateEncryptedDataInWrapperBody(encryptedSignedDoc.getDocumentElement());
240         try {
241             verify(encryptedSignedDoc, reqData);
242             fail("WSSecurityException expected");
243         } catch (WSSecurityException e) {
244             assertTrue(e.getMessage().contains("The signature or decryption was invalid"));
245         }
246 
247         encryptedSignedDoc = getRequestDocumentEncryptionFirst();
248         TestMessageTransformer.duplicateEncryptedDataInWrapperBody(encryptedSignedDoc.getDocumentElement());
249         reqData = getRequestData(true);
250 
251         try {
252             verify(encryptedSignedDoc, reqData);
253             fail("WSSecurityException expected");
254         } catch (WSSecurityException e) {
255             assertTrue(e.getMessage().contains("is not signed"));
256         }
257     }
258 
259     @Test
260     public void testEncryptedKeyRefAndDuplicatedEncDataAfterWsseWrapperBody() throws Exception {
261         Document encryptedSignedDoc = getRequestDocumentEncryptionFirst();
262         TestMessageTransformer.duplicateEncryptedDataAfterWrapperBody(encryptedSignedDoc.getDocumentElement());
263 
264         RequestData reqData = getRequestData(true);
265         try {
266             verify(encryptedSignedDoc, reqData);
267             fail("WSSecurityException expected");
268         } catch (WSSecurityException e) {
269             assertTrue(e.getMessage().contains("is not signed"));
270         }
271     }
272 
273     private static void checkFailure(Element attackElement, WSSecurityException e) {
274         final String mex = MessageFormat.format(resources.getString("elementNotSigned"), attackElement);
275         assertTrue(e.getMessage().contains(mex));
276         assertEquals(WSSecurityException.ErrorCode.FAILED_CHECK, e.getErrorCode());
277     }
278 
279     private RequestData getRequestData(boolean reqSignedEncData) throws WSSecurityException {
280         RequestData reqData = new RequestData();
281         Map<String, Object> messageContext = new TreeMap<>();
282         messageContext = new TreeMap<>();
283         messageContext.put(WSHandlerConstants.REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS, Boolean.toString(reqSignedEncData));
284         reqData.setMsgContext(messageContext);
285         CustomHandler handler = new CustomHandler();
286         handler.receive(WSSecurityUtil.decodeAction("Encrypt Signature"), reqData);
287         reqData.setCallbackHandler(callbackHandler);
288         reqData.setSigVerCrypto(crypto);
289         reqData.setDecCrypto(crypto);
290         return reqData;
291     }
292 
293     private Document getRequestDocument() throws Exception {
294         Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
295         WSSecHeader secHeader = new WSSecHeader(doc);
296         secHeader.insertSecurityHeader();
297 
298         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
299         WSSecSignature sign = new WSSecSignature(secHeader);
300         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e");
301         sign.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
302         LOG.info("Before Encryption....");
303 
304         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
305         SecretKey symmetricKey = keyGen.generateKey();
306         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
307 
308         if (LOG.isDebugEnabled()) {
309             LOG.debug("After Encryption....");
310             String outputString =
311                 XMLUtils.prettyDocumentToString(encryptedDoc);
312             LOG.debug(outputString);
313         }
314 
315         Document encryptedSignedDoc = sign.build(crypto);
316 
317         if (LOG.isDebugEnabled()) {
318             LOG.debug("After Signing....");
319             String outputString =
320                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
321             LOG.debug(outputString);
322         }
323         return encryptedSignedDoc;
324     }
325 
326     private Document getRequestDocumentEncryptionFirst() throws Exception {
327         Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
328         WSSecHeader secHeader = new WSSecHeader(doc);
329         secHeader.insertSecurityHeader();
330 
331         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
332         WSSecSignature sign = new WSSecSignature(secHeader);
333         encrypt.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e");
334         sign.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
335         LOG.info("Before Encryption....");
336 
337         Document signedDoc = sign.build(crypto);
338 
339         if (LOG.isDebugEnabled()) {
340             LOG.debug("After Signing....");
341             String outputString =
342                 XMLUtils.prettyDocumentToString(signedDoc);
343             LOG.debug(outputString);
344         }
345 
346         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
347         SecretKey symmetricKey = keyGen.generateKey();
348         Document encryptedDoc = encrypt.build(crypto, symmetricKey);
349 
350         if (LOG.isDebugEnabled()) {
351             LOG.debug("After Encryption....");
352             String outputString =
353                 XMLUtils.prettyDocumentToString(encryptedDoc);
354             LOG.debug(outputString);
355         }
356 
357         return encryptedDoc;
358     }
359 
360 
361     private WSHandlerResult verify(Document doc, RequestData reqData) throws Exception {
362         Element elem = WSSecurityUtil.getSecurityHeader(doc, null);
363         WSHandlerResult resultList =
364             secEngine.processSecurityHeader(elem, reqData);
365         if (LOG.isDebugEnabled()) {
366             String outputString =
367                 XMLUtils.prettyDocumentToString(doc);
368             LOG.debug(outputString);
369         }
370 
371         return resultList;
372     }
373 
374 }