configurable hash iterations
This commit is contained in:
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;
|
||||
|
||||
}
|
||||
}
|
||||
Regular → Executable
+31
@@ -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;
|
||||
|
||||
Regular → Executable
+9
@@ -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;
|
||||
}
|
||||
}
|
||||
Regular → Executable
+9
@@ -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
|
||||
|
||||
Reference in new issue
Block a user