FusionAuth developer image
FusionAuth developer logo
  • Back to site
  • Expert Advice
  • Blog
  • Developers
  • Downloads
  • Account
  • Contact sales
Navigate to...
  • Welcome
  • Getting Started
    • Getting Started
    • 5-minute Setup Guide
      • Overview
      • Docker
      • Fast Path
      • Sandbox
    • Setup Wizard & First Login
    • Register a User and Login
    • Self-service Registration
    • Start and Stop FusionAuth
    • Core Concepts
      • Overview
      • Users
      • Roles
      • Groups
      • Registrations
      • Applications
      • Tenants
      • Identity Providers
      • Authentication/Authorization
      • Integration Points
    • Example Apps
      • Overview
      • Dart
      • Go
      • Java
      • JavaScript
      • .NET Core
      • PHP
      • Python
      • Ruby
    • Tutorials
  • Installation Guide
    • Overview
    • System Requirements
    • Server Layout
    • Cloud
    • Cluster
    • Docker
    • Fast Path
    • Kubernetes
      • Overview
      • Deployment Guide
      • Minikube Setup
      • Amazon EKS Setup
      • Google GKE Setup
      • Microsoft AKS Setup
    • Kickstart™
    • Homebrew
    • Marketplaces
    • Packages
    • Database
    • FusionAuth App
    • FusionAuth Search
    • Common Configuration
  • Migration Guide
    • Overview
    • General
    • Auth0
    • Keycloak
    • Amazon Cognito
    • Firebase
    • Microsoft Azure AD B2C
    • Tutorial
  • Admin Guide
    • Overview
    • Account Portal
    • Config Management
    • Editions and Features
    • Key Rotation
    • Licensing
    • Monitoring
    • Prometheus Setup
    • Proxy Setup
    • Reference
      • Overview
      • Configuration
      • CORS
      • Data Types
      • Hosted Login Pages Cookies
      • Known Limitations
      • Password Hashes
    • Releases
    • Roadmap
    • Search And FusionAuth
    • Securing
    • Switch Search Engines
    • Technical Support
    • Troubleshooting
    • Upgrading
    • WebAuthn
  • Login Methods
    • Identity Providers
      • Overview
      • Apple
      • Epic Games
      • External JWT
        • Overview
        • Example
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
        • Overview
        • Amazon Cognito
        • Azure AD
        • Discord
        • Github
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • SAML v2
        • Overview
        • ADFS
        • Azure AD
      • SAML v2 IdP Initiated
        • Overview
        • Okta
      • Xbox
    • OIDC & OAuth 2.0
      • Overview
      • Endpoints
      • Tokens
      • OAuth Modes
    • Passwordless
      • Overview
      • Magic Links
      • WebAuthn & Passkeys
    • SAML v2 IdP
      • Overview
      • Google
      • Zendesk
  • Developer Guide
    • Overview
    • API Gateways
      • Overview
      • ngrok Cloud Edge
    • Client Libraries & SDKs
      • Overview
      • Dart
      • Go
      • Java
      • JavaScript
      • .NET Core
      • Node
      • OpenAPI
      • PHP
      • Python
      • React
      • Ruby
      • Typescript
    • Events & Webhooks
      • Overview
      • Writing a Webhook
      • Securing Webhooks
      • Events
        • Overview
        • Audit Log Create
        • Event Log Create
        • JWT Public Key Update
        • JWT Refresh
        • JWT Refresh Token Revoke
        • Kickstart Success
        • Group Create
        • Group Create Complete
        • Group Delete
        • Group Delete Complete
        • Group Update
        • Group Update Complete
        • Group Member Add
        • Group Member Add Complete
        • Group Member Remove
        • Group Member Remove Complete
        • Group Member Update
        • Group Member Update Complete
        • User Action
        • User Bulk Create
        • User Create
        • User Create Complete
        • User Deactivate
        • User Delete
        • User Delete Complete
        • User Email Update
        • User Email Verified
        • User IdP Link
        • User IdP Unlink
        • User Login Failed
        • User Login Id Dup. Create
        • User Login Id Dup. Update
        • User Login New Device
        • User Login Success
        • User Login Suspicious
        • User Password Breach
        • User Password Reset Send
        • User Password Reset Start
        • User Password Reset Success
        • User Password Update
        • User Reactivate
        • User Reg. Create
        • User Reg. Create Complete
        • User Reg. Delete
        • User Reg. Delete Complete
        • User Registration Update
        • User Reg. Update Complete
        • User Reg. Verified
        • User 2FA Method Add
        • User 2FA Method Remove
        • User Update
        • User Update Complete
    • Guides
      • Overview
      • Authentication Tokens
      • Exposing A Local Instance
      • JSON Web Tokens
      • Key Master
      • Localization and Internationalization
      • Multi-Factor Authentication
      • Multi-Tenant
      • Passwordless
      • Registration-based Email Verification
      • Searching With Elasticsearch
      • Securing Your APIs
      • Silent Mode
      • Single Sign-on
      • Two Factor (pre 1.26)
    • Integrations
      • Overview
      • CleanSpeak
      • Kafka
      • Twilio
    • Plugins
      • Overview
      • Writing a Plugin
      • Custom Password Hashing
    • User Control & Gating
      • Overview
      • Gate Unverified Users
      • Gate Unverified Registrations
      • User Account Lockout
  • Customization
    • Email & Templates
      • Overview
      • Configure Email
      • Email Templates
      • Email Variables
      • Message Templates
    • Lambdas
      • Overview
      • Apple Reconcile
      • Client Cred. JWT Populate
      • Epic Games Reconcile
      • External JWT Reconcile
      • Facebook Reconcile
      • Google Reconcile
      • HYPR Reconcile
      • JWT Populate
      • LDAP Connector Reconcile
      • LinkedIn Reconcile
      • Nintendo Reconcile
      • OpenID Connect Reconcile
      • SAML v2 Populate
      • SAML v2 Reconcile
      • SCIM Group Req. Converter
      • SCIM Group Resp. Convtr.
      • SCIM User Req. Converter
      • SCIM User Resp. Converter
      • Sony PSN Reconcile
      • Steam Reconcile
      • Twitch Reconcile
      • Twitter Reconcile
      • Xbox Reconcile
    • Messengers
      • Overview
      • Generic Messenger
      • Twilio Messenger
    • Themes
      • Overview
      • Examples
      • Helpers
      • Localization
      • Template Variables
  • Premium Features
    • Overview
    • Advanced Registration Forms
    • Advanced Threat Detection
    • Application Specific Themes
    • Breached Password Detection
    • Connectors
      • Overview
      • Generic Connector
      • LDAP Connector
      • FusionAuth Connector
    • Entity Management
    • SCIM
      • Overview
      • Azure AD Client
      • Okta Client
      • SCIM-SDK
    • Self Service Account Mgmt
      • Overview
      • Updating User Data & Password
      • Add Two-Factor Authenticator
      • Add Two-Factor Email
      • Add Two-Factor SMS
      • Add WebAuthn Passkey
      • Customizing
      • Troubleshooting
    • WebAuthn
  • APIs
    • Overview
    • Authentication
    • Errors
    • API Explorer
    • Actioning Users
    • API Keys
    • Applications
    • Audit Logs
    • Connectors
      • Overview
      • Generic
      • LDAP
    • Consents
    • Emails
    • Entity Management
      • Overview
      • Entities
      • Entity Types
      • Grants
    • Event Logs
    • Families
    • Forms
    • Form Fields
    • Groups
    • Identity Providers
      • Overview
      • Links
      • Apple
      • External JWT
      • Epic Games
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
      • SAML v2
      • SAML v2 IdP Initiated
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • Xbox
    • Integrations
    • IP Access Control Lists
    • JWT
    • Keys
    • Lambdas
    • Login
    • Message Templates
    • Messengers
      • Overview
      • Generic
      • Twilio
    • Multi-Factor/Two Factor
    • Passwordless
    • Reactor
    • Registrations
    • Reports
    • SCIM
      • Overview
      • SCIM User
      • SCIM Group
      • SCIM EnterpriseUser
      • SCIM Service Provider Config.
    • System
    • Tenants
    • Themes
    • Users
    • User Actions
    • User Action Reasons
    • User Comments
    • WebAuthn
    • Webhooks
  • Release Notes

    Custom Password Hashing (Password Encryptors)

    Overview

    There are times when you have a custom password hash that you want to import into FusionAuth. FusionAuth supports a number of password hashing schemes but you can write a custom plugin if you have hashed your passwords using a different scheme.

    You can use your custom password hashing scheme going forward, or you can rehash your passwords. You’d use the former strategy if you wanted to use a strong, unsupported password hashing scheme such as Argon2. You’d use the latter strategy if you are migrating from a system with a weaker hashing algorithm.

    • Write the Password Encryptor Class

    • Adding the Guice Bindings

    • Writing a Unit Test

    • Integration Test

    • Sample Code

    • Rehashing User Passwords

    This code uses the words 'encryption' and 'encryptor' for backwards compatibility, but what it is really doing is hashing the password.

    Write the Password Encryptor Class

    The main plugin interface in FusionAuth is the Password Encryptors interface. This allows you to write a custom password hashing scheme. A custom password hashing scheme is useful when you import users from an existing database into FusionAuth so that the users don’t need to reset their passwords to login into your applications.

    To write a Password Encryptor, you must first implement the io.fusionauth.plugin.spi.security.PasswordEncryptor interface. Here’s an example Password Encryptor.

    Password Encryptor
    
    /*
     * Copyright (c) 2019, FusionAuth, All Rights Reserved
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
     * either express or implied. See the License for the specific
     * language governing permissions and limitations under the License.
     */
    package com.mycompany.fusionauth.plugins;
    
    import javax.crypto.Mac;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.KeySpec;
    
    import com.mycompany.fusionauth.util.HexTools;
    import io.fusionauth.plugin.spi.security.PasswordEncryptor;
    
    /**
     *  This is an example of a PBKDF2 HMAC SHA1 Salted hashing algorithm.
     *
     * <p>
     * This code is provided to assist in your deployment and management of FusionAuth. Use of this
     * software is not covered under the FusionAuth license agreement and is provided "as is" without
     * warranty. https://fusionauth.io/license
     * </p>
     *
     * @author Daniel DeGroff
     */
    public class ExamplePBDKF2HMACSHA1PasswordEncryptor implements PasswordEncryptor {
      private final int keyLength;
    
      public ExamplePBDKF2HMACSHA1PasswordEncryptor() {
        // Default key length is 512 bits
        this.keyLength = 64;
      }
    
      @Override
      public int defaultFactor() {
        return 10_000;
      }
    
      @Override
      public String encrypt(String password, String salt, int factor) {
        if (factor <= 0) {
          throw new IllegalArgumentException("Invalid factor value [" + factor + "]");
        }
    
        SecretKeyFactory keyFactory;
        try {
          keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        } catch (NoSuchAlgorithmException e) {
          throw new IllegalStateException("No such algorithm [PBKDF2WithHmacSHA1]");
        }
    
        KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), factor, keyLength * 8);
        SecretKey secret;
        try {
          secret = keyFactory.generateSecret(keySpec);
        } catch (InvalidKeySpecException e) {
          throw new IllegalArgumentException("Could not generate secret key for algorithm [PBKDF2WithHmacSHA1]");
        }
    
        SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getEncoded(), "HmacSHA1");
        Mac mac;
        try {
          mac = Mac.getInstance("HmacSHA1");
        } catch (NoSuchAlgorithmException e) {
          throw new IllegalStateException("No such algorithm [HmacSHA1]");
        }
    
        try {
          mac.init(secretKeySpec);
          byte[] hashedPassword = mac.doFinal(password.getBytes(StandardCharsets.UTF_8));
          return HexTools.encode(hashedPassword);
        } catch (InvalidKeyException e) {
          throw new IllegalArgumentException("Invalid key used to initialize HmacSHA1");
        }
      }
    }

    Adding the Guice Bindings

    To complete the main plugin code (before we write a unit test), you need to add Guice binding for your new Password Encryptor. Password Encryptors use Guice Multibindings via Map. Here is an example of binding our new Password Encryptor so that FusionAuth can use it for users.

    Guice Module
    
    /*
     * Copyright (c) 2020-2022, FusionAuth, All Rights Reserved
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
     * either express or implied. See the License for the specific
     * language governing permissions and limitations under the License.
     */
    package com.mycompany.fusionauth.plugins.guice;
    
    import com.google.inject.AbstractModule;
    import com.google.inject.multibindings.MapBinder;
    import com.mycompany.fusionauth.plugins.ExamplePBDKF2HMACSHA1PasswordEncryptor;
    import io.fusionauth.plugin.spi.PluginModule;
    import io.fusionauth.plugin.spi.security.PasswordEncryptor;
    
    /**
     * @author Daniel DeGroff
     */
    @PluginModule
    public class MyExampleFusionAuthPluginModule extends AbstractModule {
      @Override
      protected void configure() {
        MapBinder<String, PasswordEncryptor> passwordEncryptorMapBinder = MapBinder.newMapBinder(binder(), String.class, PasswordEncryptor.class);
    
        // TODO :
        //   1. Add one or more bindings here
        //   2. Name your binding. This will be the value you set in the 'encryptionScheme' on the user to utilize this encryptor.
        //   3. Delete any example code you don't use and do not want in your plugin. In addition to the bindings, you should delete any corresponding classes and tests you do not use in your plugin.
    
        // Example PBKDF2 with a SHA-1
        passwordEncryptorMapBinder.addBinding("example-salted-pbkdf2-hmac-sha1-10000").to(ExamplePBDKF2HMACSHA1PasswordEncryptor.class);
      }
    }

    You can see that we have bound the Password Encryptor under the name example-salted-pbkdf2-hmac-sha1-10000. This is the same name that you will use when creating users via the User API.

    Writing a Unit Test

    You’ll probably want to write some tests to ensure that your new Password Encryptor is working properly. Our example uses TestNG, but you can use JUnit or another framework if you prefer. Here’s a simple unit test for our Password Encryptor:

    Unit Test
    
    package com.mycompany.fusionauth.plugins;
    
    import io.fusionauth.plugin.spi.security.PasswordEncryptor;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    import static org.testng.Assert.assertEquals;
    
    /**
     * @author Daniel DeGroff
     */
    public class ExamplePBDKF2HMACSHA1PasswordEncryptorTest {
      @Test(dataProvider = "hashes")
      public void encrypt(String password, String salt, String hash) {
        PasswordEncryptor encryptor = new ExamplePBDKF2HMACSHA1PasswordEncryptor();
        assertEquals(encryptor.encrypt(password, salt, 10_000), hash);
      }
    
      @DataProvider(name = "hashes")
      public Object[][] hashes() {
        return new Object[][]{
            {"password123", "1484161696d0ca62390273b98846f49671cecd78", "4761D3392092F9CA6036B53DC92C6D7F3D597576"},
            {"password123", "ea95629c7954d73ea670f07a798e9fd4ab907593", "9480AD9A59CB5053B832BA5E731AFCD1F78068EC"},
        };
      }
    }

    To run the tests using the Java Maven build tool, run the following command.

    
    mvn test

    Integration Test

    After you have completed your plugin, the unit test and installed the plugin into a running FusionAuth installation, you can test it by hitting the User API and creating a test user. Here’s an example JSON request that uses the new Password Encryptor:

    
    {
      "user": {
        "id": "00000000-0000-0000-0000-000000000001",
        "active": true,
        "email": "test0@fusionauth.io",
        "encryptionScheme": "example-salted-pbkdf2-hmac-sha1-10000",
        "password": "password",
        "username": "username0",
        "timezone": "Denver",
        "data": {
          "attr1": "value1",
          "attr2": ["value2", "value3"]
        },
        "preferredLanguages": ["en", "fr"],
        "registrations": [
          {
            "applicationId": "00000000-0000-0000-0000-000000000042",
            "data": {
              "attr3": "value3",
              "attr4": ["value4", "value5"]
            },
            "id": "00000000-0000-0000-0000-000000000003",
            "preferredLanguages": ["de"],
            "roles": ["role 1"],
            "username": "username0"
          }
        ]
      }
    }

    Notice that we’ve passed in the encryptionScheme property with a value of example-salted-pbkdf2-hmac-sha1-10000. This will instruct FusionAuth to use your newly written Password Encryptor.

    Sample Code

    A sample plugin project is available. If you are looking to write your own custom password hashing algorithm, this project is a good starting point.

    There is also a selection of contributed plugins, provided by the community and made available without warranty. That may also be useful to you, as someone may already have written the hasher you need.

    Rehashing User Passwords

    The purpose of writing a custom password hasher is to import users into FusionAuth using an existing hashing scheme. This allows you to seamlessly import your users without requiring them to change their password. The downside of this approach is that you now have preserved a hash which may be weak. FusionAuth will continue to use that hash unless you rehash users' passwords.

    To remedy this common situation, FusionAuth has the ability to rehash passwords on user login. Once enabled, during the next login event for a given user, FusionAuth will transparently rehash that user’s password. The stronger, more secure hash will be used in the future for that user.

    To import users and transparently rehash their passwords, do the following:

    • Write a custom password hasher.

    • Import user passwords, setting the scheme for each user to the custom password hasher.

    • Decide on the new hashing scheme you want to use.

    • In the administrative user interface, navigate to Tenants → Your Tenant → Password and then to the Cryptographic hash settings section. Here you will configure both the new scheme and the rehash on login behavior.

    • Configure the tenant to use the new hashing scheme by selecting it. You may use one of the standard hashing schemes or a different custom scheme. This will be used for all new users in this tenant as well.

    • Configure the tenant to rehash on login by checking the Re-hash on login checkbox.

    • Save the tenant configuration.

    Tenant settings for rehashing passwords on login

    After you have enabled this, when a user logs in, the password they provide will be transparently rehashed and they will use the stronger scheme in the future.

    Currently rehashing a password when it is changed is not supported. Here’s the tracking issue for this feature.

    Feedback

    How helpful was this page?

    See a problem?

    File an issue in our docs repo

    Have a question or comment to share?

    Visit the FusionAuth community forum.

    © 2023 FusionAuth
    Subscribe for developer updates