DevNotes
Support
Spring Security & JWT · Chapter 02

BCrypt Password Hashing in Java

Encoding and verifying passwords with BCryptPasswordEncoder, why the same password gives different hashes, the structure of a BCrypt hash, the cost factor, and why BCrypt beats SHA-256 for passwords.

◷ 5 min read§ 13 sections? 7 interview questions{ } 7 code examples

#Introduction

BCrypt is a password-hashing algorithm designed specifically for storing passwords. It automatically generates a random salt for every password and is resistant to brute-force attacks because it's intentionally slow — and you can make it slower as hardware gets faster.

#Maven Dependency (Spring Boot)

XML
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

(Outside Spring Boot, the spring-security-crypto module alone provides BCryptPasswordEncoder.)

#Encode a Password

Java
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class Main {
    public static void main(String[] args) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        String password = "admin123";
        String hashedPassword = encoder.encode(password);

        System.out.println("Original Password : " + password);
        System.out.println("BCrypt Hash       : " + hashedPassword);
    }
}
Example output (your hash will differ)
Original Password : admin123
BCrypt Hash       : $2a$10$7uXf1fO0e2Qx9QJ5kJzJ3uN4wGm7hX6v1xJ5wPzD4YvX0M2nY8c7e

#Verify a Password

Java
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

String rawPassword = "admin123";
String storedHash = encoder.encode(rawPassword);   // normally loaded from the database

boolean matches = encoder.matches(rawPassword, storedHash);
System.out.println(matches);
Output
true

matches() hashes the entered password using the salt embedded in the stored hash, then compares the results.

#Why Does the Same Password Produce Different Hashes?

Java
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println(encoder.encode("admin123"));
System.out.println(encoder.encode("admin123"));
Example output (shortened)
$2a$10$abc..............
$2a$10$xyz..............

The password is identical, but each hash is different because BCrypt creates a new random salt on every call. Verification still succeeds for both:

Java
encoder.matches("admin123", hash1);   // true
encoder.matches("admin123", hash2);   // true

#Structure of a BCrypt Hash

Text
$2a$10$7uXf1fO0e2Qx9QJ5kJzJ3uN4wGm7hX6v1xJ5wPzD4YvX0M2nY8c7e
PartMeaning
$2a$BCrypt algorithm version ($2a$, $2b$ or $2y$)
10Cost factor (work factor)
Next 22 charactersRandom salt
Remaining 31 charactersPassword hash

A BCrypt hash is always 60 characters long — size your database column accordingly (VARCHAR(60) or larger, e.g. VARCHAR(100) if you use the delegating encoder's {bcrypt} prefix).

#Spring Security Configuration

Java
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
Java
@Autowired
private PasswordEncoder passwordEncoder;

String hash = passwordEncoder.encode("password");
boolean valid = passwordEncoder.matches("password", hash);

#The Cost Factor

Java
new BCryptPasswordEncoder();      // default cost = 10
new BCryptPasswordEncoder(12);    // custom cost

The cost is a logarithm: BCrypt performs 2^cost rounds of its key-setup function. Each +1 doubles the work — cost 12 is 4× slower than cost 10.

A higher cost means:

  • more security;
  • slower hashing;
  • much harder brute-force attacks.

#Why BCrypt Instead of SHA-256?

SHA-256BCrypt
Very fast (billions per second on a GPU)Intentionally slow
No built-in saltGenerates a salt automatically
Vulnerable to brute-force attacksResistant to brute-force attacks
Same input → same hashSame input → different hash (random salt)
General-purpose hashing (checksums, signatures)Designed specifically for password hashing

#The Authentication Flow

Registration — user enters a password
Application hashes it with BCrypt
BCrypt hash stored in the database
Login — user enters the password again
matches() extracts the salt from the stored hash
Entered password hashed with that salt
Hashes equal?
authentication succeeds
login rejected

#Interview Questions

Q11. Why should we use BCrypt instead of SHA-256?

SHA-256 is a fast hash, so attackers can try billions of guesses per second. BCrypt is intentionally slow, automatically adds a random salt, and has a tunable cost factor, making it far more secure for passwords.

Q22. Do we need to store the salt separately?

No. BCrypt embeds the salt directly in the generated hash, so no separate database column is needed.

Q33. What is the cost factor?

The logarithm of the number of hashing rounds — BCrypt performs 2^cost rounds. Higher cost means better security but more CPU time per hash; each +1 doubles the work.

Q44. Can BCrypt hashes be decrypted?

No. BCrypt is a one-way hashing algorithm, not encryption. The original password can't be recovered; attackers can only guess and check.

Q55. Why does the same password generate different hashes?

Because BCrypt generates a new random salt every time encode() is called.

Q66. How does matches() work?

It extracts the version, cost and salt from the stored hash, hashes the user-entered password with that same salt and cost, and compares the result with the stored hash.

Q77. What happens to passwords longer than 72 bytes?

BCrypt ignores everything after the first 72 bytes, so two long passwords with the same first 72 bytes would match.

#Common Mistakes

  • Comparing hashes with equals().
  • A database column that's too short — BCrypt hashes are 60 characters.
  • A cost factor that's too low (e.g. 4) — or so high that logins time out.
  • Re-hashing the stored hash instead of the raw password.

#Key Takeaways

  • BCrypt is a slow, salted, one-way hash built for passwords.
  • The salt and cost are stored inside the 60-character hash.
  • The cost factor is logarithmic — each +1 doubles the work.
  • Same password → different hashes, but matches() still verifies them.
  • Prefer BCrypt (or Argon2) over fast hashes like SHA-256 for passwords.