You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ka...@apache.org on 2013/09/18 12:43:26 UTC

svn commit: r1524359 - /directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/password/PasswordUtil.java

Author: kayyagari
Date: Wed Sep 18 10:43:25 2013
New Revision: 1524359

URL: http://svn.apache.org/r1524359
Log:
making the hash generation compatible with that of the values generated by JIRA software (DIRSERVER-1898)

Modified:
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/password/PasswordUtil.java

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/password/PasswordUtil.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/password/PasswordUtil.java?rev=1524359&r1=1524358&r2=1524359&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/password/PasswordUtil.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/password/PasswordUtil.java Wed Sep 18 10:43:25 2013
@@ -64,7 +64,7 @@ public class PasswordUtil
     public static final int MD5_LENGTH = 16;
 
     /** The PKCS5S2 hash length */
-    public static final int PKCS5S2_LENGTH = 20;
+    public static final int PKCS5S2_LENGTH = 32;
 
     /**
      * Get the algorithm from the stored password. 
@@ -149,11 +149,15 @@ public class PasswordUtil
             case HASH_METHOD_SSHA384:
             case HASH_METHOD_SSHA512:
             case HASH_METHOD_SMD5:
-            case HASH_METHOD_PKCS5S2:
                 salt = new byte[8]; // we use 8 byte salt always except for "crypt" which needs 2 byte salt
                 new SecureRandom().nextBytes( salt );
                 break;
 
+            case HASH_METHOD_PKCS5S2:
+                salt = new byte[16]; // we use 16 byte salt for PKCS5S2
+                new SecureRandom().nextBytes( salt );
+                break;
+                
             case HASH_METHOD_CRYPT:
                 salt = new byte[2];
                 SecureRandom sr = new SecureRandom();
@@ -183,7 +187,16 @@ public class PasswordUtil
             else if ( salt != null )
             {
                 byte[] hashedPasswordWithSaltBytes = new byte[hashedPassword.length + salt.length];
-                merge( hashedPasswordWithSaltBytes, hashedPassword, salt );
+
+                if ( algorithm == LdapSecurityConstants.HASH_METHOD_PKCS5S2 )
+                {
+                    merge( hashedPasswordWithSaltBytes, salt, hashedPassword );
+                }
+                else
+                {
+                    merge( hashedPasswordWithSaltBytes, hashedPassword, salt );
+                }
+                
                 sb.append( String.valueOf( Base64.encode( hashedPasswordWithSaltBytes ) ) );
             }
             else
@@ -222,7 +235,6 @@ public class PasswordUtil
      *  <ul>
      *  <li>- length(password) - 20, starting at 21st position for SSHA</li>
      *  <li>- length(password) - 16, starting at 16th position for SMD5</li>
-     *  <li>- length(password) - 20, starting at 21st position for PKCS5S2</li>
      *  <li>- length(password) - 2, starting at 3rd position for crypt</li>
      *  </ul>
      *  <p>
@@ -230,6 +242,9 @@ public class PasswordUtil
      *  to a byte[] before comparing the password with the stored one.
      *  </p>
      *  <p>
+     *  For PKCS5S2 the salt is stored in the beginning of the password
+     *  </p>
+     *  <p>
      *  For crypt, we only have to remove the salt.
      *  </p>
      *  <p>
@@ -429,7 +444,7 @@ public class PasswordUtil
                 return getCredentials( credentials, algoLength, SHA512_LENGTH, encryptionMethod );
 
             case HASH_METHOD_PKCS5S2:
-                return getCredentials( credentials, algoLength, PKCS5S2_LENGTH, encryptionMethod );
+                return getPbkdf2Credentials( credentials, algoLength, encryptionMethod );
                 
             case HASH_METHOD_CRYPT:
                 // The password is associated with a salt. Decompose it
@@ -524,6 +539,8 @@ public class PasswordUtil
     /**
      * generates a hash based on the <a href="http://en.wikipedia.org/wiki/PBKDF2">PKCS5S2 spec</a>
      * 
+     * Note: this has been implemented to generate hashes compatible with what JIRA generates.
+     *       See the <a href="http://pythonhosted.org/passlib/lib/passlib.hash.atlassian_pbkdf2_sha1.html">JIRA's passlib</a>
      * @param algorithm the algorithm to use
      * @param password the credentials
      * @param salt the optional salt
@@ -535,7 +552,7 @@ public class PasswordUtil
         {
             SecretKeyFactory sk = SecretKeyFactory.getInstance( algorithm.getAlgorithm() );
             char[] password = Strings.utf8ToString( credentials ).toCharArray();
-            KeySpec keySpec = new PBEKeySpec( password, salt, 1000, 160 );
+            KeySpec keySpec = new PBEKeySpec( password, salt, 10000, PKCS5S2_LENGTH * 8 );
             Key key = sk.generateSecret( keySpec );
             return key.getEncoded();
         }
@@ -544,5 +561,36 @@ public class PasswordUtil
             throw new RuntimeException( e );
         }
     }
+
+    
+    /**
+     * Gets the credentials from a PKCS5S2 hash.
+     * The salt for PKCS5S2 hash is prepended to the password
+     */
+    private static byte[] getPbkdf2Credentials( byte[] credentials, int algoLength, EncryptionMethod encryptionMethod )
+    {
+        try
+        {
+            // The password is associated with a salt. Decompose it
+            // in two parts, after having decoded the password.
+            // The salt will be stored into the EncryptionMethod structure
+            // The salt is at the *beginning* of the credentials, and is 16 bytes long
+            byte[] passwordAndSalt = Base64.decode( new String( credentials, algoLength, credentials.length
+                - algoLength, "UTF-8" ).toCharArray() );
+
+            int saltLength = passwordAndSalt.length - PKCS5S2_LENGTH;
+            encryptionMethod.setSalt( new byte[saltLength] );
+            byte[] password = new byte[PKCS5S2_LENGTH];
+            
+            split( passwordAndSalt, 0, encryptionMethod.getSalt(), password );
+
+            return password;
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            // do nothing
+            return credentials;
+        }
+    }
     
 }