diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/PasswordUtility.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/PasswordUtility.java index b72e1d6..708e839 100644 --- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/PasswordUtility.java +++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/PasswordUtility.java @@ -25,6 +25,9 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * Utility to generate and compare password hashes. @@ -42,6 +45,17 @@ public class PasswordUtility { public static final int DEFAULT_ITERATIONS = 1000; /** + * An in-memory cache of password hashes done with N iterations + * to just one iteration. Used to speed up login attempts while still + * keeping the on-disk (or in-repository) password hashes protected + * with many hash iterations. We still use one hash iteration even + * in this cache to prevent an attacker from getting all active + * plain-text passwords from a heap dump. + */ + private static final Map PASSWORD_CACHE = + Collections.synchronizedMap(new HashMap()); + + /** * Avoid instantiation */ private PasswordUtility() {} @@ -101,6 +115,7 @@ public class PasswordUtility { return extractAlgorithm(password) == null; } + /** * Returns {@code true} if hash of the specified {@code password} equals the * given hashed password. @@ -122,9 +137,21 @@ public class PasswordUtility { iterations = extractIterations(hashedPassword, startPos); } + String simpleHash = generateHash( + password, algorithm, salt, NO_ITERATIONS); + if (iterations == NO_ITERATIONS) { + return hashedPassword.equals(simpleHash); + } + if (simpleHash.equals(PASSWORD_CACHE.get(hashedPassword))) { + return true; + } + String hash = generateHash(password, algorithm, salt, iterations); - return hashedPassword.equals(hash); - } // hashedPassword is plaintext -> return false + if (hashedPassword.equals(hash)) { + PASSWORD_CACHE.put(hashedPassword, simpleHash); + return true; + } + } // hashedPassword is plaintext or doesn't match -> return false } catch (NoSuchAlgorithmException e) { log.warn(e.getMessage()); } catch (UnsupportedEncodingException e) {