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

This commit is contained in:
Luke Taylor 2007-08-27 16:23:14 +00:00
parent 0425d3b638
commit f8689b18b2
2 changed files with 51 additions and 8 deletions

View File

@ -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;
}
}

View File

@ -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));
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));
}
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}"));
}
}