Prechádzať zdrojové kódy

SEC-526: Fixed. Support for different case prefixes ({SHA}, {sha} etc).

Luke Taylor 18 rokov pred
rodič
commit
f8689b18b2

+ 30 - 7
core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoder.java

@@ -29,7 +29,8 @@ import java.security.MessageDigest;
 
 /**
  * A version of {@link ShaPasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA) encodings. The values are
- * base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash.
+ * base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash. These can be made lower-case
+ * in the encoded password, if required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
  *
  * @author Luke Taylor
  * @version $Id$
@@ -40,7 +41,12 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
     /** The number of bytes in a SHA hash */
     private static final int SHA_LENGTH = 20;
     private static final String SSHA_PREFIX = "{SSHA}";
+    private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase();
     private static final String SHA_PREFIX = "{SHA}";
+    private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase();
+
+    //~ Instance fields ================================================================================================
+    private boolean forceLowerCasePrefix;
 
     //~ Constructors ===================================================================================================
 
@@ -76,7 +82,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
         try {
             sha = MessageDigest.getInstance("SHA");
         } catch (java.security.NoSuchAlgorithmException e) {
-            throw new LdapDataAccessException("No SHA implementation available!");
+            throw new LdapDataAccessException("No SHA implementation available!", e);
         }
 
         sha.update(rawPass.getBytes());
@@ -88,7 +94,15 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
 
         byte[] hash = combineHashAndSalt(sha.digest(), (byte[]) salt);
 
-        return ((salt == null) ? SHA_PREFIX : SSHA_PREFIX) + new String(Base64.encodeBase64(hash));
+        String prefix;
+
+        if (salt == null) {
+            prefix = forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
+        } else {
+            prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
+        }
+
+        return prefix + new String(Base64.encodeBase64(hash));
     }
 
     private byte[] extractSalt(String encPass) {
@@ -106,19 +120,28 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
      * Checks the validity of an unencoded password against an encoded one in the form
      * "{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI".
      *
-     * @param encPass the SSHA or SHA encoded password
+     * @param encPass the actual SSHA or SHA encoded password
      * @param rawPass unencoded password to be verified.
      * @param salt ignored. If the format is SSHA the salt bytes will be extracted from the encoded password.
      *
-     * @return true if they match.
+     * @return true if they match (independent of the case of the prefix).
      */
     public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
-        if (encPass.startsWith(SSHA_PREFIX)) {
+        String encPassWithoutPrefix;
+
+        if (encPass.startsWith(SSHA_PREFIX) || encPass.startsWith(SSHA_PREFIX_LC)) {
+            encPassWithoutPrefix = encPass.substring(6);
             salt = extractSalt(encPass);
         } else {
+            encPassWithoutPrefix = encPass.substring(5);
             salt = null;
         }
 
-        return encPass.equals(encodePassword(rawPass, salt));
+        // Compare the encoded passwords without the prefix
+        return encodePassword(rawPass, salt).endsWith(encPassWithoutPrefix);
+    }
+
+    public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {
+        this.forceLowerCasePrefix = forceLowerCasePrefix;
     }
 }

+ 21 - 1
core/src/test/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoderTests.java

@@ -55,14 +55,34 @@ public class LdapShaPasswordEncoderTests extends TestCase {
      * Test values generated by 'slappasswd -h {SHA} -s boabspasswurd'
      */
     public void testValidPasswordSucceeds() {
+        sha.setForceLowerCasePrefix(false);
         assertTrue(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
+        assertTrue(sha.isPasswordValid("{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
+        sha.setForceLowerCasePrefix(true);
+        assertTrue(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
+        assertTrue(sha.isPasswordValid("{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
     }
 
     /**
      * Test values generated by 'slappasswd -s boabspasswurd'
      */
     public void testValidSaltedPasswordSucceeds() {
+        sha.setForceLowerCasePrefix(false);
+        assertTrue(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "boabspasswurd", null));
+        assertTrue(sha.isPasswordValid("{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null));
+        sha.setForceLowerCasePrefix(true);
         assertTrue(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "boabspasswurd", null));
-        assertTrue(sha.isPasswordValid("{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null));
+        assertTrue(sha.isPasswordValid("{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null));
+    }
+
+    public void testCorrectPrefixCaseIsUsed() {
+        sha.setForceLowerCasePrefix(false);
+        assertEquals("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", sha.encodePassword("boabspasswurd", null));
+        assertTrue(sha.encodePassword("somepassword", "salt".getBytes()).startsWith("{SSHA}"));
+
+        sha.setForceLowerCasePrefix(true);
+        assertEquals("{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", sha.encodePassword("boabspasswurd", null));
+        assertTrue(sha.encodePassword("somepassword", "salt".getBytes()).startsWith("{ssha}"));        
+
     }
 }