configurable hash iterations

This commit is contained in:
Bill Burke committed 2014-07-07 12:11:45 -04:00
1 parent 4cbc423d67
commit 15d7568792
14 files changed
+165 -28

No files matched your search

@@ -0,0 +1,53 @@
package org.keycloak.models;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CredentialValidation {
private static int hashIterations(RealmModel realm) {
PasswordPolicy policy = realm.getPasswordPolicy();
if (policy != null) {
return policy.getHashIterations();
}
return -1;
}
/**
* Will update password if hash iteration policy has changed
*
* @param realm
* @param user
* @param password
* @return
*/
public static boolean validatePassword(RealmModel realm, UserModel user, String password) {
boolean validated = false;
UserCredentialValueModel passwordCred = null;
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
validated = new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue(), cred.getHashIterations());
passwordCred = cred;
}
}
if (validated) {
int iterations = hashIterations(realm);
if (iterations > -1 && iterations != passwordCred.getHashIterations()) {
UserCredentialValueModel newCred = new UserCredentialValueModel();
newCred.setType(passwordCred.getType());
newCred.setDevice(passwordCred.getDevice());
newCred.setSalt(passwordCred.getSalt());
newCred.setHashIterations(iterations);
newCred.setValue(new Pbkdf2PasswordEncoder(newCred.getSalt()).encode(password, iterations));
user.updateCredentialDirectly(newCred);
}
}
return validated;
}
}
+31
View File
@@ -52,11 +52,28 @@ public class PasswordPolicy {
list.add(new UpperCase(args));
} else if (name.equals(SpecialChars.NAME)) {
list.add(new SpecialChars(args));
} else if (name.equals(HashIterations.NAME)) {
list.add(new HashIterations(args));
}
}
return list;
}
/**
*
* @return -1 if no hash iterations setting
*/
public int getHashIterations() {
if (policies == null) return -1;
for (Policy p : policies) {
if (p instanceof HashIterations) {
return ((HashIterations)p).iterations;
}
}
return -1;
}
public String validate(String password) {
for (Policy p : policies) {
String error = p.validate(password);
@@ -71,6 +88,20 @@ public class PasswordPolicy {
public String validate(String password);
}
private static class HashIterations implements Policy {
private static final String NAME = "hashIterations";
private int iterations;
public HashIterations(String[] args) {
iterations = intArg(NAME, 1, args);
}
@Override
public String validate(String password) {
return null;
}
}
private static class Length implements Policy {
private static final String NAME = "length";
private int min;
+9
View File
@@ -11,6 +11,7 @@ public class UserCredentialValueModel {
private String value;
private String device;
private byte[] salt;
private int hashIterations;
public String getType() {
return type;
@@ -43,4 +44,12 @@ public class UserCredentialValueModel {
public void setSalt(byte[] salt) {
this.salt = salt;
}
public int getHashIterations() {
return hashIterations;
}
public void setHashIterations(int iterations) {
this.hashIterations = iterations;
}
}
+9
View File
@@ -9,6 +9,7 @@ public class CredentialEntity {
private String value;
private String device;
private byte[] salt;
private int hashIterations;
public String getType() {
return type;
@@ -41,4 +42,12 @@ public class CredentialEntity {
public void setSalt(byte[] salt) {
this.salt = salt;
}
public int getHashIterations() {
return hashIterations;
}
public void setHashIterations(int hashIterations) {
this.hashIterations = hashIterations;
}
}
@@ -43,7 +43,7 @@ public class Pbkdf2PasswordEncoder {
* @param rawPassword The password used as a master key to derive into a session key
* @return encoded password in Base64
*/
public String encode(String rawPassword) {
public String encode(String rawPassword, int iterations) {
String encodedPassword;
@@ -59,6 +59,10 @@ public class Pbkdf2PasswordEncoder {
return encodedPassword;
}
public String encode(String rawPassword) {
return encode(rawPassword, iterations);
}
/**
* Encode the password provided and compare with the hash stored into the database
* @param rawPassword The password provided
@@ -69,6 +73,16 @@ public class Pbkdf2PasswordEncoder {
return encode(rawPassword).equals(encodedPassword);
}
/**
* Encode the password provided and compare with the hash stored into the database
* @param rawPassword The password provided
* @param encodedPassword Encoded hash stored into the database
* @return true if the password is valid, otherwise false for invalid credentials
*/
public boolean verify(String rawPassword, String encodedPassword, int iterations) {
return encode(rawPassword, iterations).equals(encodedPassword);
}
/**
* Generate a salt for each password
* @return cryptographically strong random number