-
Notifications
You must be signed in to change notification settings - Fork 26
PBKDF2
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.
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();
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);
You have two way to define the cryptographic salt.
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.
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();
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();
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);
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);