Skip to content
David Bertoldi edited this page Oct 7, 2022 · 5 revisions

PBKDF2 (Password-Based Key Derivation Function 2) is a key derivation function with a sliding computational cost, used to reduce vulnerabilities to brute force attacks.

📑 Define PBKDF2 parameters

PBKDF2 accepts 3 parameters: the algorithm used as pseudo-random function, the number of iterations and the desired length of the output in bytes.

Name Default value Properties Description
Algorithm SHA-256 hash.pbkdf2.algorithm Defines the pseudo-random function from the HMAC family
Iterations 310000 hash.pbkdf2.iterations Defines the number of times the pseudo-random function is applied to the password along with the salt
Output length 256 hash.pbkdf2.length Defines the desired length of the final derived key

The HMAC is defined via com.password4j.types.Hmac enum. In case your JVM does not support a specific HMAC declared in this enum a UnsupportedOperationException is thrown. On the other hand, if you want to use a HMAC function not listed in the enum, you can specify it as a String.

You can define a singleton custom PBKDF2 function by calling PBKDF2Function.getInstance(String, int, int) or PBKDF2Function.getInstance(Hmac, int, int)

PBKDF2Function pbkdf2 = PBKDF2Function.getInstance(Hmac.SHA256, 100000, 1024);

In this case you have created a singleton instance which uses HMAC SHA-256 as pesudo-random function, performs 100000 iterations and produces an hash of 1024 bytes.

Alternatively if you have defined the parameters in the psw4j.properties file

PBKDF2Function pbkdf2 = AlgorithmFinder.getPBKDF2Instance();

#️⃣ How to hash passwords

Hashing passwords with PBKDF2 can be done quite easily.

Hash hash = Password.hash(plainTextPassword).withPBKDF2();

hash.getResult(); // /WTQfTTc8Hg8GlplP0LthpgdElUG+I3MyuvK8MI4MnQ=

This approach takes the parameters from psw4j.properties file (like AlgorithmFinder.getPBKDF2Instance()). However it's possible to use user-defined parameters as we saw previously

PBKDF2Function myPbkdf2 = PBKDF2Function.getInstance("HAVAL", 2000, 128);

Password.hash(plainTextPassword).with(myPbkdf2);

Add salt

You have two way to define the cryptographic salt.

Explicit

Method addSalt(String) make you define the intended salt.

Hash hash = Password.hash(plainTextPassword).addSalt("a1b2c3d4").withPBKDF2();

For security reasons, the salt must never be the same.

Randomly

Method addRandomSalt(int) adds a random generated salt with a defined length (in bytes)

Hash hash = Password.hash(plainTextPassword).addRandomSalt(42).withPBKDF2();

Alternatively, you can use addRandomSalt() (no arguments) to define a random salt of 64 bytes

Hash hash = Password.hash(plainTextPassword).addRandomSalt().withPBKDF2();

Add pepper

Method addPepper(CharSequence) make you define the intended pepper.

Hash hash = Password.hash(plainTextPassword).addPepper("AlicePepper").withPBKDF2();

Alternatively you can define the pepper in the psw4j.properties file at the property global.pepper

Hash hash = Password.hash(plainTextPassword).addPepper().withPBKDF2();

✔️ How to check the hash

Ideally the hash and the salt are retrieved from the database. Once retrieved those information you can check the user-provided passwords against the hash and salt from your database.

String hashFromDB = getHashFromDatabase(user);
String saltFromDB = getSaltFromDatabase(user);

boolean verified = Password.check(userProvidedPassword, hashFromDB).addSalt(saltFromDB).withPBKDF2();

The parameters used are taken from your psw4j.properties file. Alternatively you can define your own parameters

String hashFromDB = getHashFromDatabase(user);
String saltFromDB = getSaltFromDatabase(user);

PBKDF2Function myPbkdf2 = PBKDF2Function.getInstance("HAVAL", 2000, 128);

boolean verified = Password.check(userProvidedPassword, hashFromDB).addSalt(saltFromDB).with(myPbkdf2);

🔄 How to update the hash

If you want to migrate your cryptographic hashes from the original configuration to a more secure one, you can refresh them during the first user login.

String hashFromDB = getHashFromDatabase(user);
String saltFromDB = getSaltFromDatabase(user);

PBKDF2Function myPbkdf2 = PBKDF2Function.getInstance(Hmac.SHA512, 100000, 4096);

HashUpdate update = Password.check(userProvidedPassword, hashFromDB).addSalt(saltFromDB)
                            .andUpdate()
                            .addNewRandomSalt().with(myPbkdf2);

if(update.isVerified()) 
{
    Hash newHash = update.getHash();
    storeNewHash(user, newHash.getHash());
    storeNewSalt(user, newHash.getSalt());
}

You can switch to any other hashing function offered by Password4j (for example Argon2)

PBKDF2Function oldFunction = AlgorithmFinder.getPBKDF2Function();
Argon2Function newFunction = AlgorithmFinder.getArgon2Function();

HashUpdate update = Password.check(userProvidedPassword, hashFromDB).addSalt(saltFromDB)
                            .andUpdate()
                            .addNewRandomSalt().with(oldFunction, newFunction);