spring-projects/spring-security · error · IllegalArgumentException

Invalid prefix

Error message

Invalid prefix

What it means

BCrypt.gensalt throws this when the salt prefix string is not a valid BCrypt identifier. A valid prefix must start with "$2" and its third character must be 'a', 'y', or 'b' (the supported BCrypt versions 2a, 2y, 2b). The library throws IllegalArgumentException to fail fast rather than emitting a salt that no BCrypt implementation can verify.

Source

Thrown at crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java:692

		return rs.toString();
	}

	/**
	 * Generate a salt for use with the BCrypt.hashpw() method
	 * @param prefix the prefix value (default $2a)
	 * @param log_rounds the log2 of the number of rounds of hashing to apply - the work
	 * factor therefore increases as 2**log_rounds.
	 * @param random an instance of SecureRandom to use
	 * @return an encoded salt value
	 * @exception IllegalArgumentException if prefix or log_rounds is invalid
	 */
	public static String gensalt(String prefix, int log_rounds, SecureRandom random) throws IllegalArgumentException {
		StringBuilder rs = new StringBuilder();
		byte rnd[] = new byte[BCRYPT_SALT_LEN];

		if (!prefix.startsWith("$2")
				|| (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) {
			throw new IllegalArgumentException("Invalid prefix");
		}
		if (log_rounds < 4 || log_rounds > 31) {
			throw new IllegalArgumentException("Invalid log_rounds");
		}

		random.nextBytes(rnd);

		rs.append("$2");
		rs.append(prefix.charAt(2));
		rs.append("$");
		if (log_rounds < 10) {
			rs.append("0");
		}
		rs.append(log_rounds);
		rs.append("$");
		encode_base64(rnd, rnd.length, rs);
		return rs.toString();
	}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Pass one of the exact prefixes "$2a", "$2y", or "$2b" (or use BCryptPasswordEncoder's BCryptVersion enum instead of a raw string).
  2. If you have an existing encoded hash, extract the prefix via hash.substring(0, 3) rather than passing the whole hash.
  3. Validate prefix.startsWith("$2") && "ayb".indexOf(prefix.charAt(2)) >= 0 before calling to fail with a clearer message.

Example fix

// before
BCrypt.gensalt("$2x", 10);
// after
BCrypt.gensalt("$2a", 10); // or BCrypt.gensalt(BCrypt.BCryptVersion.$2A)
Defensive patterns

Strategy: validation

Validate before calling

boolean validPrefix(String p) { return p != null && p.length() >= 3 && p.startsWith("$2") && (p.charAt(2)=='a' || p.charAt(2)=='y' || p.charAt(2)=='b'); }

Prevention

When it happens

Trigger: Calling BCrypt.gensalt(prefix, log_rounds, random) with a prefix like "$1" (MD5 crypt), "$2" with no version char, "$2c", "$2x", or an empty string — anything failing the startsWith("$2") or charAt(2) in {'a','y','b'} check.

Common situations: Porting salt strings from other crypt implementations (e.g. $1$ or $6$ prefixes from glibc crypt), hand-copying hashed salts truncated to two characters, or passing a full hash as the prefix instead of just the "$2a" portion.

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/fb096873b5050d37. Report an issue: GitHub.