spring-projects/spring-security · error · IllegalArgumentException
Bad salt length
Error message
Bad salt length
What it means
BCrypt.crypt_raw() requires the raw salt byte array to be exactly BCRYPT_SALT_LEN (16) bytes long. Any other length throws "Bad salt length". This guards the internal Blowfish key schedule which consumes exactly 16 salt bytes.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java:552
long rounds;
if (log_rounds < 4 || log_rounds > 31) {
if (!for_check) {
throw new IllegalArgumentException("Bad number of rounds");
}
if (log_rounds != 0) {
throw new IllegalArgumentException("Bad number of rounds");
}
rounds = 0;
}
else {
rounds = roundsForLogRounds(log_rounds);
if (rounds < 16 || rounds > 2147483648L) {
throw new IllegalArgumentException("Bad number of rounds");
}
}
if (salt.length != BCRYPT_SALT_LEN) {
throw new IllegalArgumentException("Bad salt length");
}
init_key();
ekskey(salt, password, sign_ext_bug, safety);
for (int i = 0; i < rounds; i++) {
key(password, sign_ext_bug, safety);
key(salt, false, safety);
}
for (int i = 0; i < 64; i++) {
for (int j = 0; j < (clen >> 1); j++) {
encipher(cdata, j << 1);
}
}
byte[] ret = new byte[clen * 4];
for (int i = 0, j = 0; i < clen; i++) {
ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);View on GitHub (pinned to 96852e8860)
Solutions
- Generate salts with BCrypt.gensalt() instead of supplying your own bytes
- If decoding the embedded salt, use BCrypt's decode_base64 semantics; a $2a$/$2b$ salt substring is 22 base64 chars = 16 bytes
- Ensure your Base64 decode is unpadded bcrypt alphabet, not standard Base64
Example fix
// before byte[] salt = Base64.getDecoder().decode(realSalt); // wrong alphabet, wrong length // after String saltStr = BCrypt.gensalt(12); // let the library create the salt
Defensive patterns
Strategy: validation
Validate before calling
byte[] decoded = customDecode(realSalt);
if (decoded.length != 16) {
throw new IllegalArgumentException("bcrypt salt must decode to 16 bytes");
} Try / catch
try {
hash = BCrypt.hashpw(pw, salt);
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Bad salt length")) { /* regenerate salt via gensalt */ }
} Prevention
- Use BCrypt.gensalt() for salt generation
- Remember bcrypt's base64 alphabet differs from standard Base64 (no padding, ./ alphabet)
When it happens
Trigger: Passing a salt byte array of length != 16 directly to BCrypt.hashpw's internal path / crypt_raw, e.g. a Base64-decoded salt with wrong padding or a raw password string used as salt.
Common situations: Manually decoding the salt portion of a $2a$ hash with a decoder that adds/strips padding, generating salt with another library producing non-16-byte salts, or truncating byte arrays.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/82ce46169ba72a7b.
Report an issue: GitHub.