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.ws.security.message;
21  
22  import org.apache.ws.security.WSSecurityEngineResult;
23  import org.apache.ws.security.WSSecurityException;
24  import org.apache.ws.security.WSPasswordCallback;
25  import org.apache.ws.security.WSSecurityEngine;
26  import org.apache.ws.security.WSConstants;
27  import org.apache.ws.security.WSSConfig;
28  import org.apache.ws.security.common.CustomHandler;
29  import org.apache.ws.security.common.EncodedPasswordCallbackHandler;
30  import org.apache.ws.security.common.UsernamePasswordCallbackHandler;
31  import org.apache.ws.security.common.SOAPUtil;
32  import org.apache.ws.security.handler.RequestData;
33  import org.apache.ws.security.handler.WSHandlerConstants;
34  import org.apache.ws.security.message.token.UsernameToken;
35  import org.apache.ws.security.util.Base64;
36  import org.apache.ws.security.util.WSSecurityUtil;
37  import org.w3c.dom.Document;
38  
39  import javax.security.auth.callback.Callback;
40  import javax.security.auth.callback.CallbackHandler;
41  import javax.security.auth.callback.UnsupportedCallbackException;
42  import java.io.IOException;
43  import java.security.MessageDigest;
44  import java.util.List;
45  
46  /**
47   * WS-Security Test Case for UsernameTokens.
48   * 
49   * @author Davanum Srinivas (dims@yahoo.com)
50   */
51  public class UsernameTokenTest extends org.junit.Assert implements CallbackHandler {
52      private static final org.apache.commons.logging.Log LOG = 
53          org.apache.commons.logging.LogFactory.getLog(UsernameTokenTest.class);
54      private static final String SOAPUTMSG = 
55          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 
56          + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
57          + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
58          + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
59          + "<SOAP-ENV:Header>"
60          + "<wsse:Security SOAP-ENV:mustUnderstand=\"1\" "
61          + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">"
62          + "<wsse:UsernameToken wsu:Id=\"UsernameToken-29477163\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">"
63          + "<wsse:Username>wernerd</wsse:Username>"
64          + "<wsse:Password>verySecret</wsse:Password>"
65          + "</wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>"
66          + "<SOAP-ENV:Body>" 
67          + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">" 
68          + "<value xmlns=\"\">15</value>" + "</add>" 
69          + "</SOAP-ENV:Body>\r\n       \r\n" + "</SOAP-ENV:Envelope>";
70      private static final String SOAPUTNOUSERMSG = 
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></wsse:Username>"
80          + "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\"></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 EMPTY_PASSWORD_MSG =
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-1\" "
95          + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" "
96          + "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">"
97          + "<wsse:Username>emptyuser</wsse:Username>"
98          + "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\"/>"
99          + "</wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>"
100         + "<SOAP-ENV:Body>" 
101         + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">" 
102         + "<value xmlns=\"\">15</value>" + "</add>" 
103         + "</SOAP-ENV:Body>\r\n       \r\n" + "</SOAP-ENV:Envelope>";
104     
105     private WSSecurityEngine secEngine = new WSSecurityEngine();
106     private CallbackHandler callbackHandler = new UsernamePasswordCallbackHandler();
107 
108     /**
109      * Test that adds a UserNameToken with password Digest to a WS-Security envelope
110      */
111     @org.junit.Test
112     public void testUsernameTokenDigest() throws Exception {
113         WSSecUsernameToken builder = new WSSecUsernameToken();
114         builder.setUserInfo("wernerd", "verySecret");
115         LOG.info("Before adding UsernameToken PW Digest....");
116         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
117         WSSecHeader secHeader = new WSSecHeader();
118         secHeader.insertSecurityHeader(doc);
119         Document signedDoc = builder.build(doc, secHeader);
120 
121         if (LOG.isDebugEnabled()) {
122             LOG.debug("Message with UserNameToken PW Digest:");
123             String outputString = 
124                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
125             LOG.debug(outputString);
126         }
127         LOG.info("After adding UsernameToken PW Digest....");
128         
129         List<WSSecurityEngineResult> results = verify(signedDoc);
130         WSSecurityEngineResult actionResult =
131             WSSecurityUtil.fetchActionResult(results, WSConstants.UT);
132         UsernameToken receivedToken = 
133             (UsernameToken) actionResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
134         assertTrue(receivedToken != null);
135         
136         UsernameToken clone = new UsernameToken(receivedToken.getElement());
137         assertTrue(clone.equals(receivedToken));
138         assertTrue(clone.hashCode() == receivedToken.hashCode());
139     }
140     
141     /**
142      * Test for encoded passwords.
143      */
144     @org.junit.Test
145     public void testUsernameTokenWithEncodedPasswordBaseline() throws Exception {
146         String password = "password";
147         // The SHA-1 of the password is known as a password equivalent in the UsernameToken specification.
148         byte[] passwordHash = MessageDigest.getInstance("SHA-1").digest(password.getBytes("UTF-8"));
149 
150         String nonce = "0x7bXAPZVn40AdCD0Xbt0g==";
151         String created = "2010-06-28T15:16:37Z";
152         String expectedPasswordDigest = "C0rena/6gKpRZ9ATj+e6ss5sAbQ=";
153         String actualPasswordDigest = UsernameToken.doPasswordDigest(nonce, created, passwordHash);
154         assertEquals("the password digest is not as expected", expectedPasswordDigest, actualPasswordDigest);
155     }
156     
157     /**
158      * Test that adds a UserNameToken with password Digest to a WS-Security envelope
159      */
160     @org.junit.Test
161     public void testUsernameTokenWithEncodedPassword() throws Exception {
162         WSSecUsernameToken builder = new WSSecUsernameToken();
163         builder.setPasswordsAreEncoded(true);
164         builder.setUserInfo("wernerd", Base64.encode(MessageDigest.getInstance("SHA-1").digest("verySecret".getBytes("UTF-8"))));
165         LOG.info("Before adding UsernameToken PW Digest....");
166         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
167         WSSecHeader secHeader = new WSSecHeader();
168         secHeader.insertSecurityHeader(doc);
169         Document signedDoc = builder.build(doc, secHeader);
170 
171         if (LOG.isDebugEnabled()) {
172             LOG.debug("Message with UserNameToken PW Digest:");
173             String outputString = 
174                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
175             LOG.debug(outputString);
176         }
177         LOG.info("After adding UsernameToken PW Digest....");
178 
179         WSSecurityEngine newEngine = new WSSecurityEngine();
180         newEngine.getWssConfig().setPasswordsAreEncoded(true);
181         newEngine.processSecurityHeader(signedDoc, null, new EncodedPasswordCallbackHandler(), null);
182     }
183     
184     /**
185      * Test that a bad username with password digest does not leak whether the username
186      * is valid or not - see WSS-141.
187      */
188     @org.junit.Test
189     public void testUsernameTokenBadUsername() throws Exception {
190         WSSecUsernameToken builder = new WSSecUsernameToken();
191         builder.setUserInfo("badusername", "verySecret");
192         LOG.info("Before adding UsernameToken PW Digest....");
193         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
194         WSSecHeader secHeader = new WSSecHeader();
195         secHeader.insertSecurityHeader(doc);
196         Document signedDoc = builder.build(doc, secHeader);
197 
198         if (LOG.isDebugEnabled()) {
199             LOG.debug("Message with UserNameToken PW Digest:");
200             String outputString = 
201                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
202             LOG.debug(outputString);
203         }
204         LOG.info("After adding UsernameToken PW Digest....");
205         try {
206             verify(signedDoc);
207             fail("Failure expected on a bad username");
208         } catch (WSSecurityException ex) {
209             String message = ex.getMessage();
210             assertTrue(message.indexOf("badusername") == -1);
211             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
212             // expected
213         }
214     }
215     
216     /**
217      * Test that adds a UserNameToken with a bad password Digest to a WS-Security envelope
218      */
219     @org.junit.Test
220     public void testUsernameTokenBadDigest() throws Exception {
221         WSSecUsernameToken builder = new WSSecUsernameToken();
222         builder.setUserInfo("wernerd", "verySecre");
223         LOG.info("Before adding UsernameToken PW Digest....");
224         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
225         WSSecHeader secHeader = new WSSecHeader();
226         secHeader.insertSecurityHeader(doc);
227         Document signedDoc = builder.build(doc, secHeader);
228 
229         if (LOG.isDebugEnabled()) {
230             LOG.debug("Message with UserNameToken PW Digest:");
231             String outputString = 
232                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
233             LOG.debug(outputString);
234         }
235         LOG.info("After adding UsernameToken PW Digest....");
236         try {
237             verify(signedDoc);
238             fail("Failure expected on a bad password digest");
239         } catch (WSSecurityException ex) {
240             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
241             // expected
242         }
243     }
244 
245     /**
246      * Test that adds a UserNameToken with password text to a WS-Security envelope
247      */
248     @org.junit.Test
249     public void testUsernameTokenText() throws Exception {
250         WSSecUsernameToken builder = new WSSecUsernameToken();
251         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
252         builder.setUserInfo("wernerd", "verySecret");
253         LOG.info("Before adding UsernameToken PW Text....");
254         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
255         WSSecHeader secHeader = new WSSecHeader();
256         secHeader.insertSecurityHeader(doc);
257         Document signedDoc = builder.build(doc, secHeader);
258         if (LOG.isDebugEnabled()) {
259             LOG.debug("Message with UserNameToken PW Text:");
260             String outputString = 
261                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
262             LOG.debug(outputString);
263         }
264         LOG.info("After adding UsernameToken PW Text....");
265 
266         List<WSSecurityEngineResult> results = verify(signedDoc);
267         WSSecurityEngineResult actionResult =
268             WSSecurityUtil.fetchActionResult(results, WSConstants.UT);
269         UsernameToken receivedToken = 
270             (UsernameToken) actionResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
271         assertTrue(receivedToken != null);
272         
273         UsernameToken clone = new UsernameToken(receivedToken.getElement());
274         assertTrue(clone.equals(receivedToken));
275         assertTrue(clone.hashCode() == receivedToken.hashCode());
276     }
277     
278     /**
279      * Test that adds a UserNameToken with a digested password but with type of
280      * password test.
281      */
282     @org.junit.Test
283     public void testUsernameTokenDigestText() throws Exception {
284         WSSecUsernameToken builder = new WSSecUsernameToken();
285         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
286         byte[] password = "verySecret".getBytes();
287         MessageDigest sha = MessageDigest.getInstance("MD5");
288         sha.reset();
289         sha.update(password);
290         String passwdDigest = Base64.encode(sha.digest());
291         
292         builder.setUserInfo("wernerd", passwdDigest);
293         LOG.info("Before adding UsernameToken PW Text....");
294         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
295         WSSecHeader secHeader = new WSSecHeader();
296         secHeader.insertSecurityHeader(doc);
297         Document signedDoc = builder.build(doc, secHeader);
298         if (LOG.isDebugEnabled()) {
299             LOG.debug("Message with UserNameToken PW Text:");
300             String outputString = 
301                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
302             LOG.debug(outputString);
303         }
304     }
305     
306     /**
307      * Test that adds a UserNameToken with (bad) password text to a WS-Security envelope
308      */
309     @org.junit.Test
310     public void testUsernameTokenBadText() throws Exception {
311         WSSecUsernameToken builder = new WSSecUsernameToken();
312         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
313         builder.setUserInfo("wernerd", "verySecre");
314         LOG.info("Before adding UsernameToken PW Text....");
315         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
316         WSSecHeader secHeader = new WSSecHeader();
317         secHeader.insertSecurityHeader(doc);
318         Document signedDoc = builder.build(doc, secHeader);
319         if (LOG.isDebugEnabled()) {
320             LOG.debug("Message with UserNameToken PW Text:");
321             String outputString = 
322                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
323             LOG.debug(outputString);
324         }
325         LOG.info("After adding UsernameToken PW Text....");
326         
327         try {
328             verify(signedDoc);
329             fail("Failure expected on a bad password text");
330         } catch (WSSecurityException ex) {
331             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
332             // expected
333         }
334     }
335     
336     /**
337      * Test that adds a UserNameToken with no password type to a WS-Security envelope
338      * See WSS-152 - https://issues.apache.org/jira/browse/WSS-152
339      * "Problem with processing Username Tokens with no password type"
340      * The 1.1 spec states that the password type is optional and defaults to password text, 
341      * and so we should handle an incoming Username Token accordingly.
342      */
343     @org.junit.Test
344     public void testUsernameTokenNoPasswordType() throws Exception {
345         Document doc = SOAPUtil.toSOAPPart(SOAPUTMSG);
346         if (LOG.isDebugEnabled()) {
347             String outputString = 
348                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
349             LOG.debug(outputString);
350         }
351         
352         WSSecurityEngine newEngine = new WSSecurityEngine();
353         try {
354             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
355             fail("Expected failure as it is not BSP compliant");
356         } catch (WSSecurityException ex) {
357             // expected
358         }
359         WSSConfig config = WSSConfig.getNewInstance();
360         config.setWsiBSPCompliant(false);
361         newEngine.setWssConfig(config);
362         newEngine.processSecurityHeader(doc, null, callbackHandler, null);
363     }
364     
365     /**
366      * Test that adds a UserNameToken with no user (or password) to a WS-Security envelope
367      * See WSS-185 - https://issues.apache.org/jira/browse/WSS-185
368      * "NullPointerException on empty UsernameToken"
369      */
370     @org.junit.Test
371     public void testUsernameTokenNoUser() throws Exception {
372         Document doc = SOAPUtil.toSOAPPart(SOAPUTNOUSERMSG);
373         if (LOG.isDebugEnabled()) {
374             String outputString = 
375                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
376             LOG.debug(outputString);
377         }
378         try {
379             verify(doc);
380             fail("Failure expected on no password");
381         } catch (WSSecurityException ex) {
382             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
383             // expected
384         }
385     }
386     
387     /**
388      * Test that adds a UserNameToken with no password
389      */
390     @org.junit.Test
391     public void testUsernameTokenNoPassword() throws Exception {
392         WSSecUsernameToken builder = new WSSecUsernameToken();
393         builder.setPasswordType(null);
394         builder.setUserInfo("nopassuser", null);
395         LOG.info("Before adding UsernameToken with no password....");
396         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
397         WSSecHeader secHeader = new WSSecHeader();
398         secHeader.insertSecurityHeader(doc);
399         Document signedDoc = builder.build(doc, secHeader);
400         if (LOG.isDebugEnabled()) {
401             String outputString = 
402                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
403             LOG.debug(outputString);
404         }
405         
406         List<WSSecurityEngineResult> results = verify(signedDoc);
407         WSSecurityEngineResult actionResult =
408             WSSecurityUtil.fetchActionResult(results, WSConstants.UT_NOPASSWORD);
409         UsernameToken receivedToken = 
410             (UsernameToken) actionResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
411         assertTrue(receivedToken != null);
412     }
413     
414     /**
415      * Test that adds a UserNameToken with an empty password
416      */
417     @org.junit.Test
418     public void testUsernameTokenEmptyPassword() throws Exception {
419         WSSecUsernameToken builder = new WSSecUsernameToken();
420         builder.setPasswordType(WSConstants.PASSWORD_TEXT);
421         builder.setUserInfo("emptyuser", "");
422         LOG.info("Before adding UsernameToken with an empty password....");
423         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
424         WSSecHeader secHeader = new WSSecHeader();
425         secHeader.insertSecurityHeader(doc);
426         Document signedDoc = builder.build(doc, secHeader);
427         if (LOG.isDebugEnabled()) {
428             String outputString = 
429                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
430             LOG.debug(outputString);
431         }
432         secEngine.processSecurityHeader(doc, null, this, null);
433     }
434     
435     /**
436      * Test that processes a UserNameToken with an empty password
437      */
438     @org.junit.Test
439     public void testEmptyPasswordProcessing() throws Exception {
440         Document doc = SOAPUtil.toSOAPPart(EMPTY_PASSWORD_MSG);
441         if (LOG.isDebugEnabled()) {
442             LOG.debug("Empty password message: ");
443             String outputString = 
444                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
445             LOG.debug(outputString);
446         }
447         
448         secEngine.processSecurityHeader(doc, null, this, null);
449     }
450     
451     /**
452      * Test with a non-standard token type. This will fail as the default is to reject custom
453      * token types.
454      */
455     @org.junit.Test
456     public void testUsernameTokenCustomFail() throws Exception {
457         WSSecUsernameToken builder = new WSSecUsernameToken();
458         builder.setPasswordType("RandomType");
459         builder.setUserInfo("wernerd", "verySecret");
460         
461         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
462         WSSecHeader secHeader = new WSSecHeader();
463         secHeader.insertSecurityHeader(doc);
464         Document signedDoc = builder.build(doc, secHeader);
465         
466         if (LOG.isDebugEnabled()) {
467             LOG.debug("Message with UserNameToken PW Text:");
468             String outputString = 
469                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
470             LOG.debug(outputString);
471         }
472         try {
473             secEngine.processSecurityHeader(signedDoc, null, this, null);
474             fail("Custom token types are not permitted");
475         } catch (WSSecurityException ex) {
476             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
477             // expected
478         }
479     }
480     
481     /**
482      * Test with a non-standard password type. This will pass as the WSSConfig is configured to 
483      * handle custom token types.
484      */
485     @org.junit.Test
486     public void testUsernameTokenCustomPass() throws Exception {
487         WSSecUsernameToken builder = new WSSecUsernameToken();
488         builder.setPasswordType("RandomType");
489         builder.setUserInfo("wernerd", "verySecret");
490 
491         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
492         WSSecHeader secHeader = new WSSecHeader();
493         secHeader.insertSecurityHeader(doc);
494         Document signedDoc = builder.build(doc, secHeader);
495         
496         if (LOG.isDebugEnabled()) {
497             LOG.debug("Message with UserNameToken PW custom type:");
498             String outputString = 
499                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
500             LOG.debug(outputString);
501         }
502         
503         //
504         // Configure so that custom token types are accepted
505         //
506         WSSConfig cfg = WSSConfig.getNewInstance();
507         cfg.setHandleCustomPasswordTypes(true);
508         secEngine.setWssConfig(cfg);
509         verify(signedDoc);
510         
511         //
512         // Go back to default for other tests
513         //
514         cfg.setHandleCustomPasswordTypes(false);
515         secEngine.setWssConfig(cfg);
516     }
517     
518     
519     /**
520      * A test for WSS-66 - the nonce string is null
521      * http://issues.apache.org/jira/browse/WSS-66
522      * "Possible security hole when PasswordDigest is used by client."
523      */
524     @org.junit.Test
525     public void testNullNonce() throws Exception {
526         WSSecUsernameToken builder = new WSSecUsernameToken();
527         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
528         builder.setUserInfo("wernerd", "BAD_PASSWORD");
529 
530         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
531         WSSecHeader secHeader = new WSSecHeader();
532         secHeader.insertSecurityHeader(doc);
533         Document utDoc = builder.build(doc, secHeader);
534         
535         //
536         // Manually find the Nonce node and set the content to null
537         //
538         org.w3c.dom.Element elem = builder.getUsernameTokenElement();
539         org.w3c.dom.NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Nonce");
540         org.w3c.dom.Node nonceNode = list.item(0);
541         org.w3c.dom.Node childNode = nonceNode.getFirstChild();
542         childNode.setNodeValue("");
543         
544         if (LOG.isDebugEnabled()) {
545             String outputString = 
546                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(utDoc);
547             LOG.debug(outputString);
548         }
549         
550         try {
551             //
552             // Verification should fail as the password is bad
553             //
554             verify(utDoc);
555             fail("Expected failure due to a bad password");
556         } catch (WSSecurityException ex) {
557             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
558             // expected
559         }
560     }
561     
562     /**
563      * A test for WSS-66 - the created string is null
564      * http://issues.apache.org/jira/browse/WSS-66
565      * "Possible security hole when PasswordDigest is used by client."
566      */
567     @org.junit.Test
568     public void testNullCreated() throws Exception {
569         WSSecUsernameToken builder = new WSSecUsernameToken();
570         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
571         builder.setUserInfo("wernerd", "BAD_PASSWORD");
572 
573         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
574         WSSecHeader secHeader = new WSSecHeader();
575         secHeader.insertSecurityHeader(doc);
576         Document utDoc = builder.build(doc, secHeader);
577         
578         //
579         // Manually find the Created node and set the content to null
580         //
581         org.w3c.dom.Element elem = builder.getUsernameTokenElement();
582         org.w3c.dom.NodeList list = elem.getElementsByTagNameNS(WSConstants.WSU_NS, "Created");
583         org.w3c.dom.Node nonceNode = list.item(0);
584         org.w3c.dom.Node childNode = nonceNode.getFirstChild();
585         childNode.setNodeValue("");
586         
587         if (LOG.isDebugEnabled()) {
588             String outputString = 
589                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(utDoc);
590             LOG.debug(outputString);
591         }
592         
593         try {
594             //
595             // Verification should fail as the password is bad
596             //
597             verify(utDoc);
598             fail("Expected failure due to a bad password");
599         } catch (WSSecurityException ex) {
600             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
601             // expected
602         }
603     }
604     
605     /**
606      * Test that verifies an EncodingType is set for the nonce. See WSS-169.
607      */
608     @org.junit.Test
609     public void testUsernameTokenNonceEncodingType() throws Exception {
610         WSSecUsernameToken builder = new WSSecUsernameToken();
611         builder.setUserInfo("wernerd", "verySecret");
612         LOG.info("Before adding UsernameToken PW Digest....");
613         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
614         WSSecHeader secHeader = new WSSecHeader();
615         secHeader.insertSecurityHeader(doc);
616         Document signedDoc = builder.build(doc, secHeader);
617         String outputString = 
618             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
619         assertTrue(outputString.indexOf("EncodingType") != -1);
620     }
621     
622     /**
623      * Test that adds a UserNameToken via WSHandler
624      */
625     @org.junit.Test
626     public void testUsernameTokenWSHandler() throws Exception {
627         CustomHandler handler = new CustomHandler();
628         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
629         
630         RequestData reqData = new RequestData();
631         java.util.Map<String, Object> config = new java.util.TreeMap<String, Object>();
632         config.put("password", "verySecret");
633         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
634         reqData.setUsername("wernerd");
635         reqData.setMsgContext(config);
636         
637         java.util.List<Integer> actions = new java.util.ArrayList<Integer>();
638         actions.add(Integer.valueOf(WSConstants.UT));
639         
640         handler.send(WSConstants.UT, doc, reqData, actions, true);
641         
642         if (LOG.isDebugEnabled()) {
643             LOG.debug("Username Token via WSHandler");
644             String outputString = 
645                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
646             LOG.debug(outputString);
647         }
648     }
649     
650     /**
651      * Test that adds a UserNameToken with no password via WSHandler
652      */
653     @org.junit.Test
654     public void testUsernameTokenWSHandlerNoPassword() throws Exception {
655         CustomHandler handler = new CustomHandler();
656         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
657         
658         RequestData reqData = new RequestData();
659         java.util.Map<String, Object> config = new java.util.TreeMap<String, Object>();
660         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_NONE);
661         reqData.setUsername("wernerd");
662         reqData.setMsgContext(config);
663         
664         java.util.List<Integer> actions = new java.util.ArrayList<Integer>();
665         actions.add(Integer.valueOf(WSConstants.UT));
666         
667         handler.send(WSConstants.UT, doc, reqData, actions, true);
668         
669         if (LOG.isDebugEnabled()) {
670             LOG.debug("Username Token via WSHandler");
671             String outputString = 
672                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
673             LOG.debug(outputString);
674         }
675     }
676     
677     /**
678      * Test that adds a UserNameToken with an empty password via WSHandler
679      */
680     @org.junit.Test
681     public void testUsernameTokenWSHandlerEmptyPassword() throws Exception {
682         CustomHandler handler = new CustomHandler();
683         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
684         
685         RequestData reqData = new RequestData();
686         java.util.Map<String, Object> config = new java.util.TreeMap<String, Object>();
687         config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
688         config.put(WSHandlerConstants.PW_CALLBACK_REF, this);
689         reqData.setUsername("emptyuser");
690         reqData.setMsgContext(config);
691         
692         java.util.List<Integer> actions = new java.util.ArrayList<Integer>();
693         actions.add(Integer.valueOf(WSConstants.UT));
694         
695         handler.send(WSConstants.UT, doc, reqData, actions, true);
696         
697         if (LOG.isDebugEnabled()) {
698             LOG.debug("Username Token with an empty password via WSHandler");
699             String outputString = 
700                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
701             LOG.debug(outputString);
702         }
703     }
704     
705     /**
706      * A test for sending multiple nonces in the UsernameToken
707      */
708     @org.junit.Test
709     public void testMultipleNonce() throws Exception {
710         WSSecUsernameToken builder = new WSSecUsernameToken();
711         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
712         builder.setUserInfo("wernerd", "verySecret");
713 
714         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
715         WSSecHeader secHeader = new WSSecHeader();
716         secHeader.insertSecurityHeader(doc);
717         Document utDoc = builder.build(doc, secHeader);
718         
719         //
720         // Manually find the Nonce node and duplicate it
721         //
722         org.w3c.dom.Element elem = builder.getUsernameTokenElement();
723         org.w3c.dom.NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Nonce");
724         org.w3c.dom.Node nonceNode = list.item(0);
725         org.w3c.dom.Node nonceCopy = nonceNode.cloneNode(true);
726         nonceNode.getParentNode().insertBefore(nonceCopy, nonceNode);
727         
728         if (LOG.isDebugEnabled()) {
729             String outputString = 
730                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(utDoc);
731             LOG.debug(outputString);
732         }
733         
734         WSSecurityEngine newEngine = new WSSecurityEngine();
735         try {
736             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
737             fail("Expected failure as it is not BSP compliant");
738         } catch (WSSecurityException ex) {
739             // expected
740         }
741         WSSConfig config = WSSConfig.getNewInstance();
742         config.setWsiBSPCompliant(false);
743         newEngine.setWssConfig(config);
744         newEngine.processSecurityHeader(doc, null, callbackHandler, null);
745     }
746     
747     /**
748      * A test for sending multiple Created elements in the UsernameToken
749      */
750     @org.junit.Test
751     public void testMultipleCreated() throws Exception {
752         WSSecUsernameToken builder = new WSSecUsernameToken();
753         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
754         builder.setUserInfo("wernerd", "verySecret");
755 
756         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
757         WSSecHeader secHeader = new WSSecHeader();
758         secHeader.insertSecurityHeader(doc);
759         Document utDoc = builder.build(doc, secHeader);
760         
761         //
762         // Manually find the Created node and duplicate it
763         //
764         org.w3c.dom.Element elem = builder.getUsernameTokenElement();
765         org.w3c.dom.NodeList list = elem.getElementsByTagNameNS(WSConstants.WSU_NS, "Created");
766         org.w3c.dom.Node createdNode = list.item(0);
767         org.w3c.dom.Node createdCopy = createdNode.cloneNode(true);
768         createdNode.getParentNode().insertBefore(createdCopy, createdNode);
769         
770         if (LOG.isDebugEnabled()) {
771             String outputString = 
772                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(utDoc);
773             LOG.debug(outputString);
774         }
775         
776         WSSecurityEngine newEngine = new WSSecurityEngine();
777         try {
778             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
779             fail("Expected failure as it is not BSP compliant");
780         } catch (WSSecurityException ex) {
781             // expected
782         }
783         WSSConfig config = WSSConfig.getNewInstance();
784         config.setWsiBSPCompliant(false);
785         newEngine.setWssConfig(config);
786         newEngine.processSecurityHeader(doc, null, callbackHandler, null);
787     }
788     
789     /**
790      * A test for sending multiple passwords in the UsernameToken
791      */
792     @org.junit.Test
793     public void testMultiplePassword() throws Exception {
794         WSSecUsernameToken builder = new WSSecUsernameToken();
795         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
796         builder.setUserInfo("wernerd", "verySecret");
797 
798         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
799         WSSecHeader secHeader = new WSSecHeader();
800         secHeader.insertSecurityHeader(doc);
801         Document utDoc = builder.build(doc, secHeader);
802         
803         //
804         // Manually find the Nonce node and duplicate it
805         //
806         org.w3c.dom.Element elem = builder.getUsernameTokenElement();
807         org.w3c.dom.NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Password");
808         org.w3c.dom.Node passwordNode = list.item(0);
809         org.w3c.dom.Node passwordCopy = passwordNode.cloneNode(true);
810         passwordNode.getParentNode().insertBefore(passwordCopy, passwordNode);
811         
812         if (LOG.isDebugEnabled()) {
813             String outputString = 
814                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(utDoc);
815             LOG.debug(outputString);
816         }
817         
818         WSSecurityEngine newEngine = new WSSecurityEngine();
819         try {
820             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
821             fail("Expected failure as it is not BSP compliant");
822         } catch (WSSecurityException ex) {
823             // expected
824         }
825         WSSConfig config = WSSConfig.getNewInstance();
826         config.setWsiBSPCompliant(false);
827         newEngine.setWssConfig(config);
828         newEngine.processSecurityHeader(doc, null, callbackHandler, null);
829     }
830     
831     /**
832      * A test for sending a nonce with a bad encoding type in the UsernameToken
833      */
834     @org.junit.Test
835     public void testNonceBadEncodingType() throws Exception {
836         WSSecUsernameToken builder = new WSSecUsernameToken();
837         builder.setPasswordType(WSConstants.PASSWORD_DIGEST);
838         builder.setUserInfo("wernerd", "verySecret");
839 
840         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
841         WSSecHeader secHeader = new WSSecHeader();
842         secHeader.insertSecurityHeader(doc);
843         Document utDoc = builder.build(doc, secHeader);
844         
845         //
846         // Manually find the Nonce node and duplicate it
847         //
848         org.w3c.dom.Element elem = builder.getUsernameTokenElement();
849         org.w3c.dom.NodeList list = elem.getElementsByTagNameNS(WSConstants.WSSE_NS, "Nonce");
850         org.w3c.dom.Node nonceNode = list.item(0);
851         ((org.w3c.dom.Element)nonceNode).setAttributeNS(
852             null, "EncodingType", "http://bad_encoding_type"
853         );
854         
855         if (LOG.isDebugEnabled()) {
856             String outputString = 
857                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(utDoc);
858             LOG.debug(outputString);
859         }
860         
861         WSSecurityEngine newEngine = new WSSecurityEngine();
862         try {
863             newEngine.processSecurityHeader(doc, null, callbackHandler, null);
864             fail("Expected failure as it is not BSP compliant");
865         } catch (WSSecurityException ex) {
866             // expected
867         }
868         WSSConfig config = WSSConfig.getNewInstance();
869         config.setWsiBSPCompliant(false);
870         newEngine.setWssConfig(config);
871         newEngine.processSecurityHeader(doc, null, callbackHandler, null);
872     }
873     
874     /**
875      * Verifies the soap envelope
876      * 
877      * @param env soap envelope
878      * @throws java.lang.Exception Thrown when there is a problem in verification
879      */
880     private List<WSSecurityEngineResult> verify(Document doc) throws Exception {
881         return secEngine.processSecurityHeader(doc, null, callbackHandler, null);
882     }
883     
884     /**
885      * A CallbackHandler for some (mostly insecure) scenarios.
886      */
887     public void handle(Callback[] callbacks)
888         throws IOException, UnsupportedCallbackException {
889         for (int i = 0; i < callbacks.length; i++) {
890             if (callbacks[i] instanceof WSPasswordCallback) {
891                 WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
892                 if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN) {
893                     if ("emptyuser".equals(pc.getIdentifier())) {
894                         pc.setPassword("");
895                     } else if ("customUser".equals(pc.getIdentifier())) {
896                         return;
897                     } else if (null == pc.getIdentifier()) {
898                         // Note that this is not secure! Just doing this to test a NPE
899                         return;
900                     }
901                 }
902             } else {
903                 throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
904             }
905         }
906     }
907 }