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.handler;
21  
22  import java.util.*;
23  
24  import javax.security.auth.callback.CallbackHandler;
25  
26  import org.apache.wss4j.common.bsp.BSPRule;
27  import org.apache.wss4j.common.crypto.Crypto;
28  import org.apache.wss4j.common.crypto.CryptoFactory;
29  import org.apache.wss4j.common.ext.WSSecurityException;
30  import org.apache.wss4j.common.saml.builder.SAML2Constants;
31  import org.apache.wss4j.common.util.SOAPUtil;
32  import org.apache.wss4j.common.util.XMLUtils;
33  import org.apache.wss4j.dom.WSConstants;
34  import org.apache.wss4j.dom.common.CustomHandler;
35  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
36  import org.apache.wss4j.dom.common.SAML2CallbackHandler;
37  
38  import org.apache.wss4j.dom.engine.WSSecurityEngine;
39  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
40  import org.apache.wss4j.dom.message.WSSecHeader;
41  import org.apache.wss4j.dom.message.token.SignatureConfirmation;
42  
43  import org.junit.jupiter.api.Test;
44  import org.w3c.dom.Document;
45  import org.w3c.dom.Element;
46  
47  import static org.junit.jupiter.api.Assertions.assertFalse;
48  import static org.junit.jupiter.api.Assertions.assertNotNull;
49  import static org.junit.jupiter.api.Assertions.assertNull;
50  import static org.junit.jupiter.api.Assertions.assertTrue;
51  import static org.junit.jupiter.api.Assertions.fail;
52  
53  /**
54   * A set of test-cases for SignatureConfirmation.
55   */
56  public class SignatureConfirmationTest {
57      private static final org.slf4j.Logger LOG =
58          org.slf4j.LoggerFactory.getLogger(SignatureConfirmationTest.class);
59      private WSSecurityEngine secEngine = new WSSecurityEngine();
60      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
61      private Crypto crypto;
62  
63      public SignatureConfirmationTest() throws Exception {
64          crypto = CryptoFactory.getInstance();
65      }
66  
67      /**
68       * Test to see that a signature is saved correctly on the outbound request.
69       */
70      @SuppressWarnings("unchecked")
71      @Test
72      public void
73      testRequestSavedSignature() throws Exception {
74          final RequestData reqData = new RequestData();
75          java.util.Map<String, Object> msgContext = new java.util.TreeMap<>();
76          msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true");
77          msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
78          msgContext.put("password", "security");
79          reqData.setMsgContext(msgContext);
80          reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
81  
82          final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
83          CustomHandler handler = new CustomHandler();
84          HandlerAction action = new HandlerAction(WSConstants.SIGN);
85          handler.send(
86              doc,
87              reqData,
88              Collections.singletonList(action),
89              true
90          );
91          if (LOG.isDebugEnabled()) {
92              LOG.debug("After Signing....");
93              String outputString =
94                  XMLUtils.prettyDocumentToString(doc);
95              LOG.debug(outputString);
96          }
97  
98          msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
99          Set<Integer> savedSignatures =
100             (Set<Integer>)msgContext.get(WSHandlerConstants.SEND_SIGV);
101         assertTrue(savedSignatures != null && savedSignatures.size() == 1);
102         Integer signatureValue = savedSignatures.iterator().next();
103         assertTrue(signatureValue != null && signatureValue != 0);
104     }
105 
106 
107     /**
108      * Test to see that a signature is not saved on the outbound request if
109      * enable signature confirmation is false.
110      */
111     @SuppressWarnings("unchecked")
112     @Test
113     public void
114     testRequestNotSavedSignature() throws Exception {
115         final RequestData reqData = new RequestData();
116         java.util.Map<String, Object> msgContext = new java.util.TreeMap<>();
117         msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "false");
118         msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
119         msgContext.put("password", "security");
120         reqData.setMsgContext(msgContext);
121         reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
122 
123         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
124         CustomHandler handler = new CustomHandler();
125         HandlerAction action = new HandlerAction(WSConstants.SIGN);
126         handler.send(
127             doc,
128             reqData,
129             Collections.singletonList(action),
130             true
131         );
132         if (LOG.isDebugEnabled()) {
133             LOG.debug("After Signing....");
134             String outputString =
135                 XMLUtils.prettyDocumentToString(doc);
136             LOG.debug(outputString);
137         }
138 
139         msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
140         Set<Integer> savedSignatures =
141             (Set<Integer>)msgContext.get(WSHandlerConstants.SEND_SIGV);
142         assertNull(savedSignatures);
143     }
144 
145 
146     /**
147      * Test to see that a signature confirmation response is correctly sent on receiving
148      * a signed message.
149      */
150     @SuppressWarnings("unchecked")
151     @Test
152     public void
153     testSignatureConfirmationResponse() throws Exception {
154         final RequestData reqData = new RequestData();
155         java.util.Map<String, Object> msgContext = new java.util.TreeMap<>();
156         msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true");
157         msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
158         msgContext.put("password", "security");
159         reqData.setMsgContext(msgContext);
160         reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
161 
162         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
163         CustomHandler handler = new CustomHandler();
164         HandlerAction action = new HandlerAction(WSConstants.SIGN);
165         handler.send(
166             doc,
167             reqData,
168             Collections.singletonList(action),
169             true
170         );
171         if (LOG.isDebugEnabled()) {
172             LOG.debug("After Signing....");
173             String outputString =
174                 XMLUtils.prettyDocumentToString(doc);
175             LOG.debug(outputString);
176         }
177 
178         msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
179         Set<Integer> savedSignatures =
180             (Set<Integer>)msgContext.get(WSHandlerConstants.SEND_SIGV);
181         assertTrue(savedSignatures != null && savedSignatures.size() == 1);
182         Integer signatureValue = savedSignatures.iterator().next();
183         assertTrue(signatureValue != null && signatureValue != 0);
184 
185         //
186         // Verify the inbound request, and create a response with a Signature Confirmation
187         //
188         WSHandlerResult results = verify(doc);
189         doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
190         msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
191         List<WSHandlerResult> receivedResults = new ArrayList<>();
192         receivedResults.add(results);
193         msgContext.put(WSHandlerConstants.RECV_RESULTS, receivedResults);
194         action = new HandlerAction(WSConstants.NO_SECURITY);
195         handler.send(
196             doc,
197             reqData,
198             Collections.singletonList(action),
199             false
200         );
201         String outputString =
202             XMLUtils.prettyDocumentToString(doc);
203         if (LOG.isDebugEnabled()) {
204             LOG.debug("Signature Confirmation response....");
205             LOG.debug(outputString);
206         }
207         assertTrue(outputString.contains("SignatureConfirmation"));
208         assertTrue(outputString.contains("Value"));
209     }
210 
211 
212     /**
213      * Test to see that a signature confirmation response is correctly processed.
214      */
215     @SuppressWarnings("unchecked")
216     @Test
217     public void
218     testSignatureConfirmationProcessing() throws Exception {
219         final RequestData reqData = new RequestData();
220         java.util.Map<String, Object> msgContext = new java.util.TreeMap<>();
221         msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true");
222         msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
223         msgContext.put("password", "security");
224         reqData.setMsgContext(msgContext);
225         reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
226 
227         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
228         CustomHandler handler = new CustomHandler();
229         HandlerAction action = new HandlerAction(WSConstants.SIGN);
230         handler.send(
231             doc,
232             reqData,
233             Collections.singletonList(action),
234             true
235         );
236         if (LOG.isDebugEnabled()) {
237             LOG.debug("After Signing....");
238             String outputString =
239                 XMLUtils.prettyDocumentToString(doc);
240             LOG.debug(outputString);
241         }
242 
243         //
244         // Verify the inbound request, and create a response with a Signature Confirmation
245         //
246         WSHandlerResult results = verify(doc);
247         doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
248         msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
249         List<WSHandlerResult> receivedResults = new ArrayList<>();
250         receivedResults.add(results);
251         msgContext.put(WSHandlerConstants.RECV_RESULTS, receivedResults);
252         handler.send(
253             doc,
254             reqData,
255             Collections.singletonList(action),
256             false
257         );
258         String outputString =
259             XMLUtils.prettyDocumentToString(doc);
260         if (LOG.isDebugEnabled()) {
261             LOG.debug("Signature Confirmation response....");
262             LOG.debug(outputString);
263         }
264 
265         //
266         // Verify the SignatureConfirmation response
267         //
268         results = verify(doc);
269         WSSecurityEngineResult scResult =
270             results.getActionResults().get(WSConstants.SC).get(0);
271         assertNotNull(scResult);
272         assertNotNull(scResult.get(WSSecurityEngineResult.TAG_SIGNATURE_CONFIRMATION));
273         handler.signatureConfirmation(reqData, results);
274     }
275 
276 
277     /**
278      * Test to see that a signature confirmation response that does not contain a wsu:Id fails
279      * the BSP compliance is enabled.
280      */
281     @Test
282     public void testWsuId() throws Exception {
283         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
284         WSSecHeader secHeader = new WSSecHeader(doc);
285         secHeader.insertSecurityHeader();
286 
287         Random random = new Random();
288         byte[] randomBytes = new byte[20];
289         random.nextBytes(randomBytes);
290         SignatureConfirmation sigConf = new SignatureConfirmation(doc, randomBytes);
291         Element sigConfElement = sigConf.getElement();
292         secHeader.getSecurityHeaderElement().appendChild(sigConfElement);
293 
294         if (LOG.isDebugEnabled()) {
295             String outputString =
296                 XMLUtils.prettyDocumentToString(doc);
297             LOG.debug(outputString);
298         }
299 
300         // Verify the results
301         WSSecurityEngine newEngine = new WSSecurityEngine();
302         try {
303             newEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
304             fail("Failure expected on a request with no wsu:Id");
305         } catch (WSSecurityException ex) {
306             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
307         }
308 
309         RequestData data = new RequestData();
310         data.setCallbackHandler(callbackHandler);
311         data.setSigVerCrypto(crypto);
312         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R5441));
313         newEngine.processSecurityHeader(doc, data);
314     }
315 
316     @SuppressWarnings("unchecked")
317     @Test
318     public void testSignatureResponseForUnsignedRequest() throws Exception {
319         final RequestData reqData = new RequestData();
320         java.util.Map<String, Object> msgContext = new java.util.TreeMap<>();
321         msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true");
322         msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "crypto.properties");
323         msgContext.put("password", "security");
324         reqData.setMsgContext(msgContext);
325         reqData.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e");
326 
327         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
328         CustomHandler handler = new CustomHandler();
329         HandlerAction action = new HandlerAction(WSConstants.NO_SECURITY);
330         handler.send(
331             doc,
332             reqData,
333             Collections.singletonList(action),
334             true
335         );
336         if (LOG.isDebugEnabled()) {
337             LOG.debug("After Signing....");
338             String outputString =
339                 XMLUtils.prettyDocumentToString(doc);
340             LOG.debug(outputString);
341         }
342 
343         //
344         // Verify the inbound request, and create a response with a Signature Confirmation
345         //
346         WSHandlerResult results = verify(doc);
347         doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
348         msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
349         List<WSHandlerResult> receivedResults = new ArrayList<>();
350         receivedResults.add(results);
351         msgContext.put(WSHandlerConstants.RECV_RESULTS, receivedResults);
352         action = new HandlerAction(WSConstants.NO_SECURITY);
353         handler.send(
354             doc,
355             reqData,
356             Collections.singletonList(action),
357             false
358         );
359         String outputString =
360             XMLUtils.prettyDocumentToString(doc);
361         if (LOG.isDebugEnabled()) {
362             LOG.debug("Signature Confirmation response....");
363             LOG.debug(outputString);
364         }
365         assertTrue(outputString.contains("SignatureConfirmation"));
366         // We should have no Value attribute
367         assertFalse(outputString.contains("Value"));
368     }
369 
370     @SuppressWarnings("unchecked")
371     @Test
372     public void
373     testSAMLSignatureConfirmationProcessing() throws Exception {
374         final RequestData reqData = new RequestData();
375 
376         SAML2CallbackHandler samlCallbackHandler = new SAML2CallbackHandler();
377         samlCallbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
378         samlCallbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
379         samlCallbackHandler.setIssuer("www.example.com");
380         samlCallbackHandler.setSignAssertion(true);
381         samlCallbackHandler.setIssuerCrypto(crypto);
382         samlCallbackHandler.setIssuerName("16c73ab6-b892-458f-abf5-2f875f74882e");
383         samlCallbackHandler.setIssuerPassword("security");
384 
385         java.util.Map<String, Object> msgContext = new java.util.TreeMap<>();
386         msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true");
387         msgContext.put(WSHandlerConstants.SAML_CALLBACK_REF, samlCallbackHandler);
388         reqData.setMsgContext(msgContext);
389 
390         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
391         CustomHandler handler = new CustomHandler();
392         HandlerAction action = new HandlerAction(WSConstants.ST_UNSIGNED);
393         handler.send(
394             doc,
395             reqData,
396             Collections.singletonList(action),
397             true
398         );
399         if (LOG.isDebugEnabled()) {
400             LOG.debug("After Signing....");
401             String outputString =
402                 XMLUtils.prettyDocumentToString(doc);
403             LOG.debug(outputString);
404         }
405 
406         //
407         // Verify the inbound request, and create a response with a Signature Confirmation
408         //
409         WSHandlerResult results = verify(doc);
410         doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
411         msgContext = (java.util.Map<String, Object>)reqData.getMsgContext();
412         List<WSHandlerResult> receivedResults = new ArrayList<>();
413         receivedResults.add(results);
414         msgContext.put(WSHandlerConstants.RECV_RESULTS, receivedResults);
415         handler.send(
416             doc,
417             reqData,
418             Collections.singletonList(new HandlerAction(WSConstants.NO_SECURITY)),
419             false
420         );
421         String outputString =
422             XMLUtils.prettyDocumentToString(doc);
423         if (LOG.isDebugEnabled()) {
424             LOG.debug("Signature Confirmation response....");
425             LOG.debug(outputString);
426         }
427 
428         //
429         // Verify the SignatureConfirmation response
430         //
431         results = verify(doc);
432         WSSecurityEngineResult scResult =
433             results.getActionResults().get(WSConstants.SC).get(0);
434         assertNotNull(scResult);
435         assertNotNull(scResult.get(WSSecurityEngineResult.TAG_SIGNATURE_CONFIRMATION));
436         handler.signatureConfirmation(reqData, results);
437     }
438 
439     /**
440      * Verifies the soap envelope
441      * <p/>
442      *
443      * @param doc
444      * @throws Exception Thrown when there is a problem in verification
445      */
446     private WSHandlerResult verify(Document doc) throws Exception {
447         WSHandlerResult results =
448             secEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
449         if (LOG.isDebugEnabled()) {
450             LOG.debug("Verfied and decrypted message:");
451             String outputString =
452                 XMLUtils.prettyDocumentToString(doc);
453             LOG.debug(outputString);
454         }
455         return results;
456     }
457 
458 }