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.

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.

Appendix: 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)