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.io.IOException;
23  import java.nio.charset.StandardCharsets;
24  import java.security.MessageDigest;
25  import java.time.Instant;
26  import java.time.ZoneOffset;
27  import java.time.ZonedDateTime;
28  import java.util.Collections;
29  
30  import javax.security.auth.callback.Callback;
31  import javax.security.auth.callback.CallbackHandler;
32  import javax.security.auth.callback.UnsupportedCallbackException;
33  
34  import org.apache.wss4j.common.bsp.BSPEnforcer;
35  import org.apache.wss4j.common.bsp.BSPRule;
36  import org.apache.wss4j.common.ext.WSPasswordCallback;
37  import org.apache.wss4j.common.ext.WSSecurityException;
38  import org.apache.wss4j.common.util.*;
39  import org.apache.wss4j.dom.WSConstants;
40  import org.apache.wss4j.dom.common.CustomHandler;
41  import org.apache.wss4j.dom.common.EncodedPasswordCallbackHandler;
42  
43  import org.apache.wss4j.dom.common.UsernamePasswordCallbackHandler;
44  import org.apache.wss4j.dom.engine.WSSecurityEngine;
45  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
46  import org.apache.wss4j.dom.handler.HandlerAction;
47  import org.apache.wss4j.dom.handler.RequestData;
48  import org.apache.wss4j.dom.handler.WSHandlerConstants;
49  import org.apache.wss4j.dom.handler.WSHandlerResult;
50  import org.apache.wss4j.dom.message.token.UsernameToken;
51  
52  import org.junit.jupiter.api.Test;
53  import org.w3c.dom.Document;
54  import org.w3c.dom.Element;
55  import org.w3c.dom.Node;
56  import org.w3c.dom.NodeList;
57  
58  import static org.junit.jupiter.api.Assertions.assertEquals;
59  import static org.junit.jupiter.api.Assertions.assertFalse;
60  import static org.junit.jupiter.api.Assertions.assertNotNull;
61  import static org.junit.jupiter.api.Assertions.assertTrue;
62  import static org.junit.jupiter.api.Assertions.fail;
63  
64  /**
65   * WS-Security Test Case for UsernameTokens.
66   */
67  public class UsernameTokenTest implements CallbackHandler {
68      private static final org.slf4j.Logger LOG =
69          org.slf4j.LoggerFactory.getLogger(UsernameTokenTest.class);
70      private static final String SOAPUTMSG =
71          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
72          + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
73          + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
74          + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
75          + "<SOAP-ENV:Header>"
76          + "<wsse:Security SOAP-ENV:mustUnderstand=\"1\" "
77          + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">"
78          + "<wsse:UsernameToken wsu:Id=\"UsernameToken-29477163\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">"
79          + "<wsse:Username>wernerd</wsse:Username>"
80          + "<wsse:Password>verySecret</wsse:Password>"
81          + "</wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>"
82          + "<SOAP-ENV:Body>"
83          + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
84          + "<value xmlns=\"\">15</value>" + "</add>"
85          + "</SOAP-ENV:Body>\r\n       \r\n" + "</SOAP-ENV:Envelope>";
86      private static final String SOAPUTNOUSERMSG =
87          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
88          + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
89          + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
90          + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
91          + "<SOAP-ENV:Header>"
92          + "<wsse:Security SOAP-ENV:mustUnderstand=\"1\" "
93          + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">"
94          + "<wsse:UsernameToken wsu:Id=\"UsernameToken-29477163\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">"
95          + "<wsse:Username></wsse:Username>"
96          + "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\"></wsse:Password>"
97          + "</wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>"
98          + "<SOAP-ENV:Body>"
99          + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
100         + "<value xmlns=\"\">15</value>" + "</add>"
101         + "</SOAP-ENV:Body>\r\n       \r\n" + "</SOAP-ENV:Envelope>";
102     private static final String EMPTY_PASSWORD_MSG =
103         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
104         + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
105         + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
106         + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
107         + "<SOAP-ENV:Header>"
108         + "<wsse:Security SOAP-ENV:mustUnderstand=\"1\" "
109         + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">"
110         + "<wsse:UsernameToken wsu:Id=\"UsernameToken-1\" "
111         + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" "
112         + "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">"
113         + "<wsse:Username>emptyuser</wsse:Username>"
114         + "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\"/>"
115         + "</wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>"
116         + "<SOAP-ENV:Body>"
117         + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
118         + "<value xmlns=\"\">15</value>" + "</add>"
119         + "</SOAP-ENV:Body>\r\n       \r\n" + "</SOAP-ENV:Envelope>";
120 
121     private CallbackHandler callbackHandler = new UsernamePasswordCallbackHandler();
122 
123     /**
124      * Test that adds a UserNameToken with password Digest to a WS-Security envelope
125      */
126     @Test
127     public void testUsernameTokenDigest() throws Exception {
128         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
129         WSSecHeader secHeader = new WSSecHeader(doc);
130         secHeader.insertSecurityHeader();
131 
132         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
133         builder.setUserInfo("wernerd", "verySecret");
134         LOG.info("Before adding UsernameToken PW Digest....");
135         Document signedDoc = builder.build();
136 
137         if (LOG.isDebugEnabled()) {
138             LOG.debug("Message with UserNameToken PW Digest:");
139             String outputString =
140                 XMLUtils.prettyDocumentToString(signedDoc);
141             LOG.debug(outputString);
142         }
143         LOG.info("After adding UsernameToken PW Digest....");
144 
145         WSHandlerResult results = verify(signedDoc);
146         WSSecurityEngineResult actionResult =
147             results.getActionResults().get(WSConstants.UT).get(0);
148         UsernameToken receivedToken =
149             (UsernameToken) actionResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
150         assertNotNull(receivedToken);
151 
152         UsernameToken clone =
153             new UsernameToken(receivedToken.getElement(), false, new BSPEnforcer());
154         assertTrue(clone.equals(receivedToken));
155         assertTrue(clone.hashCode() == receivedToken.hashCode());
156     }
157 
158     /**
159      * Test for encoded passwords.
160      */
161     @Test
162     public void testUsernameTokenWithEncodedPasswordBaseline() throws Exception {
163         String password = "password";
164         // The SHA-1 of the password is known as a password equivalent in the UsernameToken specification.
165         byte[] passwordHash = MessageDigest.getInstance("SHA-1").digest(password.getBytes(StandardCharsets.UTF_8));
166 
167         String nonce = "0x7bXAPZVn40AdCD0Xbt0g==";
168         String created = "2010-06-28T15:16:37Z";
169         String expectedPasswordDigest = "C0rena/6gKpRZ9ATj+e6ss5sAbQ=";
170         byte[] decodedNonce = org.apache.xml.security.utils.XMLUtils.decode(nonce);
171         String actualPasswordDigest = UsernameTokenUtil.doPasswordDigest(decodedNonce, created, passwordHash);
172         assertEquals(expectedPasswordDigest, actualPasswordDigest, "the password digest is not as expected");
173     }
174 
175     /**
176      * Test that adds a UserNameToken with password Digest to a WS-Security envelope
177      */
178     @Test
179     public void testUsernameTokenWithEncodedPassword() throws Exception {
180         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
181         WSSecHeader secHeader = new WSSecHeader(doc);
182         secHeader.insertSecurityHeader();
183 
184         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
185         builder.setPasswordsAreEncoded(true);
186         byte[] bytes = MessageDigest.getInstance("SHA-1").digest("verySecret".getBytes(StandardCharsets.UTF_8));
187         builder.setUserInfo("wernerd", org.apache.xml.security.utils.XMLUtils.encodeToString(bytes));
188         LOG.info("Before adding UsernameToken PW Digest....");
189         Document signedDoc = builder.build();
190 
191         if (LOG.isDebugEnabled()) {
192             LOG.debug("Message with UserNameToken PW Digest:");
193             String outputString =
194                 XMLUtils.prettyDocumentToString(signedDoc);
195             LOG.debug(outputString);
196         }
197         LOG.info("After adding UsernameToken PW Digest....");
198 
199         WSSecurityEngine newEngine = new WSSecurityEngine();
200         RequestData requestData = new RequestData();
201         requestData.setEncodePasswords(true);
202         requestData.setCallbackHandler(new EncodedPasswordCallbackHandler());
203         newEngine.processSecurityHeader(signedDoc, requestData);
204     }
205 
206     /**
207      * Test that a bad username with password digest does not leak whether the username
208      * is valid or not - see WSS-141.
209      */
210     @Test
211     public void testUsernameTokenBadUsername() throws Exception {
212         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
213         WSSecHeader secHeader = new WSSecHeader(doc);
214         secHeader.insertSecurityHeader();
215 
216         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
217         builder.setUserInfo("badusername", "verySecret");
218         LOG.info("Before adding UsernameToken PW Digest....");
219         Document signedDoc = builder.build();
220 
221         if (LOG.isDebugEnabled()) {
222             LOG.debug("Message with UserNameToken PW Digest:");
223             String outputString =
224                 XMLUtils.prettyDocumentToString(signedDoc);
225             LOG.debug(outputString);
226         }
227         LOG.info("After adding UsernameToken PW Digest....");
228         try {
229             verify(signedDoc);
230             fail("Failure expected on a bad username");
231         } catch (WSSecurityException ex) {
232             String message = ex.getMessage();
233             assertFalse(message.contains("badusername"));
234             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
235             // expected
236         }
237     }
238 
239     /**
240      * Test that adds a UserNameToken with a bad password Digest to a WS-Security envelope
241      */
242     @Test
243     public void testUsernameTokenBadDigest() throws Exception {
244         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
245         WSSecHeader secHeader = new WSSecHeader(doc);
246         secHeader.insertSecurityHeader();
247 
248         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
249         builder.setUserInfo("wernerd", "verySecre");
250         LOG.info("Before adding UsernameToken PW Digest....");
251         Document signedDoc = builder.build();
252 
253         if (LOG.isDebugEnabled()) {
254             LOG.debug("Message with UserNameToken PW Digest:");
255             String outputString =
256                 XMLUtils.prettyDocumentToString(signedDoc);
257             LOG.debug(outputString);
258         }
259         LOG.info("After adding UsernameToken PW Digest....");
260         try {
261             verify(signedDoc);
262             fail("Failure expected on a bad password digest");
263         } catch (WSSecurityException ex) {
264             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
265             // expected
266         }
267     }
268 
269     /**
270      * This is a test for processing an "old" UsernameToken, i.e. one with a "Created" element that is
271      * out of date
272      */
273     @Test
274     public void testOldUsernameToken() throws Exception {
275         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
276         WSSecHeader secHeader = new WSSecHeader(doc);
277         secHeader.insertSecurityHeader();
278 
279         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
280         builder.setUserInfo("wernerd", "verySecret");
281         Document signedDoc = builder.build();
282 
283         if (LOG.isDebugEnabled()) {
284             String outputString =
285                 XMLUtils.prettyDocumentToString(signedDoc);
286             LOG.debug(outputString);
287         }
288 
289         RequestData requestData = new RequestData();
290         requestData.setUtTTL(-1);
291         requestData.setCallbackHandler(callbackHandler);
292 
293         try {
294             WSSecurityEngine secEngine = new WSSecurityEngine();
295             secEngine.processSecurityHeader(doc, requestData);
296             fail("The UsernameToken validation should have failed");
297         } catch (WSSecurityException ex) {
298             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
299         }
300     }
301 
302     /**
303      * This is a test for processing a UsernameToken where the "Created" element is in the (near)
304      * future. It should be accepted by default when it is created 30 seconds in the future,
305      * and then rejected once we configure "0 seconds" for future-time-to-live.
306      */
307     @Test
308     public void testNearFutureCreated() throws Exception {
309         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
310         WSSecHeader secHeader = new WSSecHeader(doc);
311         secHeader.insertSecurityHeader();
312 
313         Element usernameTokenElement =
314             doc.createElementNS(
315                 WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX + ":" + WSConstants.USERNAME_TOKEN_LN
316             );
317         Element usernameElement =
318             doc.createElementNS(
319                 WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX + ":" + WSConstants.USERNAME_LN
320             );
321         usernameElement.appendChild(doc.createTextNode("wernerd"));
322         usernameTokenElement.appendChild(usernameElement);
323 
324         Element passwordElement =
325             doc.createElementNS(
326                 WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX + ":" + WSConstants.PASSWORD_LN
327             );
328         passwordElement.setAttributeNS(null, "Type", WSConstants.PASSWORD_TEXT);
329         passwordElement.appendChild(doc.createTextNode("verySecret"));
330         usernameTokenElement.appendChild(passwordElement);
331 
332         Element elementCreated =
333             doc.createElementNS(
334                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
335             );
336         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(30L);
337         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
338         usernameTokenElement.appendChild(elementCreated);
339 
340         secHeader.getSecurityHeaderElement().appendChild(usernameTokenElement);
341 
342         if (LOG.isDebugEnabled()) {
343             String outputString =
344                 XMLUtils.prettyDocumentToString(doc);
345             LOG.debug(outputString);
346         }
347 
348         // This should work
349         WSSecurityEngine secEngine = new WSSecurityEngine();
350         secEngine.processSecurityHeader(doc, null, callbackHandler, null);
351 
352         // This should not
353         try {
354             RequestData requestData = new RequestData();
355             requestData.setUtFutureTTL(0);
356             requestData.setCallbackHandler(callbackHandler);
357 
358             secEngine.processSecurityHeader(doc, requestData);
359             fail("The UsernameToken validation should have failed");
360         } catch (WSSecurityException ex) {
361             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
362         }
363     }
364 
365     /**
366      * This is a test for processing a UsernameToken where the "Created" element is in the future.
367      * A UsernameToken that is 120 seconds in the future should be rejected by default.
368      */
369     @Test
370     public void testFutureCreated() throws Exception {
371         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
372         WSSecHeader secHeader = new WSSecHeader(doc);
373         secHeader.insertSecurityHeader();
374 
375         Element usernameTokenElement =
376             doc.createElementNS(
377                 WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX + ":" + WSConstants.USERNAME_TOKEN_LN
378             );
379         Element usernameElement =
380             doc.createElementNS(
381                 WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX + ":" + WSConstants.USERNAME_LN
382             );
383         usernameElement.appendChild(doc.createTextNode("wernerd"));
384         usernameTokenElement.appendChild(usernameElement);
385 
386         Element passwordElement =
387             doc.createElementNS(
388                 WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX + ":" + WSConstants.PASSWORD_LN
389             );
390         passwordElement.setAttributeNS(null, "Type", WSConstants.PASSWORD_TEXT);
391         passwordElement.appendChild(doc.createTextNode("verySecret"));
392         usernameTokenElement.appendChild(passwordElement);
393 
394         Element elementCreated =
395             doc.createElementNS(
396                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
397             );
398         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(120L);
399         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
400         usernameTokenElement.appendChild(elementCreated);
401 
402         secHeader.getSecurityHeaderElement().appendChild(usernameTokenElement);
403 
404         if (LOG.isDebugEnabled()) {
405             String outputString =
406                 XMLUtils.prettyDocumentToString(doc);
407             LOG.debug(outputString);
408         }
409 
410         try {
411             WSSecurityEngine secEngine = new WSSecurityEngine();
412             secEngine.processSecurityHeader(doc, null, callbackHandler, null);
413             fail("The UsernameToken validation should have failed");
414         } catch (WSSecurityException ex) {
415             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
416         }
417     }
418 
419     /**
420      * Test that adds a UserNameToken with password text to a WS-Security envelope
421      */
422     @Test
423     public void testUsernameTokenText() throws Exception {
424         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
425         WSSecHeader secHeader = new WSSecHeader(doc);
426         secHeader.insertSecurityHeader();
427 
428         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
429         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
430         builder.setUserInfo("wernerd", "verySecret");
431         LOG.info("Before adding UsernameToken PW Text....");
432         Document signedDoc = builder.build();
433         if (LOG.isDebugEnabled()) {
434             LOG.debug("Message with UserNameToken PW Text:");
435             String outputString =
436                 XMLUtils.prettyDocumentToString(signedDoc);
437             LOG.debug(outputString);
438         }
439         LOG.info("After adding UsernameToken PW Text....");
440 
441         WSHandlerResult results = verify(signedDoc);
442         WSSecurityEngineResult actionResult =
443             results.getActionResults().get(WSConstants.UT).get(0);
444         UsernameToken receivedToken =
445             (UsernameToken) actionResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
446         assertNotNull(receivedToken);
447 
448         UsernameToken clone =
449             new UsernameToken(receivedToken.getElement(), false, new BSPEnforcer());
450         assertTrue(clone.equals(receivedToken));
451         assertTrue(clone.hashCode() == receivedToken.hashCode());
452     }
453 
454     /**
455      * Test that adds a UserNameToken with a digested password but with type of
456      * password test.
457      */
458     @Test
459     public void testUsernameTokenDigestText() throws Exception {
460         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
461         WSSecHeader secHeader = new WSSecHeader(doc);
462         secHeader.insertSecurityHeader();
463 
464         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
465         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
466         byte[] password = "verySecret".getBytes();
467         MessageDigest sha = MessageDigest.getInstance("MD5");
468         sha.reset();
469         sha.update(password);
470         String passwdDigest = org.apache.xml.security.utils.XMLUtils.encodeToString(sha.digest());
471 
472         builder.setUserInfo("wernerd", passwdDigest);
473         LOG.info("Before adding UsernameToken PW Text....");
474         Document signedDoc = builder.build();
475         if (LOG.isDebugEnabled()) {
476             LOG.debug("Message with UserNameToken PW Text:");
477             String outputString =
478                 XMLUtils.prettyDocumentToString(signedDoc);
479             LOG.debug(outputString);
480         }
481     }
482 
483     /**
484      * Test that adds a UserNameToken with (bad) password text to a WS-Security envelope
485      */
486     @Test
487     public void testUsernameTokenBadText() throws Exception {
488         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
489         WSSecHeader secHeader = new WSSecHeader(doc);
490         secHeader.insertSecurityHeader();
491 
492         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
493         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
494         builder.setUserInfo("wernerd", "verySecre");
495         LOG.info("Before adding UsernameToken PW Text....");
496         Document signedDoc = builder.build();
497         if (LOG.isDebugEnabled()) {
498             LOG.debug("Message with UserNameToken PW Text:");
499             String outputString =
500                 XMLUtils.prettyDocumentToString(signedDoc);
501             LOG.debug(outputString);
502         }
503         LOG.info("After adding UsernameToken PW Text....");
504 
505         try {
506             verify(signedDoc);
507             fail("Failure expected on a bad password text");
508         } catch (WSSecurityException ex) {
509             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
510             // expected
511         }
512     }
513 
514     /**
515      * Test that adds a UserNameToken with no password type to a WS-Security envelope
516      * See WSS-152 - https://issues.apache.org/jira/browse/WSS-152
517      * "Problem with processing Username Tokens with no password type"
518      * The 1.1 spec states that the password type is optional and defaults to password text,
519      * and so we should handle an incoming Username Token accordingly.
520      */
521     @Test
522     public void testUsernameTokenNoPasswordType() throws Exception {
523         Document doc = SOAPUtil.toSOAPPart(SOAPUTMSG);
524         if (LOG.isDebugEnabled()) {
525             String outputString =
526                 XMLUtils.prettyDocumentToString(doc);
527             LOG.debug(outputString);
528         }
529 
530         WSSecurityEngine newEngine = new WSSecurityEngine();
531         try {
532             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
533             fail("Expected failure as it is not BSP compliant");
534         } catch (WSSecurityException ex) {
535             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
536         }
537         RequestData data = new RequestData();
538         data.setCallbackHandler(callbackHandler);
539         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R4201));
540         newEngine.processSecurityHeader(doc, data);
541     }
542 
543     /**
544      * Test that adds a UserNameToken with no user (or password) to a WS-Security envelope
545      * See WSS-185 - https://issues.apache.org/jira/browse/WSS-185
546      * "NullPointerException on empty UsernameToken"
547      */
548     @Test
549     public void testUsernameTokenNoUser() throws Exception {
550         Document doc = SOAPUtil.toSOAPPart(SOAPUTNOUSERMSG);
551         if (LOG.isDebugEnabled()) {
552             String outputString =
553                 XMLUtils.prettyDocumentToString(doc);
554             LOG.debug(outputString);
555         }
556         try {
557             verify(doc);
558             fail("Failure expected on no password");
559         } catch (WSSecurityException ex) {
560             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
561             // expected
562         }
563     }
564 
565     /**
566      * Test that adds a UserNameToken with no password
567      */
568     @Test
569     public void testUsernameTokenNoPassword() throws Exception {
570         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
571         WSSecHeader secHeader = new WSSecHeader(doc);
572         secHeader.insertSecurityHeader();
573 
574         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
575         builder.setPasswordType(null);
576         builder.setUserInfo("nopassuser", null);
577         LOG.info("Before adding UsernameToken with no password....");
578         Document signedDoc = builder.build();
579         if (LOG.isDebugEnabled()) {
580             String outputString =
581                 XMLUtils.prettyDocumentToString(signedDoc);
582             LOG.debug(outputString);
583         }
584 
585         WSHandlerResult results = verify(signedDoc, true);
586         WSSecurityEngineResult actionResult =
587             results.getActionResults().get(WSConstants.UT_NOPASSWORD).get(0);
588         UsernameToken receivedToken =
589             (UsernameToken) actionResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
590         assertNotNull(receivedToken);
591     }
592 
593     /**
594      * Test that adds a UserNameToken with an empty password
595      */
596     @Test
597     public void testUsernameTokenEmptyPassword() throws Exception {
598         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
599         WSSecHeader secHeader = new WSSecHeader(doc);
600         secHeader.insertSecurityHeader();
601 
602         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
603         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
604         builder.setUserInfo("emptyuser", "");
605         LOG.info("Before adding UsernameToken with an empty password....");
606         Document signedDoc = builder.build();
607         if (LOG.isDebugEnabled()) {
608             String outputString =
609                 XMLUtils.prettyDocumentToString(signedDoc);
610             LOG.debug(outputString);
611         }
612         WSSecurityEngine secEngine = new WSSecurityEngine();
613         secEngine.processSecurityHeader(doc, null, this, null);
614     }
615 
616     /**
617      * Test that processes a UserNameToken with an empty password
618      */
619     @Test
620     public void testEmptyPasswordProcessing() throws Exception {
621         Document doc = SOAPUtil.toSOAPPart(EMPTY_PASSWORD_MSG);
622         if (LOG.isDebugEnabled()) {
623             LOG.debug("Empty password message: ");
624             String outputString =
625                 XMLUtils.prettyDocumentToString(doc);
626             LOG.debug(outputString);
627         }
628 
629         WSSecurityEngine secEngine = new WSSecurityEngine();
630         secEngine.processSecurityHeader(doc, null, this, null);
631     }
632 
633     /**
634      * Test with a non-standard token type. This will fail as the default is to reject custom
635      * token types.
636      */
637     @Test
638     public void testUsernameTokenCustomFail() throws Exception {
639         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
640         WSSecHeader secHeader = new WSSecHeader(doc);
641         secHeader.insertSecurityHeader();
642 
643         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
644         builder.setPasswordType("RandomType");
645         builder.setUserInfo("wernerd", "verySecret");
646 
647         Document signedDoc = builder.build();
648 
649         if (LOG.isDebugEnabled()) {
650             LOG.debug("Message with UserNameToken PW Text:");
651             String outputString =
652                 XMLUtils.prettyDocumentToString(signedDoc);
653             LOG.debug(outputString);
654         }
655         try {
656             WSSecurityEngine secEngine = new WSSecurityEngine();
657             secEngine.processSecurityHeader(signedDoc, null, this, null);
658             fail("Custom token types are not permitted");
659         } catch (WSSecurityException ex) {
660             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
661             // expected
662         }
663     }
664 
665     /**
666      * Test with a non-standard password type. This will pass as the WSSConfig is configured to
667      * handle custom token types.
668      */
669     @Test
670     public void testUsernameTokenCustomPass() throws Exception {
671         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
672         WSSecHeader secHeader = new WSSecHeader(doc);
673         secHeader.insertSecurityHeader();
674 
675         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
676         builder.setPasswordType("RandomType");
677         builder.setUserInfo("wernerd", "verySecret");
678 
679         Document signedDoc = builder.build();
680 
681         if (LOG.isDebugEnabled()) {
682             LOG.debug("Message with UserNameToken PW custom type:");
683             String outputString =
684                 XMLUtils.prettyDocumentToString(signedDoc);
685             LOG.debug(outputString);
686         }
687 
688         //
689         // Configure so that custom token types are accepted
690         //
691         WSSecurityEngine secEngine = new WSSecurityEngine();
692 
693         RequestData requestData = new RequestData();
694         requestData.setHandleCustomPasswordTypes(true);
695         requestData.setCallbackHandler(callbackHandler);
696 
697         secEngine.processSecurityHeader(doc, requestData);
698     }
699 
700 
701     /**
702      * A test for WSS-66 - the nonce string is null
703      * http://issues.apache.org/jira/browse/WSS-66
704      * "Possible security hole when PasswordDigest is used by client."
705      */
706     @Test
707     public void testNullNonce() throws Exception {
708         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
709         WSSecHeader secHeader = new WSSecHeader(doc);
710         secHeader.insertSecurityHeader();
711 
712         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
713         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
714         builder.setUserInfo("wernerd", "BAD_PASSWORD");
715 
716         Document utDoc = builder.build();
717 
718         //
719         // Manually find the Nonce node and set the content to null
720         //
721         Element elem = builder.getUsernameTokenElement();
722         NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Nonce");
723         Node nonceNode = list.item(0);
724         Node childNode = nonceNode.getFirstChild();
725         childNode.setNodeValue("");
726 
727         if (LOG.isDebugEnabled()) {
728             String outputString =
729                 XMLUtils.prettyDocumentToString(utDoc);
730             LOG.debug(outputString);
731         }
732 
733         try {
734             //
735             // Verification should fail as the password is bad
736             //
737             verify(utDoc);
738             fail("Expected failure due to a bad password");
739         } catch (WSSecurityException ex) {
740             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
741             // expected
742         }
743     }
744 
745     /**
746      * A test for WSS-66 - the created string is null
747      * http://issues.apache.org/jira/browse/WSS-66
748      * "Possible security hole when PasswordDigest is used by client."
749      */
750     @Test
751     public void testNullCreated() throws Exception {
752         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
753         WSSecHeader secHeader = new WSSecHeader(doc);
754         secHeader.insertSecurityHeader();
755 
756         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
757         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
758         builder.setUserInfo("wernerd", "BAD_PASSWORD");
759 
760         Document utDoc = builder.build();
761         //
762         // Manually find the Created node and set the content to null
763         //
764         Element elem = builder.getUsernameTokenElement();
765         NodeList list = elem.getElementsByTagNameNS(WSConstants.WSU_NS, "Created");
766         Node nonceNode = list.item(0);
767         Node childNode = nonceNode.getFirstChild();
768         childNode.setNodeValue("");
769 
770         if (LOG.isDebugEnabled()) {
771             String outputString =
772                 XMLUtils.prettyDocumentToString(utDoc);
773             LOG.debug(outputString);
774         }
775 
776         try {
777             //
778             // Verification should fail as the password is bad
779             //
780             verify(utDoc);
781             fail("Expected failure due to a bad password");
782         } catch (WSSecurityException ex) {
783             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
784             // expected
785         }
786     }
787 
788     /**
789      * Test that verifies an EncodingType is set for the nonce. See WSS-169.
790      */
791     @Test
792     public void testUsernameTokenNonceEncodingType() throws Exception {
793         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
794         WSSecHeader secHeader = new WSSecHeader(doc);
795         secHeader.insertSecurityHeader();
796 
797         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
798         builder.setUserInfo("wernerd", "verySecret");
799         LOG.info("Before adding UsernameToken PW Digest....");
800         Document signedDoc = builder.build();
801         String outputString =
802             XMLUtils.prettyDocumentToString(signedDoc);
803         assertTrue(outputString.contains("EncodingType"));
804     }
805 
806     /**
807      * Test that adds a UserNameToken via WSHandler
808      */
809     @Test
810     public void testUsernameTokenWSHandler() throws Exception {
811         CustomHandler handler = new CustomHandler();
812         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
813 
814         RequestData reqData = new RequestData();
815         java.util.Map<String, Object> config = new java.util.TreeMap<>();
816         config.put("password", "verySecret");
817         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
818         reqData.setUsername("wernerd");
819         reqData.setMsgContext(config);
820 
821         HandlerAction action = new HandlerAction(WSConstants.UT);
822         handler.send(
823             doc,
824             reqData,
825             Collections.singletonList(action),
826             true
827         );
828 
829         if (LOG.isDebugEnabled()) {
830             LOG.debug("Username Token via WSHandler");
831             String outputString =
832                 XMLUtils.prettyDocumentToString(doc);
833             LOG.debug(outputString);
834         }
835     }
836 
837     /**
838      * Test that adds a UserNameToken with no password via WSHandler
839      */
840     @Test
841     public void testUsernameTokenWSHandlerNoPassword() throws Exception {
842         CustomHandler handler = new CustomHandler();
843         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
844 
845         RequestData reqData = new RequestData();
846         java.util.Map<String, Object> config = new java.util.TreeMap<>();
847         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_NONE);
848         reqData.setUsername("wernerd");
849         reqData.setMsgContext(config);
850 
851         HandlerAction action = new HandlerAction(WSConstants.UT);
852         handler.send(
853             doc,
854             reqData,
855             Collections.singletonList(action),
856             true
857         );
858 
859         if (LOG.isDebugEnabled()) {
860             LOG.debug("Username Token via WSHandler");
861             String outputString =
862                 XMLUtils.prettyDocumentToString(doc);
863             LOG.debug(outputString);
864         }
865     }
866 
867     /**
868      * Test that adds a UserNameToken with no password via WSHandler
869      */
870     @Test
871     public void testUsernameTokenWSHandlerNoPassword2() throws Exception {
872         CustomHandler handler = new CustomHandler();
873         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
874 
875         RequestData reqData = new RequestData();
876         java.util.Map<String, Object> config = new java.util.TreeMap<>();
877         reqData.setUsername("wernerd");
878         reqData.setMsgContext(config);
879 
880         HandlerAction action = new HandlerAction(WSConstants.UT_NOPASSWORD);
881         handler.send(
882             doc,
883             reqData,
884             Collections.singletonList(action),
885             true
886         );
887 
888         if (LOG.isDebugEnabled()) {
889             LOG.debug("Username Token via WSHandler");
890             String outputString =
891                 XMLUtils.prettyDocumentToString(doc);
892             LOG.debug(outputString);
893         }
894     }
895 
896     /**
897      * Test that adds a UserNameToken with an empty password via WSHandler
898      */
899     @Test
900     public void testUsernameTokenWSHandlerEmptyPassword() throws Exception {
901         CustomHandler handler = new CustomHandler();
902         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
903 
904         RequestData reqData = new RequestData();
905         java.util.Map<String, Object> config = new java.util.TreeMap<>();
906         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
907         config.put(WSHandlerConstants.PW_CALLBACK_REF, this);
908         reqData.setUsername("emptyuser");
909         reqData.setMsgContext(config);
910 
911         HandlerAction action = new HandlerAction(WSConstants.UT);
912         handler.send(
913             doc,
914             reqData,
915             Collections.singletonList(action),
916             true
917         );
918 
919         if (LOG.isDebugEnabled()) {
920             LOG.debug("Username Token with an empty password via WSHandler");
921             String outputString =
922                 XMLUtils.prettyDocumentToString(doc);
923             LOG.debug(outputString);
924         }
925     }
926 
927     /**
928      * A test for sending multiple nonces in the UsernameToken
929      */
930     @Test
931     public void testMultipleNonce() throws Exception {
932         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
933         WSSecHeader secHeader = new WSSecHeader(doc);
934         secHeader.insertSecurityHeader();
935 
936         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
937         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
938         builder.setUserInfo("wernerd", "verySecret");
939 
940         Document utDoc = builder.build();
941 
942         //
943         // Manually find the Nonce node and duplicate it
944         //
945         Element elem = builder.getUsernameTokenElement();
946         NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Nonce");
947         Node nonceNode = list.item(0);
948         Node nonceCopy = nonceNode.cloneNode(true);
949         nonceNode.getParentNode().insertBefore(nonceCopy, nonceNode);
950 
951         if (LOG.isDebugEnabled()) {
952             String outputString =
953                 XMLUtils.prettyDocumentToString(utDoc);
954             LOG.debug(outputString);
955         }
956 
957         WSSecurityEngine newEngine = new WSSecurityEngine();
958         try {
959             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
960             fail("Expected failure as it is not BSP compliant");
961         } catch (WSSecurityException ex) {
962             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY      );
963         }
964 
965         RequestData data = new RequestData();
966         data.setCallbackHandler(callbackHandler);
967         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R4225));
968         newEngine.processSecurityHeader(doc, data);
969     }
970 
971     /**
972      * A test for sending multiple Created elements in the UsernameToken
973      */
974     @Test
975     public void testMultipleCreated() throws Exception {
976         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
977         WSSecHeader secHeader = new WSSecHeader(doc);
978         secHeader.insertSecurityHeader();
979 
980         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
981         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
982         builder.setUserInfo("wernerd", "verySecret");
983 
984         Document utDoc = builder.build();
985 
986         //
987         // Manually find the Created node and duplicate it
988         //
989         Element elem = builder.getUsernameTokenElement();
990         NodeList list = elem.getElementsByTagNameNS(WSConstants.WSU_NS, "Created");
991         Node createdNode = list.item(0);
992         Node createdCopy = createdNode.cloneNode(true);
993         createdNode.getParentNode().insertBefore(createdCopy, createdNode);
994 
995         if (LOG.isDebugEnabled()) {
996             String outputString =
997                 XMLUtils.prettyDocumentToString(utDoc);
998             LOG.debug(outputString);
999         }
1000 
1001         WSSecurityEngine newEngine = new WSSecurityEngine();
1002         try {
1003             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
1004             fail("Expected failure as it is not BSP compliant");
1005         } catch (WSSecurityException ex) {
1006             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
1007         }
1008 
1009         RequestData data = new RequestData();
1010         data.setCallbackHandler(callbackHandler);
1011         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R4223));
1012         newEngine.processSecurityHeader(doc, data);
1013     }
1014 
1015     /**
1016      * A test for sending multiple passwords in the UsernameToken
1017      */
1018     @Test
1019     public void testMultiplePassword() throws Exception {
1020         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
1021         WSSecHeader secHeader = new WSSecHeader(doc);
1022         secHeader.insertSecurityHeader();
1023 
1024         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
1025         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
1026         builder.setUserInfo("wernerd", "verySecret");
1027 
1028         Document utDoc = builder.build();
1029 
1030         //
1031         // Manually find the Nonce node and duplicate it
1032         //
1033         Element elem = builder.getUsernameTokenElement();
1034         NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Password");
1035         Node passwordNode = list.item(0);
1036         Node passwordCopy = passwordNode.cloneNode(true);
1037         passwordNode.getParentNode().insertBefore(passwordCopy, passwordNode);
1038 
1039         if (LOG.isDebugEnabled()) {
1040             String outputString =
1041                 XMLUtils.prettyDocumentToString(utDoc);
1042             LOG.debug(outputString);
1043         }
1044 
1045         WSSecurityEngine newEngine = new WSSecurityEngine();
1046         try {
1047             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
1048             fail("Expected failure as it is not BSP compliant");
1049         } catch (WSSecurityException ex) {
1050             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
1051         }
1052 
1053         RequestData data = new RequestData();
1054         data.setCallbackHandler(callbackHandler);
1055         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R4222));
1056         newEngine.processSecurityHeader(doc, data);
1057     }
1058 
1059     /**
1060      * A test for sending a nonce with a bad encoding type in the UsernameToken
1061      */
1062     @Test
1063     public void testNonceBadEncodingType() throws Exception {
1064         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
1065         WSSecHeader secHeader = new WSSecHeader(doc);
1066         secHeader.insertSecurityHeader();
1067 
1068         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
1069         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
1070         builder.setUserInfo("wernerd", "verySecret");
1071 
1072         Document utDoc = builder.build();
1073 
1074         //
1075         // Manually find the Nonce node and duplicate it
1076         //
1077         Element elem = builder.getUsernameTokenElement();
1078         NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Nonce");
1079         Node nonceNode = list.item(0);
1080         ((Element)nonceNode).setAttributeNS(
1081             null, "EncodingType", "http://bad_encoding_type"
1082         );
1083 
1084         if (LOG.isDebugEnabled()) {
1085             String outputString =
1086                 XMLUtils.prettyDocumentToString(utDoc);
1087             LOG.debug(outputString);
1088         }
1089 
1090         WSSecurityEngine newEngine = new WSSecurityEngine();
1091         try {
1092             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
1093             fail("Expected failure as it is not BSP compliant");
1094         } catch (WSSecurityException ex) {
1095             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
1096         }
1097 
1098         RequestData data = new RequestData();
1099         data.setCallbackHandler(callbackHandler);
1100         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R4221));
1101         newEngine.processSecurityHeader(doc, data);
1102     }
1103 
1104     @Test
1105     public void testUsernameTokenWSHandlerNonceCreated() throws Exception {
1106         CustomHandler handler = new CustomHandler();
1107         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
1108 
1109         RequestData reqData = new RequestData();
1110         java.util.Map<String, Object> config = new java.util.TreeMap<>();
1111         config.put("password", "verySecret");
1112         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
1113         config.put(WSHandlerConstants.ADD_USERNAMETOKEN_NONCE, "true");
1114         config.put(WSHandlerConstants.ADD_USERNAMETOKEN_CREATED, "true");
1115         reqData.setUsername("wernerd");
1116         reqData.setMsgContext(config);
1117 
1118         HandlerAction action = new HandlerAction(WSConstants.UT);
1119         handler.send(
1120             doc,
1121             reqData,
1122             Collections.singletonList(action),
1123             true
1124         );
1125 
1126         if (LOG.isDebugEnabled()) {
1127             LOG.debug("Username Token via WSHandler");
1128             String outputString =
1129                 XMLUtils.prettyDocumentToString(doc);
1130             LOG.debug(outputString);
1131         }
1132     }
1133 
1134     /**
1135      * This is a test to create a "Spoofed" UsernameToken (see WSS-441)
1136      */
1137     @Test
1138     public void testSpoofedUsernameToken() throws Exception {
1139         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
1140         WSSecHeader secHeader = new WSSecHeader(doc);
1141         secHeader.insertSecurityHeader();
1142 
1143         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
1144         builder.setUserInfo("wernerd", "verySecret");
1145 
1146         WSTimeSource spoofedTimeSource = new WSTimeSource() {
1147 
1148             public Instant now() {
1149                 return Instant.now().minusSeconds(500L);
1150             }
1151 
1152         };
1153 
1154         builder.setWsTimeSource(spoofedTimeSource);
1155         Document signedDoc = builder.build();
1156 
1157         if (LOG.isDebugEnabled()) {
1158             String outputString =
1159                 XMLUtils.prettyDocumentToString(signedDoc);
1160             LOG.debug(outputString);
1161         }
1162 
1163         try {
1164             WSSecurityEngine secEngine = new WSSecurityEngine();
1165             secEngine.processSecurityHeader(doc, null, callbackHandler, null);
1166             fail("The UsernameToken validation should have failed");
1167         } catch (WSSecurityException ex) {
1168             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
1169         }
1170     }
1171 
1172 
1173     private WSHandlerResult verify(Document doc) throws Exception {
1174         return verify(doc, false);
1175     }
1176 
1177     /**
1178      * Verifies the soap envelope
1179      *
1180      * @param doc
1181      * @param allowUsernameTokenDerivedKeys
1182      * @throws Exception Thrown when there is a problem in verification
1183      */
1184     private WSHandlerResult verify(Document doc, boolean allowUsernameTokenDerivedKeys) throws Exception {
1185         WSSecurityEngine secEngine = new WSSecurityEngine();
1186 
1187         RequestData requestData = new RequestData();
1188         requestData.setAllowUsernameTokenNoPassword(allowUsernameTokenDerivedKeys);
1189         requestData.setCallbackHandler(callbackHandler);
1190 
1191         return secEngine.processSecurityHeader(doc, requestData);
1192     }
1193 
1194     /**
1195      * A CallbackHandler for some (mostly insecure) scenarios.
1196      */
1197     public void handle(Callback[] callbacks)
1198         throws IOException, UnsupportedCallbackException {
1199         for (Callback callback : callbacks) {
1200             if (callback instanceof WSPasswordCallback) {
1201                 WSPasswordCallback pc = (WSPasswordCallback) callback;
1202                 if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN) {
1203                     if ("emptyuser".equals(pc.getIdentifier())) {
1204                         pc.setPassword("");
1205                     } else if ("customUser".equals(pc.getIdentifier())) {
1206                         return;
1207                     } else if (null == pc.getIdentifier()) {
1208                         // Note that this is not secure! Just doing this to test a NPE
1209                         return;
1210                     }
1211                 }
1212             } else {
1213                 throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
1214             }
1215         }
1216     }
1217 }