You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ws.apache.org by co...@apache.org on 2010/11/09 12:43:11 UTC

svn commit: r1032939 - in /webservices/wss4j/branches/1_5_x-fixes: src/org/apache/ws/security/ src/org/apache/ws/security/action/ src/org/apache/ws/security/handler/ src/org/apache/ws/security/message/ src/org/apache/ws/security/message/token/ src/org/...

Author: coheigea
Date: Tue Nov  9 11:43:11 2010
New Revision: 1032939

URL: http://svn.apache.org/viewvc?rev=1032939&view=rev
Log:
[WSS-239] - Need ability to handle password "equivalent" between WSPasswordCallback and UsernameToken when it's binary data

Modified:
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/WSSConfig.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/action/UsernameTokenAction.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandler.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandlerConstants.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecUsernameToken.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/token/UsernameToken.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
    webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew5.java
    webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityUTDK.java

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/WSSConfig.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/WSSConfig.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/WSSConfig.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/WSSConfig.java Tue Nov  9 11:43:11 2010
@@ -212,6 +212,18 @@ public class WSSConfig {
      */
     protected int secretKeyLength = WSConstants.WSE_DERIVED_KEY_LEN;
     
+    /**
+     * Whether the password should be treated as a binary value.  This
+     * is needed to properly handle password equivalence for UsernameToken
+     * passwords.  Binary passwords are Base64 encoded so they can be
+     * treated as strings in most places, but when the password digest
+     * is calculated or a key is derived from the password, the password
+     * will be Base64 decoded before being used. This is most useful for
+     * hashed passwords as password equivalents.
+     *
+     * See https://issues.apache.org/jira/browse/WSS-239
+     */
+    protected boolean passwordsAreEncoded = false;
     
     /**
      * The default wsu:Id allocator is a simple "start at 1 and increment up"
@@ -446,6 +458,21 @@ public class WSSConfig {
     }
     
     /**
+     * @param passwordsAreEncoded
+     * whether passwords are encoded
+     */
+    public void setPasswordsAreEncoded(boolean passwordsAreEncoded) {
+        this.passwordsAreEncoded = passwordsAreEncoded;
+    }
+    
+    /**
+     * @return whether passwords are encoded
+     */
+    public boolean getPasswordsAreEncoded() {
+        return passwordsAreEncoded;
+    }
+    
+    /**
      * @return Returns the WsuIdAllocator used to generate wsu:Id attributes
      */
     public WsuIdAllocator getIdAllocator() {

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/action/UsernameTokenAction.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/action/UsernameTokenAction.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/action/UsernameTokenAction.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/action/UsernameTokenAction.java Tue Nov  9 11:43:11 2010
@@ -43,6 +43,7 @@ public class UsernameTokenAction impleme
         WSSecUsernameToken builder = new WSSecUsernameToken();
         builder.setWsConfig(reqData.getWssConfig());
         builder.setPasswordType(reqData.getPwType());
+        builder.setPasswordsAreEncoded(reqData.getWssConfig().getPasswordsAreEncoded());
         builder.setUserInfo(providedUsername, password);
 
         if (reqData.getUtElements() != null && reqData.getUtElements().length > 0) {

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandler.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandler.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandler.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandler.java Tue Nov  9 11:43:11 2010
@@ -100,6 +100,7 @@ public abstract class WSHandler {
         wssConfig.setEnableSignatureConfirmation(
             enableSigConf || ((doAction & WSConstants.SC) != 0)
         );
+        wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(reqData));
 
         wssConfig.setPrecisionInMilliSeconds(
             decodeTimestampPrecision(reqData)
@@ -263,6 +264,7 @@ public abstract class WSHandler {
         );
         wssConfig.setTimeStampStrict(decodeTimestampStrict(reqData));
         wssConfig.setHandleCustomPasswordTypes(decodeCustomPasswordTypes(reqData));
+        wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(reqData));
         wssConfig.setAllowNamespaceQualifiedPasswordTypes(
             decodeNamespaceQualifiedPasswordTypes(reqData)
         );
@@ -763,6 +765,28 @@ public abstract class WSHandler {
         );
     }
     
+    protected boolean decodeUseEncodedPasswords(RequestData reqData) 
+        throws WSSecurityException {
+        String value = getString(
+            WSHandlerConstants.USE_ENCODED_PASSWORDS,
+            reqData.getMsgContext()
+        );
+    
+        if (value == null) {
+            return false;
+        }
+        if ("0".equals(value) || "false".equals(value)) {
+            return false;
+        } 
+        if ("1".equals(value) || "true".equals(value)) {
+            return true;
+        }
+    
+        throw new WSSecurityException(
+            "WSHandler: illegal useEncodedPasswords parameter"
+        );
+    }
+    
     protected boolean decodeNamespaceQualifiedPasswordTypes(RequestData reqData) 
         throws WSSecurityException {
         String value = getString(

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandlerConstants.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandlerConstants.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandlerConstants.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/handler/WSHandlerConstants.java Tue Nov  9 11:43:11 2010
@@ -800,6 +800,18 @@ public class WSHandlerConstants {
     public static final String TIMESTAMP_STRICT = "timestampStrict";
     
     /**
+     * Set the value of this parameter to true to treat passwords as binary values
+     * for Username Tokens.
+     * 
+     * This is needed to properly handle password equivalence for UsernameToken
+     * passwords.  Binary passwords are Base64 encoded so they can be treated as 
+     * strings in most places, but when the password digest is calculated or a key
+     * is derived from the password, the password will be Base64 decoded before 
+     * being used. This is most useful for hashed passwords as password equivalents.
+     */
+    public static final String USE_ENCODED_PASSWORDS = "useEncodedPasswords";
+    
+    /**
      * Define the parameter values to set the key identifier types. These are:
      * <ul>
      * <li><code>DirectReference</code> for {@link WSConstants#BST_DIRECT_REFERENCE}

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecUsernameToken.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecUsernameToken.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecUsernameToken.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecUsernameToken.java Tue Nov  9 11:43:11 2010
@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFac
 import org.apache.ws.security.WSConstants;
 import org.apache.ws.security.WSSecurityException;
 import org.apache.ws.security.message.token.UsernameToken;
+import org.apache.ws.security.util.Base64;
 import org.apache.ws.security.util.WSSecurityUtil;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -48,6 +49,7 @@ public class WSSecUsernameToken extends 
     private byte[] saltValue;
     private int iteration = UsernameToken.DEFAULT_ITERATION;
     private int secretKeyLength = WSConstants.WSE_DERIVED_KEY_LEN;
+    private boolean passwordsAreEncoded = false;
 
     /**
      * Constructor.
@@ -130,7 +132,11 @@ public class WSSecUsernameToken extends 
             return null;
         }
         if (useDerivedKey) {
-            return UsernameToken.generateDerivedKey(password, saltValue, iteration);
+            if (passwordsAreEncoded) {
+                return UsernameToken.generateDerivedKey(Base64.decode(password), saltValue, iteration);
+            } else {
+                return UsernameToken.generateDerivedKey(password, saltValue, iteration);
+            }
         }
         return ut.getSecretKey(secretKeyLength);
     }
@@ -149,7 +155,26 @@ public class WSSecUsernameToken extends 
         if (ut == null || !useDerivedKey) {
             return null;
         }
-        return UsernameToken.generateDerivedKey(password, saltValue, iteration);
+        if (passwordsAreEncoded) {
+            return UsernameToken.generateDerivedKey(Base64.decode(password), saltValue, iteration);
+        } else {
+            return UsernameToken.generateDerivedKey(password, saltValue, iteration);
+        }
+    }
+
+    /**
+     * @param passwordsAreEncoded
+     * whether passwords are encoded
+     */
+    public void setPasswordsAreEncoded(boolean passwordsAreEncoded) {
+        this.passwordsAreEncoded = passwordsAreEncoded;
+    }
+
+    /**
+     * @return whether passwords are encoded
+     */
+    public boolean getPasswordsAreEncoded() {
+        return passwordsAreEncoded;
     }
 
     /**
@@ -180,6 +205,7 @@ public class WSSecUsernameToken extends 
      */
     public void prepare(Document doc) {
         ut = new UsernameToken(wssConfig.isPrecisionInMilliSeconds(), doc, passwordType);
+        ut.setPasswordsAreEncoded(passwordsAreEncoded);
         ut.setName(user);
         if (useDerivedKey) {
             saltValue = ut.addSalt(doc, saltValue, useMac);

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/token/UsernameToken.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/token/UsernameToken.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/token/UsernameToken.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/token/UsernameToken.java Tue Nov  9 11:43:11 2010
@@ -73,6 +73,7 @@ public class UsernameToken {
     protected String passwordType = null;
     protected boolean hashed = true;
     private String rawPassword;        // enhancement by Alberto Coletti
+    private boolean passwordsAreEncoded = false;
     
     static {
         try {
@@ -453,7 +454,11 @@ public class UsernameToken {
                 node.setData(pwd);
                 elementPassword.setAttributeNS(null, "Type", WSConstants.PASSWORD_TEXT);
             } else {
-                node.setData(doPasswordDigest(getNonce(), getCreated(), pwd));
+                if (passwordsAreEncoded) {
+                    node.setData(doPasswordDigest(getNonce(), getCreated(), Base64.decode(pwd)));
+                } else {
+                    node.setData(doPasswordDigest(getNonce(), getCreated(), pwd));
+                }
                 elementPassword.setAttributeNS(null, "Type", WSConstants.PASSWORD_DIGEST);
             }
         } catch (Exception e) {
@@ -479,12 +484,26 @@ public class UsernameToken {
         return rawPassword;
     }
     
-    public static String doPasswordDigest(String nonce, String created, String password) {
+    /**
+     * @param passwordsAreEncoded whether passwords are encoded
+     */
+    public void setPasswordsAreEncoded(boolean passwordsAreEncoded) {
+        this.passwordsAreEncoded = passwordsAreEncoded;
+    }
+    
+    /**
+     * @return whether passwords are encoded
+     */
+    public boolean getPasswordsAreEncoded() {
+        return passwordsAreEncoded;
+    }
+    
+    public static String doPasswordDigest(String nonce, String created, byte[] password) {
         String passwdDigest = null;
         try {
             byte[] b1 = nonce != null ? Base64.decode(nonce) : new byte[0];
             byte[] b2 = created != null ? created.getBytes("UTF-8") : new byte[0];
-            byte[] b3 = password.getBytes("UTF-8");
+            byte[] b3 = password;
             byte[] b4 = new byte[b1.length + b2.length + b3.length];
             int offset = 0;
             System.arraycopy(b1, 0, b4, offset, b1.length);
@@ -507,6 +526,18 @@ public class UsernameToken {
         return passwdDigest;
     }
 
+    public static String doPasswordDigest(String nonce, String created, String password) {
+        String passwdDigest = null;
+        try {
+            passwdDigest = doPasswordDigest(nonce, created, password.getBytes("UTF-8"));
+        } catch (Exception e) {
+            if (DO_DEBUG) {
+                LOG.debug(e.getMessage(), e);
+            }
+        }
+        return passwdDigest;
+    }
+
     /**
      * Returns the first text node of an element.
      * 
@@ -613,7 +644,12 @@ public class UsernameToken {
         byte[] key = null;
         try {
             Mac mac = Mac.getInstance("HMACSHA1");
-            byte[] password = rawPassword.getBytes("UTF-8"); // enhancement by Alberto Coletti
+            byte[] password;
+            if (passwordsAreEncoded) {
+                password = Base64.decode(rawPassword);
+            } else {
+                password = rawPassword.getBytes("UTF-8"); // enhancement by Alberto Coletti
+            }
             byte[] label = labelString.getBytes("UTF-8");
             byte[] nonce = Base64.decode(getNonce());
             byte[] created = getCreated().getBytes("UTF-8");
@@ -647,7 +683,7 @@ public class UsernameToken {
         return key;
     }
     
-  
+    
     /**
      * This static method generates a derived key as defined in WSS Username
      * Token Profile.
@@ -660,26 +696,17 @@ public class UsernameToken {
      * @throws WSSecurityException
      */
     public static byte[] generateDerivedKey(
-        String password, 
+        byte[] password, 
         byte[] salt, 
         int iteration
     ) throws WSSecurityException {
         if (iteration == 0) {
             iteration = DEFAULT_ITERATION;
         }
-        byte[] pwBytes = null;
-        try {
-            pwBytes = password.getBytes("UTF-8");
-        } catch (final java.io.UnsupportedEncodingException e) {
-            if (DO_DEBUG) {
-                LOG.debug(e.getMessage(), e);
-            }
-            throw new WSSecurityException("Unable to convert password to UTF-8", e);
-        }
 
-        byte[] pwSalt = new byte[salt.length + pwBytes.length];
-        System.arraycopy(pwBytes, 0, pwSalt, 0, pwBytes.length);
-        System.arraycopy(salt, 0, pwSalt, pwBytes.length, salt.length);
+        byte[] pwSalt = new byte[salt.length + password.length];
+        System.arraycopy(password, 0, pwSalt, 0, password.length);
+        System.arraycopy(salt, 0, pwSalt, password.length, salt.length);
 
         MessageDigest sha = null;
         try {
@@ -707,6 +734,32 @@ public class UsernameToken {
         return K;
     }
     
+    /**
+     * This static method generates a derived key as defined in WSS Username
+     * Token Profile.
+     * 
+     * @param password The password to include in the key generation
+     * @param salt The Salt value
+     * @param iteration The Iteration value. If zero (0) is given the method uses the
+     *                  default value
+     * @return Returns the derived key a byte array
+     * @throws WSSecurityException
+     */
+    public static byte[] generateDerivedKey(
+        String password, 
+        byte[] salt, 
+        int iteration
+    ) throws WSSecurityException {
+        try {
+            return generateDerivedKey(password.getBytes("UTF-8"), salt, iteration);
+        } catch (final java.io.UnsupportedEncodingException e) {
+            if (DO_DEBUG) {
+                LOG.debug(e.getMessage(), e);
+            }
+            throw new WSSecurityException("Unable to convert password to UTF-8", e);
+        }
+    }
+    
     
     /**
      * This method gets a derived key as defined in WSS Username Token Profile.
@@ -717,7 +770,11 @@ public class UsernameToken {
     public byte[] getDerivedKey() throws WSSecurityException {
         int iteration = getIteration();
         byte[] salt = getSalt();
-        return generateDerivedKey(rawPassword, salt, iteration);
+        if (passwordsAreEncoded) {
+            return generateDerivedKey(Base64.decode(rawPassword), salt, iteration);
+        } else {
+            return generateDerivedKey(rawPassword, salt, iteration);
+        }
     }
     
     /**

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/UsernameTokenProcessor.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/UsernameTokenProcessor.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/UsernameTokenProcessor.java Tue Nov  9 11:43:11 2010
@@ -30,6 +30,7 @@ import org.apache.ws.security.WSSecurity
 import org.apache.ws.security.WSUsernameTokenPrincipal;
 import org.apache.ws.security.components.crypto.Crypto;
 import org.apache.ws.security.message.token.UsernameToken;
+import org.apache.ws.security.util.Base64;
 import org.w3c.dom.Element;
 
 import javax.security.auth.callback.Callback;
@@ -46,6 +47,7 @@ public class UsernameTokenProcessor impl
     private UsernameToken ut;
     private boolean handleCustomPasswordTypes;
     private boolean allowNamespaceQualifiedPasswordTypes;
+    private boolean passwordsAreEncoded;
     
     public void handleToken(Element elem, Crypto crypto, Crypto decCrypto, CallbackHandler cb, 
         WSDocInfo wsDocInfo, Vector returnResults, WSSConfig wsc) throws WSSecurityException {
@@ -54,6 +56,7 @@ public class UsernameTokenProcessor impl
         }
         handleCustomPasswordTypes = wsc.getHandleCustomPasswordTypes();
         allowNamespaceQualifiedPasswordTypes = wsc.getAllowNamespaceQualifiedPasswordTypes();
+        passwordsAreEncoded = wsc.getPasswordsAreEncoded();
         
         Principal lastPrincipalFound = handleUsernameToken((Element) elem, cb);
         returnResults.add(
@@ -91,6 +94,7 @@ public class UsernameTokenProcessor impl
         // Parse the UsernameToken element
         //
         ut = new UsernameToken(token, allowNamespaceQualifiedPasswordTypes);
+        ut.setPasswordsAreEncoded(passwordsAreEncoded);
         String user = ut.getName();
         String password = ut.getPassword();
         String nonce = ut.getNonce();
@@ -140,7 +144,12 @@ public class UsernameTokenProcessor impl
                 }
                 throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             }
-            String passDigest = UsernameToken.doPasswordDigest(nonce, createdTime, origPassword);
+            String passDigest;
+            if (passwordsAreEncoded) {
+                passDigest = UsernameToken.doPasswordDigest(nonce, createdTime, Base64.decode(origPassword));
+            } else {
+                passDigest = UsernameToken.doPasswordDigest(nonce, createdTime, origPassword);
+            }
             if (!passDigest.equals(password)) {
                 throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             }
@@ -210,6 +219,10 @@ public class UsernameTokenProcessor impl
         }
         byte[] saltValue = ut.getSalt();
         int iteration = ut.getIteration();
-        return UsernameToken.generateDerivedKey(password, saltValue, iteration);
+        if (passwordsAreEncoded) {
+            return UsernameToken.generateDerivedKey(Base64.decode(password), saltValue, iteration);
+        } else {
+            return UsernameToken.generateDerivedKey(password, saltValue, iteration);
+        }
     }
 }

Modified: webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew5.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew5.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew5.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew5.java Tue Nov  9 11:43:11 2010
@@ -38,6 +38,7 @@ import org.apache.ws.security.handler.Re
 import org.apache.ws.security.handler.WSHandlerConstants;
 import org.apache.ws.security.message.WSSecUsernameToken;
 import org.apache.ws.security.message.WSSecHeader;
+import org.apache.ws.security.message.token.UsernameToken;
 import org.apache.ws.security.util.Base64;
 import org.w3c.dom.Document;
 
@@ -48,6 +49,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.MessageDigest;
+import java.util.Arrays;
 
 
 /**
@@ -174,6 +176,51 @@ public class TestWSSecurityNew5 extends 
     }
     
     /**
+     * Test for encoded passwords.
+     */
+    public void testUsernameTokenWithEncodedPasswordBaseline() throws Exception {
+        String password = "password";
+        // The SHA-1 of the password is known as a password equivalent in the UsernameToken specification.
+        byte[] passwordHash = MessageDigest.getInstance("SHA-1").digest(password.getBytes("UTF-8"));
+
+        String nonce = "0x7bXAPZVn40AdCD0Xbt0g==";
+        String created = "2010-06-28T15:16:37Z";
+        String expectedPasswordDigest = "C0rena/6gKpRZ9ATj+e6ss5sAbQ=";
+        String actualPasswordDigest = UsernameToken.doPasswordDigest(nonce, created, passwordHash);
+        assertEquals("the password digest is not as expected", expectedPasswordDigest, actualPasswordDigest);
+    }
+    
+    /**
+     * Test that adds a UserNameToken with password Digest to a WS-Security envelope
+     */
+    public void testUsernameTokenWithEncodedPassword() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordsAreEncoded(true);
+        builder.setUserInfo("wernerd", Base64.encode(MessageDigest.getInstance("SHA-1").digest("verySecret".getBytes("UTF-8"))));
+        LOG.info("Before adding UsernameToken PW Digest....");
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Message with UserNameToken PW Digest:");
+            String outputString = 
+                org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
+            LOG.debug(outputString);
+        }
+        LOG.info("After adding UsernameToken PW Digest....");
+
+        boolean passwordsAreEnabledOrig = WSSecurityEngine.getInstance().getWssConfig().getPasswordsAreEncoded();
+        try {
+            WSSecurityEngine.getInstance().getWssConfig().setPasswordsAreEncoded(true);
+            verify(signedDoc);
+        } finally {
+            WSSecurityEngine.getInstance().getWssConfig().setPasswordsAreEncoded(passwordsAreEnabledOrig);
+        }
+    }
+    
+    /**
      * Test that a bad username with password digest does not leak whether the username
      * is valid or not - see WSS-141.
      */
@@ -633,26 +680,53 @@ public class TestWSSecurityNew5 extends 
         for (int i = 0; i < callbacks.length; i++) {
             if (callbacks[i] instanceof WSPasswordCallback) {
                 WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
+                boolean passwordsAreEncoded = WSSecurityEngine.getInstance().getWssConfig().getPasswordsAreEncoded();
                 if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN
                     && "wernerd".equals(pc.getIdentifier())) {
-                    pc.setPassword("verySecret");
+                    if (passwordsAreEncoded) {
+                        // "hGqoUreBgahTJblQ3DbJIkE6uNs=" is the Base64 encoded SHA-1 hash of "verySecret".
+                        pc.setPassword("hGqoUreBgahTJblQ3DbJIkE6uNs=");
+                    } else {
+                        pc.setPassword("verySecret");
+                    }
                 } else if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN
                     && "emptyuser".equals(pc.getIdentifier())) {
-                    pc.setPassword("");
+                    if (passwordsAreEncoded) {
+                        // "2jmj7l5rSw0yVb/vlWAYkK/YBwk=" is the Base64 encoded SHA-1 hash of "".
+                        pc.setPassword("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
+                    } else {
+                        pc.setPassword("");
+                    }
                 }  
                 else if (
                     pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN
                 ) {
-                    if ("wernerd".equals(pc.getIdentifier())
-                        && "verySecret".equals(pc.getPassword())) {
-                        return;
-                    } else if ("customUser".equals(pc.getIdentifier())) {
-                        return;
-                    } else if ("wernerd".equals(pc.getIdentifier())
-                            && "".equals(pc.getPassword())) {
-                        return;
+                    if (passwordsAreEncoded) {
+                        if ("wernerd".equals(pc.getIdentifier())
+                            && "hGqoUreBgahTJblQ3DbJIkE6uNs=".equals(pc.getPassword())) {
+                            // "hGqoUreBgahTJblQ3DbJIkE6uNs=" is the Base64 encoded SHA-1 hash of "verySecret".
+                            return;
+                        } else if ("customUser".equals(pc.getIdentifier())) {
+                            return;
+                        } else if ("wernerd".equals(pc.getIdentifier())
+                                && "2jmj7l5rSw0yVb/vlWAYkK/YBwk=".equals(pc.getPassword())) {
+                            // "2jmj7l5rSw0yVb/vlWAYkK/YBwk=" is the Base64 encoded SHA-1 hash of "".
+                            return;
+                        } else {
+                            throw new IOException("Authentication failed");
+                        }
                     } else {
-                        throw new IOException("Authentication failed");
+                        if ("wernerd".equals(pc.getIdentifier())
+                            && "verySecret".equals(pc.getPassword())) {
+                            return;
+                        } else if ("customUser".equals(pc.getIdentifier())) {
+                            return;
+                        } else if ("wernerd".equals(pc.getIdentifier())
+                                && "".equals(pc.getPassword())) {
+                            return;
+                        } else {
+                            throw new IOException("Authentication failed");
+                        }
                     }
                 }
             } else {

Modified: webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityUTDK.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityUTDK.java?rev=1032939&r1=1032938&r2=1032939&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityUTDK.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityUTDK.java Tue Nov  9 11:43:11 2010
@@ -41,6 +41,7 @@ import org.apache.ws.security.message.WS
 import org.apache.ws.security.message.WSSecHeader;
 import org.apache.ws.security.message.WSSecUsernameToken;
 import org.apache.ws.security.message.token.UsernameToken;
+import org.apache.ws.security.util.Base64;
 import org.apache.ws.security.util.WSSecurityUtil;
 import org.apache.xml.security.signature.XMLSignature;
 import org.w3c.dom.Document;
@@ -53,6 +54,8 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import java.security.MessageDigest;
+import java.util.Arrays;
 import java.util.Vector;
 
 /**
@@ -163,8 +166,25 @@ public class TestWSSecurityUTDK extends 
         byte[] derivedKey = UsernameToken.generateDerivedKey("security", salt, 500);
         assertTrue(derivedKey.length == 20);
         
+        // "c2VjdXJpdHk=" is the Base64 encoding of "security"
+        derivedKey = UsernameToken.generateDerivedKey(Base64.decode("c2VjdXJpdHk="), salt, 500);
+        assertTrue(derivedKey.length == 20);
+    }
+
+    /**
+     * Test for encoded passwords.
+     */
+    public void testDerivedKeyWithEncodedPasswordBaseline() throws Exception {
+        String password = "password";
+        // The SHA-1 of the password is known as a password equivalent in the UsernameToken specification.
+        byte[] passwordHash = MessageDigest.getInstance("SHA-1").digest(password.getBytes("UTF-8"));
+
+        byte[] salt = Base64.decode("LKpycbfgRzwDnBz6kkhAAQ==");
+        int iteration = 1049;
+        byte[] expectedDerivedKey = Base64.decode("C7Ll/OY4TECb6hZuMMiX/5hzszo=");
+        byte[] derivedKey = UsernameToken.generateDerivedKey(passwordHash, salt, iteration);
+        assertTrue("the derived key is not as expected", Arrays.equals(expectedDerivedKey, derivedKey));
     }
-    
 
     /**
      * Test using a UsernameToken derived key for encrypting a SOAP body
@@ -209,6 +229,55 @@ public class TestWSSecurityUTDK extends 
     }
     
     /**
+     * Test using a UsernameToken derived key for encrypting a SOAP body
+     */
+    public void testDerivedKeyEncryptionWithEncodedPassword() throws Exception {
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordsAreEncoded(true);
+        builder.setUserInfo("bob", Base64.encode(MessageDigest.getInstance("SHA-1").digest("security".getBytes("UTF-8"))));
+        builder.addDerivedKey(false, null, 1000);
+        builder.prepare(doc);
+        
+        byte[] derivedKey = builder.getDerivedKey();
+        assertTrue(derivedKey.length == 20);
+        
+        String tokenIdentifier = builder.getId();
+        
+        //
+        // Derived key encryption
+        //
+        WSSecDKEncrypt encrBuilder = new WSSecDKEncrypt();
+        encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_128);
+        encrBuilder.setExternalKey(derivedKey, tokenIdentifier);
+        Document encryptedDoc = encrBuilder.build(doc, secHeader);
+        
+        builder.prependToHeader(secHeader);
+        
+        String outputString = 
+            org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
+        assertTrue(outputString.indexOf("wsse:Username") != -1);
+        assertTrue(outputString.indexOf("wsse:Password") == -1);
+        assertTrue(outputString.indexOf("wsse11:Salt") != -1);
+        assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
+        assertTrue(outputString.indexOf("testMethod") == -1);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug(outputString);
+        }
+        
+        boolean passwordsAreEnabledOrig = WSSecurityEngine.getInstance().getWssConfig().getPasswordsAreEncoded();
+        try {
+            WSSecurityEngine.getInstance().getWssConfig().setPasswordsAreEncoded(true);
+            verify(encryptedDoc);
+        } finally {
+            WSSecurityEngine.getInstance().getWssConfig().setPasswordsAreEncoded(passwordsAreEnabledOrig);
+        }
+    }
+    
+    /**
      * Test using a UsernameToken derived key for encrypting a SOAP body. In this test the
      * derived key is modified before encryption, and so decryption should fail.
      */
@@ -355,6 +424,60 @@ public class TestWSSecurityUTDK extends 
     }
     
     /**
+     * Test using a UsernameToken derived key for signing a SOAP body
+     */
+    public void testDerivedKeySignatureWithEncodedPassword() throws Exception {
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordsAreEncoded(true);
+        builder.setUserInfo("bob", Base64.encode(MessageDigest.getInstance("SHA-1").digest("security".getBytes("UTF-8"))));
+        builder.addDerivedKey(true, null, 1000);
+        builder.prepare(doc);
+        
+        byte[] derivedKey = builder.getDerivedKey();
+        assertTrue(derivedKey.length == 20);
+        
+        String tokenIdentifier = builder.getId();
+        
+        //
+        // Derived key signature
+        //
+        WSSecDKSign sigBuilder = new WSSecDKSign();
+        sigBuilder.setExternalKey(derivedKey, tokenIdentifier);
+        sigBuilder.setSignatureAlgorithm(XMLSignature.ALGO_ID_MAC_HMAC_SHA1);
+        Document signedDoc = sigBuilder.build(doc, secHeader);
+        
+        builder.prependToHeader(secHeader);
+        
+        String outputString = 
+            org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
+        assertTrue(outputString.indexOf("wsse:Username") != -1);
+        assertTrue(outputString.indexOf("wsse:Password") == -1);
+        assertTrue(outputString.indexOf("wsse11:Salt") != -1);
+        assertTrue(outputString.indexOf("wsse11:Iteration") != -1);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug(outputString);
+        }
+        
+        boolean passwordsAreEnabledOrig = WSSecurityEngine.getInstance().getWssConfig().getPasswordsAreEncoded();
+        try {
+            WSSecurityEngine.getInstance().getWssConfig().setPasswordsAreEncoded(true);
+            Vector results = verify(signedDoc);
+            WSSecurityEngineResult actionResult =
+                WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
+            java.security.Principal principal = 
+                (java.security.Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
+            //System.out.println(principal.getName());
+            assertTrue(principal.getName().indexOf("derivedKey") != -1);
+        } finally {
+            WSSecurityEngine.getInstance().getWssConfig().setPasswordsAreEncoded(passwordsAreEnabledOrig);
+        }
+    }
+    
+    /**
      * Test using a UsernameToken derived key for signing a SOAP body. In this test the
      * derived key is modified before signature, and so signature verification should
      * fail.
@@ -462,7 +585,12 @@ public class TestWSSecurityUTDK extends 
                 WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
                 if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN
                     && "bob".equals(pc.getIdentifier())) {
-                    pc.setPassword("security");
+                    if (WSSecurityEngine.getInstance().getWssConfig().getPasswordsAreEncoded()) {
+                        // "jux7xGGAjguKKHg9C+waOiLrCCE=" is the Base64 encoded SHA-1 hash of "security".
+                        pc.setPassword("jux7xGGAjguKKHg9C+waOiLrCCE=");
+                    } else {
+                        pc.setPassword("security");
+                    }
                 } else {
                     throw new IOException("Authentication failed");
                 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ws.apache.org
For additional commands, e-mail: dev-help@ws.apache.org