Understanding BCrypt and Password Hashing in Spring Security
This article explains the differences between hashing and encryption, demonstrates Java's built‑in hash implementations, discusses the insecurity of MD5 and rainbow‑table attacks, and provides a detailed guide on using BCrypt with Spring Security, including code examples and the PasswordEncoder interface.
Introduction – The article introduces the built‑in encryption algorithm BCrypt in Spring Security, highlighting its reputation as a highly secure hashing method.
Hash (Hash) vs. Encryption (Encrypt)
Hashing converts input text into a fixed‑length, irreversible digest, while encryption transforms input into a reversible ciphertext of variable length.
Hash algorithms generate outputs of the same length; encryption outputs vary with the plaintext length.
Hashes are one‑way; encryption is reversible.
Although a hash is not an encryption algorithm, its one‑way nature makes it a component of security mechanisms.
JDK String hashCode Implementation
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}The algorithm follows the formula s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] , where s[i] is the character at index i and n is the string length.
HashMap Secondary Hashing
HashMap first calls hashCode() on the key, then applies a secondary hash:
// Compute secondary hash
int hash = hash(key.hashCode());
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}Despite different formulas, the secondary hash ensures that identical keys produce the same final hash value.
Why Is a Hash Irreversible?
Because information is lost during the hashing process; many different inputs can produce the same hash (e.g., a simple addition hash of 3+4=7 cannot be uniquely reversed).
Why MD5 Is Insecure
MD5 produces a fixed hash for a given input, making it vulnerable to brute‑force enumeration, dictionary attacks, and rainbow tables. Even though brute‑force on a single machine is costly, distributed computing can still crack MD5 hashes.
Defending Against Rainbow‑Table Attacks
The most effective defense is adding a unique salt to each password before hashing, which makes pre‑computed rainbow tables ineffective.
BCrypt – A More Secure Hashing Algorithm
BCrypt, designed by Niels Provos and David Mazières, is based on the Blowfish cipher and includes built‑in random salting and an adjustable cost factor to slow down brute‑force attacks.
In Spring Security, BCrypt can be used as follows:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}The generated hash format is $2b$[cost]$[22‑character‑salt][31‑character‑hash] . Example:
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWyHere $2a$ identifies BCrypt, 10 is the cost factor (2^10 = 1024 rounds), the next 22 characters are the Base64‑encoded salt, and the final 31 characters are the Base64‑encoded hash.
PasswordEncoder Interface
Spring Security provides the PasswordEncoder interface with three methods:
public interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
default boolean upgradeEncoding(String encodedPassword) { return false; }
}encode – hashes a raw password for storage (e.g., during user registration).
matches – verifies a raw password against a stored hash (e.g., during login).
upgradeEncoding – determines whether a stored hash needs re‑encoding.
Example usage in a registration flow:
// Save user with hashed password
user.setPassword(passwordEncoder.encode(user.getPassword()));BCryptPasswordEncoder is the recommended implementation:
public class PasswordEncoderTest {
@Test
void bCryptPasswordTest() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String rawPassword = "123456"; // original password
String encodedPassword = passwordEncoder.encode(rawPassword);
System.out.println("Original password" + rawPassword);
System.out.println("Hashed password:" + encodedPassword);
System.out.println(rawPassword + " matches " + encodedPassword + ":" + passwordEncoder.matches(rawPassword, encodedPassword));
System.out.println("654321 matches " + encodedPassword + ":" + passwordEncoder.matches("654321", encodedPassword));
}
}Running the test shows that each call to encode produces a different hash due to random salting, making BCrypt resistant to rainbow‑table and brute‑force attacks.
In summary, BCrypt provides random salts, adjustable cost, and strong security, making it the preferred password‑hashing algorithm in Spring Security.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.