SEC-526: Fixed. Support for different case prefixes ({SHA}, {sha} etc).
This commit is contained in:
parent
0425d3b638
commit
f8689b18b2
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}"));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue