-
Notifications
You must be signed in to change notification settings - Fork 27
BCrypt
bcrypt is a password-hashing function, based on the Blowfish cipher. Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.
bcrypt accepts 2 parameters: the version of the algorithm (a
, x
, y
or b
) and the cost factor, that is the number of rounds in logarithmic form (e.g. cost factor of 10 means 210 = 1024 rounds).
Name | Default value | Properties | Description |
---|---|---|---|
Version | b | hash.bcrypt.minor |
Defines the minor version of bcrypt |
Cost factor | 10 | hash.bcrypt.rounds |
Defines the number of rounds expressed as exponent of base 2 |
The suggested version of the algorithm is b
, the latest one. The other versions should be used only for backward compatibility reasons and we recommend to update your hashes as soon as possible.
You can define a singleton custom bcrypt function by calling BCryptFunction.getInstance(BCrypt, int)
or BCryptFunction.getInstance(int)
BCryptFunction bcrypt = BCryptFunction.getInstance(BCrypt.Y, 11);
In this case you have created a singleton instance which uses version y
of bcrypt and has a cost factor of 211 = 2048.
Alternatively if you have defined the parameters in the psw4j.properties
file
BCryptFunction bcrypt = AlgorithmFinder.getBCryptInstance();
Hashing passwords with bcrypt can be done quite easily.
Hash hash = Password.hash(plainTextPassword).withBCrypt();
hash.getResult(); // $2b$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS
This approach takes the parameters from psw4j.properties
file (like AlgorithmFinder.getBCryptInstance()
).
However it's possible to use user-defined parameters as we saw previously
BCryptFunction myBcrypt = BCryptFunction.getInstance(BCrypt.Y, 11);
Password.hash(plainTextPassword).with(myBcrypt);
bcrypt calculates the salt by its own depending on the algorithm's parameters and it is not possible to specify a custom salt in order to avoid inconsistencies.
Method addPepper(CharSequence)
make you define the intended pepper.
Hash hash = Password.hash(plainTextPassword).addPepper("AlicePepper").withBCrypt();
Alternatively you can define the pepper in the psw4j.properties
file at the property global.pepper
Hash hash = Password.hash(plainTextPassword).addPepper().withBCrypt();
The pepper is always prepended.
Ideally the hash is retrieved from the database. Once retrieved you can check the user-provided passwords against the hash from your database. The salt is always encoded within the hash.
String hashFromDB = getHashFromDatabase(user);
boolean verified = Password.check(userProvidedPassword, hashFromDB).withBCrypt();
The parameters used are taken from your psw4j.properties
file.
Alternatively you can define your own parameters
String hashFromDB = getHashFromDatabase(user);
BCryptFunction myBcrypt = BCryptFunction.getInstance(BCrypt.Y, 11);
boolean verified = Password.check(userProvidedPassword, hashFromDB).with(myBcrypt);
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);
BCryptFunction myBcrypt = BCryptFunction.getInstance(BCrypt.B, 12);
HashUpdate update = Password.check(userProvidedPassword, hashFromDB)
.andUpdate()
.addNewRandomSalt().with(myBcrypt);
if(update.isVerified())
{
Hash newHash = update.getHash();
storeNewHash(user, newHash.getHash());
}
You can switch to any other hashing function offered by Password4j (for example Argon2)
BCryptFunction oldFunction = BCryptFunction.getInstance(BCrypt.A, 10);
Argon2Function newFunction = AlgorithmFinder.getArgon2Function();
HashUpdate update = Password.check(userProvidedPassword, hashFromDB)
.andUpdate()
.addNewRandomSalt().with(oldFunction, newFunction);