keycloak-uncached

KEYCLOAK-1900 Add password hash SPI and provider - Default

11/18/2015 12:06:23 PM

Changes

Details

diff --git a/model/api/src/main/java/org/keycloak/hash/DefaultPasswordHashProvider.java b/model/api/src/main/java/org/keycloak/hash/DefaultPasswordHashProvider.java
new file mode 100644
index 0000000..c8a5b38
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/hash/DefaultPasswordHashProvider.java
@@ -0,0 +1,41 @@
+package org.keycloak.hash;
+
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+
+/**
+ * @author <a href="mailto:me@tsudot.com">Kunal Kerkar</a>
+ */
+public class DefaultPasswordHashProvider implements PasswordHashProvider {
+
+    private final String algorithm;
+    private final int iterations;
+
+    public DefaultPasswordHashProvider() {
+        this.algorithm = "pbkdf2";
+        this.iterations = 1;
+    }
+
+    public String encode(String rawPassword, byte[] salt) {
+        return this.encode(rawPassword, salt, this.iterations);
+    }
+
+    public String encode(String rawPassword, byte[] salt, int iterations) {
+        Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(salt, iterations);
+        return encoder.encode(rawPassword);
+    }
+
+    public boolean verify(String rawPassword, String encodedPassword, byte[] salt) {
+        Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(salt);
+        return encoder.verify(rawPassword, encodedPassword);
+    }
+
+    public String getAlgorithm() {
+        return this.algorithm;
+    }
+
+    public void close() {
+    }
+
+}
diff --git a/model/api/src/main/java/org/keycloak/hash/DefaultPasswordHashProviderFactory.java b/model/api/src/main/java/org/keycloak/hash/DefaultPasswordHashProviderFactory.java
new file mode 100644
index 0000000..8d3da0e
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/hash/DefaultPasswordHashProviderFactory.java
@@ -0,0 +1,35 @@
+package org.keycloak.hash;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:me@tsudot.com">Kunal Kerkar</a>
+ */
+public class DefaultPasswordHashProviderFactory implements PasswordHashProviderFactory {
+
+    @Override
+    public PasswordHashProvider create(KeycloakSession session) {  
+        return new DefaultPasswordHashProvider();
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public String getId() {
+        return "pbkdf2";
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
+
diff --git a/model/api/src/main/java/org/keycloak/hash/PasswordHashProvider.java b/model/api/src/main/java/org/keycloak/hash/PasswordHashProvider.java
new file mode 100644
index 0000000..34dc14a
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/hash/PasswordHashProvider.java
@@ -0,0 +1,16 @@
+package org.keycloak.hash;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:me@tsudot.com">Kunal Kerkar</a>
+ */
+public interface PasswordHashProvider extends Provider {
+
+    String encode(String rawPassword, byte[] salt);
+
+    String encode(String rawPassword, byte[] salt, int iterations);
+
+    boolean verify(String rawPassword, String encodedPassword, byte[] salt);
+
+}
diff --git a/model/api/src/main/java/org/keycloak/hash/PasswordHashProviderFactory.java b/model/api/src/main/java/org/keycloak/hash/PasswordHashProviderFactory.java
new file mode 100644
index 0000000..5c16baa
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/hash/PasswordHashProviderFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.hash;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:me@tsudot.com">Kunal Kerkar</a>
+ */
+public interface PasswordHashProviderFactory extends ProviderFactory<PasswordHashProvider> {
+
+}
diff --git a/model/api/src/main/java/org/keycloak/hash/PasswordHashSpi.java b/model/api/src/main/java/org/keycloak/hash/PasswordHashSpi.java
new file mode 100644
index 0000000..3f1d9af
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/hash/PasswordHashSpi.java
@@ -0,0 +1,31 @@
+package org.keycloak.hash;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:me@tsudot.com">Kunal Kerkar</a>
+ */
+public class PasswordHashSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "password-hash";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return PasswordHashProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return PasswordHashProviderFactory.class;
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index 0c52929..159d943 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -21,6 +21,8 @@ public interface Constants {
     String[] BROKER_SERVICE_ROLES = {READ_TOKEN_ROLE};
     String OFFLINE_ACCESS_ROLE = OAuth2Constants.OFFLINE_ACCESS;
 
+    String DEFAULT_HASH_ALGORITHM = "pbkdf2";
+
     // 15 minutes
     int DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT = 900;
     // 30 days
diff --git a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
index 7e2bfb4..1929c9f 100755
--- a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
@@ -21,6 +21,7 @@ public class UserCredentialModel {
     protected String type;
     protected String value;
     protected String device;
+    protected String algorithm;
 
     public UserCredentialModel() {
     }
@@ -107,4 +108,12 @@ public class UserCredentialModel {
     public void setDevice(String device) {
         this.device = device;
     }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
index 1a1709b..8c64daf 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -403,7 +403,7 @@ public class UserFederationManager implements UserProvider {
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input) {
         UserFederationProvider link = getFederationLink(realm, user);
         if (link != null) {
             validateUser(realm, user);
@@ -421,10 +421,10 @@ public class UserFederationManager implements UserProvider {
                 if (!link.validCredentials(realm, user, fedCreds)) {
                     return false;
                 }
-                return session.userStorage().validCredentials(realm, user, localCreds);
+                return session.userStorage().validCredentials(session, realm, user, localCreds);
             }
         }
-        return session.userStorage().validCredentials(realm, user, input);
+        return session.userStorage().validCredentials(session, realm, user, input);
     }
 
     /**
@@ -466,12 +466,12 @@ public class UserFederationManager implements UserProvider {
 
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
-        return validCredentials(realm, user, Arrays.asList(input));
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel... input) {
+        return validCredentials(session, realm, user, Arrays.asList(input));
     }
 
     @Override
-    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+    public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
         List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
         List<UserFederationProvider> fedProviders = new ArrayList<UserFederationProvider>();
         for (UserFederationProviderModel fedProviderModel : fedProviderModels) {
diff --git a/model/api/src/main/java/org/keycloak/models/UserProvider.java b/model/api/src/main/java/org/keycloak/models/UserProvider.java
index 962bc77..1c2b5c7 100755
--- a/model/api/src/main/java/org/keycloak/models/UserProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java
@@ -59,9 +59,9 @@ public interface UserProvider extends Provider {
     void preRemove(RealmModel realm, ClientModel client);
     void preRemove(ClientModel realm, ProtocolMapperModel protocolMapper);
 
-    boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
-    boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
-    CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input);
+    boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input);
+    boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel... input);
+    CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input);
 
     void close();
 }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
index d3e4d4a..49623e5 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
@@ -1,8 +1,12 @@
 package org.keycloak.models.utils;
 
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.OTPPolicy;
 import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
@@ -11,6 +15,7 @@ import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.PasswordToken;
 import org.keycloak.common.util.Time;
+import org.keycloak.hash.PasswordHashProvider;
 
 import java.io.IOException;
 import java.util.List;
@@ -38,7 +43,7 @@ public class CredentialValidation {
      * @param password
      * @return
      */
-    public static boolean validPassword(RealmModel realm, UserModel user, String password) {
+    public static boolean validPassword(KeycloakSession session, RealmModel realm, UserModel user, String password) {
         UserCredentialValueModel passwordCred = null;
         for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
             if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
@@ -47,16 +52,30 @@ public class CredentialValidation {
         }
         if (passwordCred == null) return false;
 
-        return validateHashedCredential(realm, user, password, passwordCred);
+        return validateHashedCredential(session, realm, user, password, passwordCred);
 
     }
 
-    public static boolean validateHashedCredential(RealmModel realm, UserModel user, String unhashedCredValue, UserCredentialValueModel credential) {
+
+    public static boolean validateHashedCredential(KeycloakSession session, RealmModel realm, UserModel user, String unhashedCredValue, UserCredentialValueModel credential) {
         if(unhashedCredValue == null){
             return false;
         }
+        String algorithm;
+        if (credential.getAlgorithm() == null || credential.getAlgorithm().trim().equals("")) {
+            algorithm = Constants.DEFAULT_HASH_ALGORITHM;
+        } else {
+            algorithm = credential.getAlgorithm();
+        }
+
+        byte[] salt = getSalt();
+
+        PasswordHashProvider provider = session.getProvider(PasswordHashProvider.class, algorithm);
+
+        byte[] credSalt = credential.getSalt();
+
+        boolean validated = provider.verify(unhashedCredValue, credential.getValue(), credSalt);
 
-        boolean validated = new Pbkdf2PasswordEncoder(credential.getSalt()).verify(unhashedCredValue, credential.getValue(), credential.getHashIterations());
         if (validated) {
             int iterations = hashIterations(realm);
             if (iterations > -1 && iterations != credential.getHashIterations()) {
@@ -65,7 +84,7 @@ public class CredentialValidation {
                 newCred.setDevice(credential.getDevice());
                 newCred.setSalt(credential.getSalt());
                 newCred.setHashIterations(iterations);
-                newCred.setValue(new Pbkdf2PasswordEncoder(newCred.getSalt()).encode(unhashedCredValue, iterations));
+                newCred.setValue(provider.encode(unhashedCredValue, salt, iterations));
                 user.updateCredentialDirectly(newCred);
             }
 
@@ -157,9 +176,9 @@ public class CredentialValidation {
      * @param credentials
      * @return
      */
-    public static boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> credentials) {
+    public static boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> credentials) {
         for (UserCredentialModel credential : credentials) {
-            if (!validCredential(realm, user, credential)) return false;
+            if (!validCredential(session, realm, user, credential)) return false;
         }
         return true;
     }
@@ -172,16 +191,16 @@ public class CredentialValidation {
      * @param credentials
      * @return
      */
-    public static boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... credentials) {
+    public static boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel... credentials) {
         for (UserCredentialModel credential : credentials) {
-            if (!validCredential(realm, user, credential)) return false;
+            if (!validCredential(session, realm, user, credential)) return false;
         }
         return true;
     }
 
-    private static boolean validCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
+    private static boolean validCredential(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel credential) {
         if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
-            if (!validPassword(realm, user, credential.getValue())) {
+            if (!validPassword(session, realm, user, credential.getValue())) {
                 return false;
             }
         } else if (credential.getType().equals(UserCredentialModel.PASSWORD_TOKEN)) {
diff --git a/model/api/src/main/resources/META-INF/services/org.keycloak.hash.PasswordHashProviderFactory b/model/api/src/main/resources/META-INF/services/org.keycloak.hash.PasswordHashProviderFactory
new file mode 100644
index 0000000..56193f6
--- /dev/null
+++ b/model/api/src/main/resources/META-INF/services/org.keycloak.hash.PasswordHashProviderFactory
@@ -0,0 +1 @@
+org.keycloak.hash.DefaultPasswordHashProviderFactory
diff --git a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index be3982b..fd8c0fd 100755
--- a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -4,4 +4,5 @@ org.keycloak.models.RealmSpi
 org.keycloak.models.UserSessionSpi
 org.keycloak.models.UserSpi
 org.keycloak.models.session.UserSessionPersisterSpi
-org.keycloak.migration.MigrationSpi
\ No newline at end of file
+org.keycloak.migration.MigrationSpi
+org.keycloak.hash.PasswordHashSpi
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
index 44260a1..c7f5bf2 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
@@ -299,18 +299,18 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
-        return getDelegate().validCredentials(realm, user, input);
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+        return getDelegate().validCredentials(session, realm, user, input);
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
-        return getDelegate().validCredentials(realm, user, input);
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel... input) {
+        return getDelegate().validCredentials(session, realm, user, input);
     }
 
     @Override
-    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
-        return getDelegate().validCredentials(realm, input);
+    public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
+        return getDelegate().validCredentials(session, realm, input);
     }
 
     @Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
index 8ef4dd2..9f6fdba 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
@@ -4,6 +4,7 @@ import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.common.util.Time;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.OTPPolicy;
@@ -29,7 +30,6 @@ import org.keycloak.models.jpa.entities.UserEntity;
 import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
 import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -43,8 +43,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
-
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 64650c9..b536a9c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -59,7 +59,7 @@ public class JpaUserProvider implements UserProvider {
         entity.setRealmId(realm.getId());
         em.persist(entity);
         em.flush();
-        UserModel userModel = new UserAdapter(realm, em, entity);
+        UserModel userModel = new UserAdapter(session, realm, em, entity);
 
         if (addDefaultRoles) {
             for (String r : realm.getDefaultRoles()) {
@@ -241,7 +241,7 @@ public class JpaUserProvider implements UserProvider {
 
         List<UserModel> users = new ArrayList<UserModel>();
         for (UserEntity user : results) {
-            users.add(new UserAdapter(realm, em, user));
+            users.add(new UserAdapter(session, realm, em, user));
         }
         return users;
     }
@@ -259,7 +259,7 @@ public class JpaUserProvider implements UserProvider {
         query.setParameter("realmId", realm.getId());
         List<UserEntity> entities = query.getResultList();
         if (entities.size() == 0) return null;
-        return new UserAdapter(realm, em, entities.get(0));
+        return new UserAdapter(session, realm, em, entities.get(0));
     }
 
     @Override
@@ -269,7 +269,7 @@ public class JpaUserProvider implements UserProvider {
         query.setParameter("realmId", realm.getId());
         List<UserEntity> results = query.getResultList();
         if (results.size() == 0) return null;
-        return new UserAdapter(realm, em, results.get(0));
+        return new UserAdapter(session, realm, em, results.get(0));
     }
 
     @Override
@@ -278,7 +278,7 @@ public class JpaUserProvider implements UserProvider {
         query.setParameter("email", email.toLowerCase());
         query.setParameter("realmId", realm.getId());
         List<UserEntity> results = query.getResultList();
-        return results.isEmpty() ? null : new UserAdapter(realm, em, results.get(0));
+        return results.isEmpty() ? null : new UserAdapter(session, realm, em, results.get(0));
     }
 
      @Override
@@ -299,7 +299,7 @@ public class JpaUserProvider implements UserProvider {
                     ", userId=" + identity.getUserId() + ", results=" + results);
         } else {
             UserEntity user = results.get(0);
-            return new UserAdapter(realm, em, user);
+            return new UserAdapter(session, realm, em, user);
         }
     }
 
@@ -316,7 +316,7 @@ public class JpaUserProvider implements UserProvider {
                     ", results=" + results);
         } else {
             UserEntity user = results.get(0);
-            return new UserAdapter(client.getRealm(), em, user);
+            return new UserAdapter(session, client.getRealm(), em, user);
         }
     }
 
@@ -347,7 +347,7 @@ public class JpaUserProvider implements UserProvider {
         }
         List<UserEntity> results = query.getResultList();
         List<UserModel> users = new ArrayList<UserModel>();
-        for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+        for (UserEntity entity : results) users.add(new UserAdapter(session, realm, em, entity));
         return users;
     }
 
@@ -365,7 +365,7 @@ public class JpaUserProvider implements UserProvider {
 
         List<UserModel> users = new ArrayList<UserModel>();
         for (UserEntity user : results) {
-            users.add(new UserAdapter(realm, em, user));
+            users.add(new UserAdapter(session, realm, em, user));
         }
         return users;
     }
@@ -388,7 +388,7 @@ public class JpaUserProvider implements UserProvider {
         }
         List<UserEntity> results = query.getResultList();
         List<UserModel> users = new ArrayList<UserModel>();
-        for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+        for (UserEntity entity : results) users.add(new UserAdapter(session, realm, em, entity));
         return users;
     }
 
@@ -446,7 +446,7 @@ public class JpaUserProvider implements UserProvider {
         }
         List<UserEntity> results = query.getResultList();
         List<UserModel> users = new ArrayList<UserModel>();
-        for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+        for (UserEntity entity : results) users.add(new UserAdapter(session, realm, em, entity));
         return users;
     }
 
@@ -460,7 +460,7 @@ public class JpaUserProvider implements UserProvider {
         List<UserModel> users = new ArrayList<UserModel>();
         for (UserAttributeEntity attr : results) {
             UserEntity user = attr.getUser();
-            users.add(new UserAdapter(realm, em, user));
+            users.add(new UserAdapter(session, realm, em, user));
         }
         return users;
     }
@@ -495,17 +495,17 @@ public class JpaUserProvider implements UserProvider {
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
-        return CredentialValidation.validCredentials(realm, user, input);
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+        return CredentialValidation.validCredentials(session, realm, user, input);
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
-        return CredentialValidation.validCredentials(realm, user, input);
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel... input) {
+        return CredentialValidation.validCredentials(session, realm, user, input);
     }
 
     @Override
-    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+    public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
         // Not supported yet
         return null;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index 5246890..8594bca 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -2,6 +2,7 @@ package org.keycloak.models.jpa;
 
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.OTPPolicy;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.UserConsentModel;
@@ -24,9 +25,9 @@ import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
 import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
 import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.common.util.Time;
+import org.keycloak.hash.PasswordHashProvider;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -52,11 +53,13 @@ public class UserAdapter implements UserModel {
     protected UserEntity user;
     protected EntityManager em;
     protected RealmModel realm;
+    private final KeycloakSession session;
 
-    public UserAdapter(RealmModel realm, EntityManager em, UserEntity user) {
+    public UserAdapter(KeycloakSession session, RealmModel realm, EntityManager em, UserEntity user) {
         this.em = em;
         this.user = user;
         this.realm = realm;
+        this.session = session;
     }
 
     public UserEntity getUser() {
@@ -389,6 +392,7 @@ public class UserAdapter implements UserModel {
     private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
         byte[] salt = getSalt();
         int hashIterations = 1;
+        PasswordHashProvider provider = session.getProvider(PasswordHashProvider.class);
         PasswordPolicy policy = realm.getPasswordPolicy();
         if (policy != null) {
             hashIterations = policy.getHashIterations();
@@ -396,7 +400,7 @@ public class UserAdapter implements UserModel {
                 hashIterations = 1;
         }
         credentialEntity.setCreatedDate(Time.toMillis(Time.currentTime()));
-        credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+        credentialEntity.setValue(provider.encode(cred.getValue(), salt, hashIterations));
         credentialEntity.setSalt(salt);
         credentialEntity.setHashIterations(hashIterations);
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index 44758b7..3b5a1ac 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -474,17 +474,17 @@ public class MongoUserProvider implements UserProvider {
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
-        return CredentialValidation.validCredentials(realm, user, input);
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+        return CredentialValidation.validCredentials(session, realm, user, input);
     }
 
     @Override
-    public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
-        return CredentialValidation.validCredentials(realm, user, input);
+    public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel... input) {
+        return CredentialValidation.validCredentials(session, realm, user, input);
     }
 
     @Override
-    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+    public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
         // Not supported yet
         return null;
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index 83979c3..3137ab4 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -26,8 +26,8 @@ import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
 import org.keycloak.models.mongo.utils.MongoModelUtils;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 import org.keycloak.common.util.Time;
+import org.keycloak.hash.PasswordHashProvider;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -331,6 +331,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
     private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
         byte[] salt = getSalt();
         int hashIterations = 1;
+        PasswordHashProvider provider = session.getProvider(PasswordHashProvider.class);
         PasswordPolicy policy = realm.getPasswordPolicy();
         if (policy != null) {
             hashIterations = policy.getHashIterations();
@@ -338,7 +339,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
                 hashIterations = 1;
         }
         credentialEntity.setCreatedDate(Time.toMillis(Time.currentTime()));
-        credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+        credentialEntity.setValue(provider.encode(cred.getValue(), salt, hashIterations));
         credentialEntity.setSalt(salt);
         credentialEntity.setHashIterations(hashIterations);
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
index 5afaa3d..68b036f 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
@@ -149,7 +149,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
         credentials.add(UserCredentialModel.password(password));
-        boolean valid = context.getSession().users().validCredentials(context.getRealm(), user, credentials);
+        boolean valid = context.getSession().users().validCredentials(context.getSession(), context.getRealm(), user, credentials);
         if (!valid) {
             context.getEvent().user(user);
             context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
index 4a0c48d..3991011 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
@@ -49,7 +49,7 @@ public class OTPFormAuthenticator extends AbstractUsernameFormAuthenticator impl
             return;
         }
         credentials.add(UserCredentialModel.otp(context.getRealm().getOTPPolicy().getType(), password));
-        boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+        boolean valid = context.getSession().users().validCredentials(context.getSession(), context.getRealm(), context.getUser(), credentials);
         if (!valid) {
             context.getEvent().user(context.getUser())
                     .error(Errors.INVALID_USER_CREDENTIALS);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
index 091c14d..92d3f21 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
@@ -69,7 +69,7 @@ public class SpnegoAuthenticator extends AbstractUsernameFormAuthenticator imple
         String spnegoToken = tokens[1];
         UserCredentialModel spnegoCredential = UserCredentialModel.kerberos(spnegoToken);
 
-        CredentialValidationOutput output = context.getSession().users().validCredentials(context.getRealm(), spnegoCredential);
+        CredentialValidationOutput output = context.getSession().users().validCredentials(context.getSession(), context.getRealm(), spnegoCredential);
 
         if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
             context.setUser(output.getAuthenticatedUser());
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
index c26da77..f555ae6 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
@@ -51,7 +51,7 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
             return;
         }
         credentials.add(UserCredentialModel.otp(context.getRealm().getOTPPolicy().getType(), otp));
-        boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+        boolean valid = context.getSession().users().validCredentials(context.getSession(), context.getRealm(), context.getUser(), credentials);
         if (!valid) {
             context.getEvent().user(context.getUser());
             context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
index 21aa18d..0bb89e4 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
@@ -32,7 +32,7 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
         credentials.add(UserCredentialModel.password(password));
-        boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+        boolean valid = context.getSession().users().validCredentials(context.getSession(), context.getRealm(), context.getUser(), credentials);
         if (!valid) {
             context.getEvent().user(context.getUser());
             context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
index 18e6682..6c74154 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
@@ -73,7 +73,7 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
         UserCredentialModel cred = new UserCredentialModel();
         cred.setType(context.getRealm().getOTPPolicy().getType());
         cred.setValue(totp);
-        context.getSession().users().validCredentials(context.getRealm(), context.getUser(), cred);
+        context.getSession().users().validCredentials(context.getSession(), context.getRealm(), context.getUser(), cred);
 
         context.getUser().setOtpEnabled(true);
         context.success();
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 1394832..08ad37a 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -572,7 +572,7 @@ public class AccountService extends AbstractSecuredLocalService {
         UserCredentialModel cred = new UserCredentialModel();
         cred.setType(realm.getOTPPolicy().getType());
         cred.setValue(totp);
-        session.users().validCredentials(realm, user, cred);
+        session.users().validCredentials(session, realm, user, cred);
 
         event.event(EventType.UPDATE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
 
@@ -619,7 +619,7 @@ public class AccountService extends AbstractSecuredLocalService {
             }
 
             UserCredentialModel cred = UserCredentialModel.password(password);
-            if (!session.users().validCredentials(realm, user, cred)) {
+            if (!session.users().validCredentials(session, realm, user, cred)) {
                 setReferrerOnPage();
                 return account.setError(Messages.INVALID_PASSWORD_EXISTING).createResponse(AccountPages.PASSWORD);
             }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 5119548..6189178 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -8,4 +8,4 @@ org.keycloak.authentication.ClientAuthenticatorSpi
 org.keycloak.authentication.RequiredActionSpi
 org.keycloak.authentication.FormAuthenticatorSpi
 org.keycloak.authentication.FormActionSpi
-org.keycloak.services.clientregistration.ClientRegistrationSpi
\ No newline at end of file
+org.keycloak.services.clientregistration.ClientRegistrationSpi
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
index 2f89194..b01e1b9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -322,7 +322,12 @@ public class ExportImportTest {
             Assert.fail("user " + username + " not found");
         }
 
-        Assert.assertTrue(userProvider.validCredentials(realm, user, UserCredentialModel.password(password)));
+        KeycloakSession session = keycloakRule.startSession();
+        try {
+            Assert.assertTrue(userProvider.validCredentials(session, realm, user, UserCredentialModel.password(password)));
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
     }
 
     private void assertNotAuthenticated(UserProvider userProvider, RealmProvider realmProvider, String realmName, String username, String password) {
@@ -336,7 +341,12 @@ public class ExportImportTest {
             return;
         }
 
-        Assert.assertFalse(userProvider.validCredentials(realm, user, UserCredentialModel.password(password)));
+        KeycloakSession session = keycloakRule.startSession();
+        try {
+            Assert.assertFalse(userProvider.validCredentials(session, realm, user, UserCredentialModel.password(password)));
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
     }
 
     private static void addUser(UserProvider userProvider, RealmModel appRealm, String username, String password) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
index b67ffa3..ae2e4be 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
@@ -678,7 +678,7 @@ public class FederationProvidersIntegrationTest {
             user.updateCredential(cred);
             UserCredentialValueModel userCredentialValueModel = user.getCredentialsDirectly().get(0);
             Assert.assertEquals(UserCredentialModel.PASSWORD, userCredentialValueModel.getType());
-            Assert.assertTrue(session.users().validCredentials(appRealm, user, cred));
+            Assert.assertTrue(session.users().validCredentials(session, appRealm, user, cred));
 
             // LDAP password is still unchanged
             LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, model);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
index 8c84581..435f221 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
@@ -144,11 +144,11 @@ public class AdapterTest extends AbstractModelTest {
         cred.setType(CredentialRepresentation.PASSWORD);
         cred.setValue("geheim");
         user.updateCredential(cred);
-        Assert.assertTrue(userProvider.validCredentials(realmModel, user, UserCredentialModel.password("geheim")));
+        Assert.assertTrue(userProvider.validCredentials(session, realmModel, user, UserCredentialModel.password("geheim")));
         List<UserCredentialValueModel> creds = user.getCredentialsDirectly();
         Assert.assertEquals(creds.get(0).getHashIterations(), 1);
         realmModel.setPasswordPolicy(new PasswordPolicy("hashIterations(200)"));
-        Assert.assertTrue(userProvider.validCredentials(realmModel, user, UserCredentialModel.password("geheim")));
+        Assert.assertTrue(userProvider.validCredentials(session, realmModel, user, UserCredentialModel.password("geheim")));
         creds = user.getCredentialsDirectly();
         Assert.assertEquals(creds.get(0).getHashIterations(), 200);
         realmModel.setPasswordPolicy(new PasswordPolicy("hashIterations(1)"));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java
index 1a4c660..0da22b0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java
@@ -39,10 +39,10 @@ public class MultipleRealmsTest extends AbstractModelTest {
         r1user1.updateCredential(UserCredentialModel.password("pass1"));
         r2user1.updateCredential(UserCredentialModel.password("pass2"));
 
-        Assert.assertTrue(session.users().validCredentials(realm1, r1user1, UserCredentialModel.password("pass1")));
-        Assert.assertFalse(session.users().validCredentials(realm1, r1user1, UserCredentialModel.password("pass2")));
-        Assert.assertFalse(session.users().validCredentials(realm2, r2user1, UserCredentialModel.password("pass1")));
-        Assert.assertTrue(session.users().validCredentials(realm2, r2user1, UserCredentialModel.password("pass2")));
+        Assert.assertTrue(session.users().validCredentials(session, realm1, r1user1, UserCredentialModel.password("pass1")));
+        Assert.assertFalse(session.users().validCredentials(session, realm1, r1user1, UserCredentialModel.password("pass2")));
+        Assert.assertFalse(session.users().validCredentials(session, realm2, r2user1, UserCredentialModel.password("pass1")));
+        Assert.assertTrue(session.users().validCredentials(session, realm2, r2user1, UserCredentialModel.password("pass2")));
 
         // Test searching
         Assert.assertEquals(2, session.users().searchForUser("user", realm1).size());
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
index 3f4a23b..37e36e0 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
@@ -102,7 +102,7 @@ public class ReadUsersWorker implements Worker {
 
             // Validate password (shoould be same as username)
             if (readPassword) {
-                session.users().validCredentials(realm, user, UserCredentialModel.password(username));
+                session.users().validCredentials(session, realm, user, UserCredentialModel.password(username));
             }
 
             // Read federatedIdentities of user
diff --git a/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java b/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java
index 7be53c6..47e1400 100644
--- a/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java
+++ b/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java
@@ -12,6 +12,7 @@ import org.jboss.aesh.console.command.invocation.CommandInvocation;
 import org.jboss.aesh.console.command.registry.AeshCommandRegistryBuilder;
 import org.jboss.aesh.console.command.registry.CommandRegistry;
 import org.keycloak.common.util.Base64;
+import org.keycloak.hash.DefaultPasswordHashProvider;
 import org.keycloak.models.Constants;
 import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 import org.keycloak.representations.idm.CredentialRepresentation;
@@ -145,7 +146,7 @@ public class AddUser {
         credentials.setType(CredentialRepresentation.PASSWORD);
         credentials.setHashIterations(iterations);
         credentials.setSalt(Base64.encodeBytes(salt));
-        credentials.setHashedSaltedValue(new Pbkdf2PasswordEncoder(salt).encode(password, iterations));
+        credentials.setHashedSaltedValue(new DefaultPasswordHashProvider().encode(password, salt, iterations));
 
         user.getCredentials().add(credentials);
 
@@ -289,4 +290,4 @@ public class AddUser {
         }
     }
 
-}
\ No newline at end of file
+}