How bcrypt Works – Java Password Hashing Security
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
bcrypt is an adaptive password-hashing function built around Blowfish's expensive key setup. It creates a random salt and repeats expensive setup work according to a logarithmic cost. Raising the cost by one roughly doubles the work, so a service can increase it as hardware improves.
For example, registering two users with the same password should still produce different stored bcrypt strings because each call uses a new salt. During login, the library reads the version, cost, and salt from the stored string, hashes the submitted password in the same way, and compares the result; the application does not decrypt the password.
bcrypt remains practical for existing password databases, but the cost must be benchmarked under peak authentication load rather than copied from a tutorial. It also processes only the first 72 password bytes, which matters for long Unicode input. For a new application, OWASP prefers memory-hard Argon2id; bcrypt is mainly a well-supported legacy option when configured and migrated carefully.
How bcrypt Works – Java Password Hashing Security
Direct Answer
bcrypt is an adaptive password-hashing function built around the expensive Blowfish key setup. It generates a random salt, applies work controlled by a logarithmic cost, and stores version, cost, salt, and result in its standard 60-character string. Increasing the cost by one roughly doubles the work. bcrypt remains useful for existing systems, but it has a 72-byte password-input limit and OWASP prefers memory-hard Argon2id for new applications.
Code Example
@Bean
PasswordEncoder passwordEncoder() {
int cost = measuredCostForThisService();
return new BCryptPasswordEncoder(cost);
}
String stored = passwordEncoder.encode(candidate);
boolean accepted = passwordEncoder.matches(candidate, stored);
Choose the cost from a load test that includes peak concurrent logins. Record the chosen policy and use upgradeEncoding or an equivalent check to migrate older values after successful authentication.
Common Mistakes & Pitfalls
- Calling encode multiple times on the same password: Freshers sometimes do encoder.encode(encoder.encode(password)) thinking more hashing is more secure, but bcrypt output is 60 characters of mixed alphanumeric—hashing it again produces a hash of a hash, breaking the verification logic entirely.
- Using cost factor 4-6 for speed: Setting a low cost factor defeats the entire purpose of bcrypt. Cost 4 takes milliseconds and gives attackers the speed advantage you're trying to prevent. Start at 10 minimum, tune to 12-14 for production.
- Comparing hashes with equals: Never do storedHash.equals(encoder.encode(input))—this creates a new hash with a new salt, so it'll never match. Always use encoder.matches(), which extracts the salt from storedHash and recomputes correctly.
- Thinking bcrypt is encryption: Saying bcrypt encrypts passwords reveals a fundamental misunderstanding—encryption is reversible with a key, hashing is mathematically one-way.
Check your understanding
Answer these without looking back at the article.
- What cost factor should you use in production and how do you decide?
- How does BCryptPasswordEncoder.matches() work without storing the salt separately?
- What is Argon2 and how does it differ from bcrypt?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.