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.WSConstants;
23  import org.apache.ws.security.WSSConfig;
24  import org.apache.ws.security.WSSecurityEngineResult;
25  import org.apache.ws.security.WSSecurityException;
26  import org.apache.ws.security.WSSecurityEngine;
27  import org.apache.ws.security.common.EncodedPasswordCallbackHandler;
28  import org.apache.ws.security.common.SOAPUtil;
29  import org.apache.ws.security.common.UsernamePasswordCallbackHandler;
30  import org.apache.ws.security.components.crypto.Crypto;
31  import org.apache.ws.security.components.crypto.CryptoFactory;
32  import org.apache.ws.security.message.token.SecurityTokenReference;
33  import org.apache.ws.security.message.token.UsernameToken;
34  import org.apache.ws.security.util.Base64;
35  import org.apache.ws.security.util.WSSecurityUtil;
36  import org.w3c.dom.Document;
37  
38  import javax.security.auth.callback.CallbackHandler;
39  
40  import java.security.MessageDigest;
41  import java.util.Arrays;
42  import java.util.List;
43  
44  /**
45   * WS-Security Test Case for UsernameToken Key Derivation, as defined in the 
46   * UsernameTokenProfile 1.1 specification. The derived keys are used to encrypt
47   * and sign, as per wsc:DerivedKeyToken.
48   */
49  public class UTDerivedKeyTest extends org.junit.Assert {
50      private static final org.apache.commons.logging.Log LOG = 
51          org.apache.commons.logging.LogFactory.getLog(UTDerivedKeyTest.class);
52      private CallbackHandler callbackHandler = new UsernamePasswordCallbackHandler();
53      private Crypto crypto = null;
54      
55      public UTDerivedKeyTest() throws Exception {
56          crypto = CryptoFactory.getInstance();
57      }
58  
59      /**
60       * Unit test for the UsernameToken derived key functionality 
61       */
62      @org.junit.Test
63      public void testUsernameTokenUnit() throws Exception {
64          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
65          WSSecHeader secHeader = new WSSecHeader();
66          secHeader.insertSecurityHeader(doc);
67          
68          UsernameToken usernameToken = new UsernameToken(true, doc, null);
69          usernameToken.setName("bob");
70          
71          byte[] salt = usernameToken.addSalt(doc, null, false);
72          assertTrue(salt.length == 16);
73          assertTrue(salt[0] == 0x02);
74          byte[] utSalt = usernameToken.getSalt();
75          assertTrue(salt.length == utSalt.length);
76          for (int i = 0; i < salt.length; i++) {
77              assertTrue(salt[i] == utSalt[i]);
78          }
79          
80          usernameToken.addIteration(doc, 500);
81          assertTrue(usernameToken.getIteration() == 500);
82          
83          WSSecurityUtil.prependChildElement(
84              secHeader.getSecurityHeader(), usernameToken.getElement()
85          );
86          
87          String outputString = 
88              org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
89          assertTrue(outputString.indexOf("wsse:Username") != -1);
90          assertTrue(outputString.indexOf("wsse:Password") == -1);
91          assertTrue(outputString.indexOf("wsse11:Salt") != -1);
92          assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
93          
94          byte[] derivedKey = UsernameToken.generateDerivedKey("security", salt, 500);
95          assertTrue(derivedKey.length == 20);
96          
97          // "c2VjdXJpdHk=" is the Base64 encoding of "security"
98          derivedKey = UsernameToken.generateDerivedKey(Base64.decode("c2VjdXJpdHk="), salt, 500);
99          assertTrue(derivedKey.length == 20);
100     }
101 
102     /**
103      * Test for encoded passwords.
104      */
105     @org.junit.Test
106     public void testDerivedKeyWithEncodedPasswordBaseline() throws Exception {
107         String password = "password";
108         // The SHA-1 of the password is known as a password equivalent in the UsernameToken specification.
109         byte[] passwordHash = MessageDigest.getInstance("SHA-1").digest(password.getBytes("UTF-8"));
110 
111         byte[] salt = Base64.decode("LKpycbfgRzwDnBz6kkhAAQ==");
112         int iteration = 1049;
113         byte[] expectedDerivedKey = Base64.decode("C7Ll/OY4TECb6hZuMMiX/5hzszo=");
114         byte[] derivedKey = UsernameToken.generateDerivedKey(passwordHash, salt, iteration);
115         assertTrue("the derived key is not as expected", Arrays.equals(expectedDerivedKey, derivedKey));
116     }
117 
118     /**
119      * Test using a UsernameToken derived key for encrypting a SOAP body
120      */
121     @org.junit.Test
122     public void testDerivedKeyEncryption() throws Exception {
123         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
124         WSSecHeader secHeader = new WSSecHeader();
125         secHeader.insertSecurityHeader(doc);
126         
127         WSSecUsernameToken builder = new WSSecUsernameToken();
128         builder.setUserInfo("bob", "security");
129         builder.addDerivedKey(false, null, 1000);
130         builder.prepare(doc);
131         
132         byte[] derivedKey = builder.getDerivedKey();
133         assertTrue(derivedKey.length == 20);
134         
135         String tokenIdentifier = builder.getId();
136         
137         //
138         // Derived key encryption
139         //
140         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
141         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
142         encrBuilder.setExternalKey(derivedKey, tokenIdentifier);
143         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
144         Document encryptedDoc = encrBuilder.build(doc, secHeader);
145         
146         builder.prependToHeader(secHeader);
147         
148         String outputString = 
149             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
150         assertTrue(outputString.indexOf("wsse:Username") != -1);
151         assertTrue(outputString.indexOf("wsse:Password") == -1);
152         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
153         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
154         assertTrue(outputString.indexOf("testMethod") == -1);
155         if (LOG.isDebugEnabled()) {
156             LOG.debug(outputString);
157         }
158         
159         verify(encryptedDoc);
160     }
161     
162     /**
163      * Test using a UsernameToken derived key for encrypting a SOAP body
164      */
165     @org.junit.Test
166     public void testDerivedKeyEncryptionWithEncodedPassword() throws Exception {
167         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
168         WSSecHeader secHeader = new WSSecHeader();
169         secHeader.insertSecurityHeader(doc);
170         
171         WSSecUsernameToken builder = new WSSecUsernameToken();
172         builder.setPasswordsAreEncoded(true);
173         builder.setUserInfo("bob", Base64.encode(MessageDigest.getInstance("SHA-1").digest("security".getBytes("UTF-8"))));
174         builder.addDerivedKey(false, null, 1000);
175         builder.prepare(doc);
176         
177         byte[] derivedKey = builder.getDerivedKey();
178         assertTrue(derivedKey.length == 20);
179         String tokenIdentifier = builder.getId();
180         
181         //
182         // Derived key encryption
183         //
184         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
185         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
186         encrBuilder.setExternalKey(derivedKey, tokenIdentifier);
187         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
188         Document encryptedDoc = encrBuilder.build(doc, secHeader);
189         
190         builder.prependToHeader(secHeader);
191         
192         String outputString = 
193             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
194         assertTrue(outputString.indexOf("wsse:Username") != -1);
195         assertTrue(outputString.indexOf("wsse:Password") == -1);
196         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
197         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
198         assertTrue(outputString.indexOf("testMethod") == -1);
199         if (LOG.isDebugEnabled()) {
200             LOG.debug(outputString);
201         }
202         
203         WSSecurityEngine newEngine = new WSSecurityEngine();
204         newEngine.getWssConfig().setPasswordsAreEncoded(true);
205         newEngine.processSecurityHeader(
206             encryptedDoc, null, new EncodedPasswordCallbackHandler(), null
207         );
208     }
209     
210     /**
211      * Test using a UsernameToken derived key for encrypting a SOAP body. In this test the
212      * derived key is modified before encryption, and so decryption should fail.
213      */
214     @org.junit.Test
215     public void testDerivedKeyChangedEncryption() throws Exception {
216         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
217         WSSecHeader secHeader = new WSSecHeader();
218         secHeader.insertSecurityHeader(doc);
219         
220         WSSecUsernameToken builder = new WSSecUsernameToken();
221         builder.setUserInfo("bob", "security");
222         builder.addDerivedKey(false, null, 1000);
223         builder.prepare(doc);
224         
225         byte[] derivedKey = builder.getDerivedKey();
226         derivedKey[5] = 'z';
227         derivedKey[6] = 'a';
228         assertTrue(derivedKey.length == 20);
229         
230         String tokenIdentifier = builder.getId();
231         
232         //
233         // Derived key encryption
234         //
235         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
236         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
237         encrBuilder.setExternalKey(derivedKey, tokenIdentifier);
238         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
239         Document encryptedDoc = encrBuilder.build(doc, secHeader);
240         
241         builder.prependToHeader(secHeader);
242         
243         String outputString = 
244             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
245         assertTrue(outputString.indexOf("wsse:Username") != -1);
246         assertTrue(outputString.indexOf("wsse:Password") == -1);
247         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
248         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
249         assertTrue(outputString.indexOf("testMethod") == -1);
250         if (LOG.isDebugEnabled()) {
251             LOG.debug(outputString);
252         }
253         
254         try {
255             verify(encryptedDoc);
256             fail("Failure expected on a bad derived encryption");
257         } catch (WSSecurityException ex) {
258             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_CHECK);
259             // expected
260         }
261     }
262     
263     /**
264      * Test using a UsernameToken derived key for encrypting a SOAP body. In this test the
265      * user is "colm" rather than "bob", and so decryption should fail.
266      */
267     @org.junit.Test
268     public void testDerivedKeyBadUserEncryption() throws Exception {
269         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
270         WSSecHeader secHeader = new WSSecHeader();
271         secHeader.insertSecurityHeader(doc);
272         
273         WSSecUsernameToken builder = new WSSecUsernameToken();
274         builder.setUserInfo("colm", "security");
275         builder.addDerivedKey(false, null, 1000);
276         builder.prepare(doc);
277         
278         byte[] derivedKey = builder.getDerivedKey();
279         assertTrue(derivedKey.length == 20);
280         
281         String tokenIdentifier = builder.getId();
282         
283         //
284         // Derived key encryption
285         //
286         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
287         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
288         encrBuilder.setExternalKey(derivedKey, tokenIdentifier);
289         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
290         Document encryptedDoc = encrBuilder.build(doc, secHeader);
291         
292         builder.prependToHeader(secHeader);
293         
294         String outputString = 
295             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
296         assertTrue(outputString.indexOf("wsse:Username") != -1);
297         assertTrue(outputString.indexOf("wsse:Password") == -1);
298         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
299         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
300         assertTrue(outputString.indexOf("testMethod") == -1);
301         if (LOG.isDebugEnabled()) {
302             LOG.debug(outputString);
303         }
304         
305         try {
306             verify(encryptedDoc);
307             fail("Failure expected on a bad derived encryption");
308         } catch (WSSecurityException ex) {
309             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
310             // expected
311         }
312     }
313     
314     /**
315      * Test using a UsernameToken derived key for signing a SOAP body
316      */
317     @org.junit.Test
318     public void testDerivedKeySignature() throws Exception {
319         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
320         WSSecHeader secHeader = new WSSecHeader();
321         secHeader.insertSecurityHeader(doc);
322         
323         WSSecUsernameToken builder = new WSSecUsernameToken();
324         builder.setUserInfo("bob", "security");
325         builder.addDerivedKey(true, null, 1000);
326         builder.prepare(doc);
327         
328         byte[] derivedKey = builder.getDerivedKey();
329         assertTrue(derivedKey.length == 20);
330         
331         String tokenIdentifier = builder.getId();
332         
333         //
334         // Derived key signature
335         //
336         WSSecDKSign sigBuilder = new WSSecDKSign();
337         sigBuilder.setExternalKey(derivedKey, tokenIdentifier);
338         sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
339         sigBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
340         Document signedDoc = sigBuilder.build(doc, secHeader);
341         
342         builder.prependToHeader(secHeader);
343         
344         String outputString = 
345             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
346         assertTrue(outputString.indexOf("wsse:Username") != -1);
347         assertTrue(outputString.indexOf("wsse:Password") == -1);
348         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
349         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
350         if (LOG.isDebugEnabled()) {
351             LOG.debug(outputString);
352         }
353         
354         List<WSSecurityEngineResult> results = verify(signedDoc);
355         WSSecurityEngineResult actionResult =
356             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
357         java.security.Principal principal = 
358             (java.security.Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
359         // System.out.println(principal.getName());
360         assertTrue(principal.getName().indexOf("DK") != -1);
361     }
362     
363     /**
364      * Test using a UsernameToken derived key for signing a SOAP body
365      */
366     @org.junit.Test
367     public void testDerivedKeySignatureWithEncodedPassword() throws Exception {
368         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
369         WSSecHeader secHeader = new WSSecHeader();
370         secHeader.insertSecurityHeader(doc);
371         
372         WSSecUsernameToken builder = new WSSecUsernameToken();
373         builder.setPasswordsAreEncoded(true);
374         builder.setUserInfo("bob", Base64.encode(MessageDigest.getInstance("SHA-1").digest("security".getBytes("UTF-8"))));
375         builder.addDerivedKey(true, null, 1000);
376         builder.prepare(doc);
377         
378         byte[] derivedKey = builder.getDerivedKey();
379         assertTrue(derivedKey.length == 20);
380         
381         String tokenIdentifier = builder.getId();
382         
383         //
384         // Derived key signature
385         //
386         WSSecDKSign sigBuilder = new WSSecDKSign();
387         sigBuilder.setExternalKey(derivedKey, tokenIdentifier);
388         sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
389         sigBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
390         Document signedDoc = sigBuilder.build(doc, secHeader);
391         
392         builder.prependToHeader(secHeader);
393         
394         String outputString = 
395             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
396         assertTrue(outputString.indexOf("wsse:Username") != -1);
397         assertTrue(outputString.indexOf("wsse:Password") == -1);
398         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
399         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
400         if (LOG.isDebugEnabled()) {
401             LOG.debug(outputString);
402         }
403         
404         WSSecurityEngine newEngine = new WSSecurityEngine();
405         newEngine.getWssConfig().setPasswordsAreEncoded(true);
406         List<WSSecurityEngineResult> results =  newEngine.processSecurityHeader(
407             signedDoc, null, new EncodedPasswordCallbackHandler(), null
408         );
409         WSSecurityEngineResult actionResult =
410             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
411         java.security.Principal principal = 
412             (java.security.Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
413         // System.out.println(principal.getName());
414         assertTrue(principal.getName().indexOf("DK") != -1);
415     }
416     
417     /**
418      * Test using a UsernameToken derived key for signing a SOAP body. In this test the
419      * derived key is modified before signature, and so signature verification should
420      * fail.
421      */
422     @org.junit.Test
423     public void testDerivedKeyChangedSignature() throws Exception {
424         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
425         WSSecHeader secHeader = new WSSecHeader();
426         secHeader.insertSecurityHeader(doc);
427         
428         WSSecUsernameToken builder = new WSSecUsernameToken();
429         builder.setUserInfo("bob", "security");
430         builder.addDerivedKey(true, null, 1000);
431         builder.prepare(doc);
432         
433         byte[] derivedKey = builder.getDerivedKey();
434         derivedKey[5] = 12;
435         assertTrue(derivedKey.length == 20);
436         
437         String tokenIdentifier = builder.getId();
438         
439         //
440         // Derived key signature
441         //
442         WSSecDKSign sigBuilder = new WSSecDKSign();
443         sigBuilder.setExternalKey(derivedKey, tokenIdentifier);
444         sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
445         sigBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
446         Document signedDoc = sigBuilder.build(doc, secHeader);
447         
448         builder.prependToHeader(secHeader);
449         
450         String outputString = 
451             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
452         if (LOG.isDebugEnabled()) {
453             LOG.debug(outputString);
454         }
455 
456         try {
457             verify(signedDoc);
458             fail("Failure expected on a bad derived signature");
459         } catch (WSSecurityException ex) {
460             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_CHECK);
461             // expected
462         }
463     }
464     
465     /**
466      * Test using a UsernameToken derived key for signing a SOAP body. In this test the
467      * user is "colm" rather than "bob", and so signature verification should fail.
468      */
469     @org.junit.Test
470     public void testDerivedKeyBadUserSignature() throws Exception {
471         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
472         WSSecHeader secHeader = new WSSecHeader();
473         secHeader.insertSecurityHeader(doc);
474         
475         WSSecUsernameToken builder = new WSSecUsernameToken();
476         builder.setUserInfo("colm", "security");
477         builder.addDerivedKey(true, null, 1000);
478         builder.prepare(doc);
479         
480         byte[] derivedKey = builder.getDerivedKey();
481         assertTrue(derivedKey.length == 20);
482         
483         String tokenIdentifier = builder.getId();
484         
485         //
486         // Derived key signature
487         //
488         WSSecDKSign sigBuilder = new WSSecDKSign();
489         sigBuilder.setExternalKey(derivedKey, tokenIdentifier);
490         sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
491         sigBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
492         Document signedDoc = sigBuilder.build(doc, secHeader);
493         
494         builder.prependToHeader(secHeader);
495         
496         String outputString = 
497             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
498         if (LOG.isDebugEnabled()) {
499             LOG.debug(outputString);
500         }
501 
502         try {
503             verify(signedDoc);
504             fail("Failure expected on a bad derived signature");
505         } catch (WSSecurityException ex) {
506             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
507             // expected
508         }
509     }
510     
511     /**
512      * Unit test for creating a Username Token with no salt element that is used for
513      * deriving a key for encryption.
514      */
515     @org.junit.Test
516     public void testNoSaltEncryption() throws Exception {
517         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
518         WSSecHeader secHeader = new WSSecHeader();
519         secHeader.insertSecurityHeader(doc);
520         
521         UsernameToken usernameToken = new UsernameToken(true, doc, null);
522         usernameToken.setName("bob");
523         WSSConfig config = WSSConfig.getNewInstance();
524         usernameToken.setID(config.getIdAllocator().createId("UsernameToken-", usernameToken));
525         
526         byte[] salt = UsernameToken.generateSalt(false);
527         usernameToken.addIteration(doc, 1000);
528         
529         byte[] derivedKey = UsernameToken.generateDerivedKey("security", salt, 1000);
530         
531         //
532         // Derived key encryption
533         //
534         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
535         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
536         encrBuilder.setExternalKey(derivedKey, usernameToken.getID());
537         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
538         Document encryptedDoc = encrBuilder.build(doc, secHeader);
539         
540         WSSecurityUtil.prependChildElement(
541             secHeader.getSecurityHeader(), usernameToken.getElement()
542         );
543         
544         String outputString = 
545             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
546         assertTrue(outputString.indexOf("wsse:Username") != -1);
547         assertTrue(outputString.indexOf("wsse:Password") == -1);
548         assertTrue(outputString.indexOf("wsse11:Salt") == -1);
549         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
550         if (LOG.isDebugEnabled()) {
551             LOG.debug(outputString);
552         }
553         
554         try {
555             verify(encryptedDoc);
556             fail("Failure expected on no salt element");
557         } catch (WSSecurityException ex) {
558             // expected
559         }
560     }
561     
562     /**
563      * Unit test for creating a Username Token with no iteration element that is used for
564      * deriving a key for encryption.
565      */
566     @org.junit.Test
567     public void testNoIterationEncryption() throws Exception {
568         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
569         WSSecHeader secHeader = new WSSecHeader();
570         secHeader.insertSecurityHeader(doc);
571         
572         UsernameToken usernameToken = new UsernameToken(true, doc, null);
573         usernameToken.setName("bob");
574         WSSConfig config = WSSConfig.getNewInstance();
575         usernameToken.setID(config.getIdAllocator().createId("UsernameToken-", usernameToken));
576         
577         byte[] salt = usernameToken.addSalt(doc, null, false);
578         byte[] derivedKey = UsernameToken.generateDerivedKey("security", salt, 1000);
579         
580         //
581         // Derived key encryption
582         //
583         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
584         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
585         encrBuilder.setExternalKey(derivedKey, usernameToken.getID());
586         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
587         Document encryptedDoc = encrBuilder.build(doc, secHeader);
588         
589         WSSecurityUtil.prependChildElement(
590             secHeader.getSecurityHeader(), usernameToken.getElement()
591         );
592         
593         String outputString = 
594             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
595         assertTrue(outputString.indexOf("wsse:Username") != -1);
596         assertTrue(outputString.indexOf("wsse:Password") == -1);
597         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
598         assertTrue(outputString.indexOf("wsse11:Iteration") == -1);
599         if (LOG.isDebugEnabled()) {
600             LOG.debug(outputString);
601         }
602         
603         try {
604             verify(encryptedDoc);
605             fail("Failure expected on no iteration element");
606         } catch (WSSecurityException ex) {
607             // expected
608         }
609     }
610     
611     /**
612      * Unit test for creating a Username Token with an iteration value < 1000 that is used for
613      * deriving a key for encryption.
614      */
615     @org.junit.Test
616     public void testLowIterationEncryption() throws Exception {
617         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
618         WSSecHeader secHeader = new WSSecHeader();
619         secHeader.insertSecurityHeader(doc);
620         
621         UsernameToken usernameToken = new UsernameToken(true, doc, null);
622         usernameToken.setName("bob");
623         WSSConfig config = WSSConfig.getNewInstance();
624         usernameToken.setID(config.getIdAllocator().createId("UsernameToken-", usernameToken));
625         
626         usernameToken.addIteration(doc, 500);
627         byte[] salt = usernameToken.addSalt(doc, null, false);
628         byte[] derivedKey = UsernameToken.generateDerivedKey("security", salt, 500);
629         
630         //
631         // Derived key encryption
632         //
633         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
634         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
635         encrBuilder.setExternalKey(derivedKey, usernameToken.getID());
636         encrBuilder.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
637         Document encryptedDoc = encrBuilder.build(doc, secHeader);
638         
639         WSSecurityUtil.prependChildElement(
640             secHeader.getSecurityHeader(), usernameToken.getElement()
641         );
642         
643         String outputString = 
644             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
645         assertTrue(outputString.indexOf("wsse:Username") != -1);
646         assertTrue(outputString.indexOf("wsse:Password") == -1);
647         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
648         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
649         if (LOG.isDebugEnabled()) {
650             LOG.debug(outputString);
651         }
652         
653         try {
654             verify(encryptedDoc);
655             fail("Failure expected on a low iteration value");
656         } catch (WSSecurityException ex) {
657             // expected
658         }
659         
660         // Turn off BSP compliance and it should work
661         config.setWsiBSPCompliant(false);
662         WSSecurityEngine newEngine = new WSSecurityEngine();
663         newEngine.setWssConfig(config);
664         newEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
665     }
666 
667     
668     /**
669      * Test using a UsernameToken derived key for encrypting a SOAP body. The Reference to the
670      * UsernameToken contains a non-standard value type, which is rejected when BSP compliance
671      * is turned on.
672      */
673     @org.junit.Test
674     public void testBadValueType() throws Exception {
675         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
676         WSSecHeader secHeader = new WSSecHeader();
677         secHeader.insertSecurityHeader(doc);
678         
679         WSSecUsernameToken builder = new WSSecUsernameToken();
680         builder.setUserInfo("bob", "security");
681         builder.addDerivedKey(false, null, 1000);
682         builder.prepare(doc);
683         
684         byte[] derivedKey = builder.getDerivedKey();
685         assertTrue(derivedKey.length == 20);
686         
687         String tokenIdentifier = builder.getId();
688         
689         //
690         // Derived key encryption
691         //
692         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
693         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
694         encrBuilder.setExternalKey(derivedKey, tokenIdentifier);
695         encrBuilder.setCustomValueType(WSConstants.WSS_SAML_TOKEN_TYPE);
696         Document encryptedDoc = encrBuilder.build(doc, secHeader);
697         
698         builder.prependToHeader(secHeader);
699         
700         String outputString = 
701             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
702         assertTrue(outputString.indexOf("wsse:Username") != -1);
703         assertTrue(outputString.indexOf("wsse:Password") == -1);
704         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
705         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
706         assertTrue(outputString.indexOf("testMethod") == -1);
707         if (LOG.isDebugEnabled()) {
708             LOG.debug(outputString);
709         }
710         
711         try {
712             verify(encryptedDoc);
713             fail("Failure expected on a bad value type");
714         } catch (WSSecurityException ex) {
715             // expected
716         }
717         
718         // Turn off BSP compliance and it should work
719         WSSConfig config = WSSConfig.getNewInstance();
720         config.setWsiBSPCompliant(false);
721         WSSecurityEngine newEngine = new WSSecurityEngine();
722         newEngine.setWssConfig(config);
723         newEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
724     }
725 
726     
727     /**
728      * Test using a UsernameToken derived key for encrypting a SOAP body. A KeyIdentifier is
729      * used to refer to the UsernameToken, which is forbidden by the BSP.
730      */
731     @org.junit.Test
732     public void testKeyIdentifier() throws Exception {
733         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
734         WSSecHeader secHeader = new WSSecHeader();
735         secHeader.insertSecurityHeader(doc);
736         
737         WSSecUsernameToken builder = new WSSecUsernameToken();
738         builder.setUserInfo("bob", "security");
739         builder.addDerivedKey(false, null, 1000);
740         builder.prepare(doc);
741         
742         byte[] derivedKey = builder.getDerivedKey();
743         assertTrue(derivedKey.length == 20);
744         
745         String tokenIdentifier = builder.getId();
746         
747         //
748         // Derived key encryption
749         //
750         WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
751         encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
752         
753         SecurityTokenReference strEncKey = new SecurityTokenReference(doc);
754         strEncKey.setKeyIdentifier(
755             WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE, tokenIdentifier, true
756         );
757         encrBuilder.setExternalKey(derivedKey, strEncKey.getElement());
758         
759         Document encryptedDoc = encrBuilder.build(doc, secHeader);
760         
761         builder.prependToHeader(secHeader);
762         
763         String outputString = 
764             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
765         assertTrue(outputString.indexOf("wsse:Username") != -1);
766         assertTrue(outputString.indexOf("wsse:Password") == -1);
767         assertTrue(outputString.indexOf("wsse11:Salt") != -1);
768         assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
769         assertTrue(outputString.indexOf("testMethod") == -1);
770         if (LOG.isDebugEnabled()) {
771             LOG.debug(outputString);
772         }
773         
774         try {
775             verify(encryptedDoc);
776             fail("Failure expected on a key identifier");
777         } catch (WSSecurityException ex) {
778             // expected
779         }
780         
781         // Turn off BSP compliance and it should work
782         WSSConfig config = WSSConfig.getNewInstance();
783         config.setWsiBSPCompliant(false);
784         WSSecurityEngine newEngine = new WSSecurityEngine();
785         newEngine.setWssConfig(config);
786         newEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
787     }
788 
789     
790     /**
791      * Verifies the soap envelope.
792      * 
793      * @param env soap envelope
794      * @throws java.lang.Exception Thrown when there is a problem in verification
795      */
796     private List<WSSecurityEngineResult> verify(Document doc) throws Exception {
797         WSSecurityEngine secEngine = new WSSecurityEngine();
798         return secEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
799     }
800 
801 }