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

  1. Generate salts with BCrypt.gensalt() instead of supplying your own bytes
  2. If decoding the embedded salt, use BCrypt's decode_base64 semantics; a $2a$/$2b$ salt substring is 22 base64 chars = 16 bytes
  3. 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

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.