spring-projects/spring-security · error · IllegalArgumentException

Unsupported password prefix '{prefix}'

Error message

Unsupported password prefix '{prefix}'

What it means

During matches(), getSalt examines the prefix extracted from the encoded password and throws this IllegalArgumentException when the prefix is neither {SHA}, {SSHA} nor their lowercase variants. It means the stored password was not produced by LdapShaPasswordEncoder (or was corrupted), so salt extraction — and therefore comparison — cannot proceed.

Source

Thrown at crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java:162

	 */
	@Override
	protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
		String prefix = extractPrefix(encodedPassword);
		if (prefix == null) {
			return PasswordEncoderUtils.equals(encodedPassword, rawPassword);
		}
		byte[] salt = getSalt(encodedPassword, prefix);
		int startOfHash = prefix.length();
		String encodedRawPass = encode(rawPassword, salt).substring(startOfHash);
		return PasswordEncoderUtils.equals(encodedRawPass, encodedPassword.substring(startOfHash));
	}

	private byte @Nullable [] getSalt(String encodedPassword, String prefix) {
		if (prefix.equals(SSHA_PREFIX) || prefix.equals(SSHA_PREFIX_LC)) {
			return extractSalt(encodedPassword);
		}
		if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
			throw new IllegalArgumentException("Unsupported password prefix '" + prefix + "'");
		}
		// Standard SHA
		return null;
	}

	/**
	 * Returns the hash prefix or null if there isn't one.
	 */
	private @Nullable String extractPrefix(String encPass) {
		if (!encPass.startsWith("{")) {
			return null;
		}
		int secondBrace = encPass.lastIndexOf('}');
		if (secondBrace < 0) {
			throw new IllegalArgumentException("Couldn't find closing brace for SHA prefix");
		}
		return encPass.substring(0, secondBrace + 1);
	}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Only feed this encoder hashes it produced: strings starting with {SHA} or {SSHA} (or lowercase forms).
  2. Re-encode the user's password with LdapShaPasswordEncoder and update the stored value.
  3. Use DelegatingPasswordEncoder to route each stored hash to the encoder matching its prefix instead of hard-wiring LdapShaPasswordEncoder.

Example fix

// before
boolean ok = ldapEncoder.matches(raw, stored); // stored = "{bcrypt}$2a$..."
// after
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
boolean ok = encoder.matches(raw, stored);
Defensive patterns

Strategy: validation

Validate before calling

if (!(stored.startsWith("{SHA}") || stored.startsWith("{SSHA}")
        || stored.startsWith("{sha}") || stored.startsWith("{ssha}"))) {
    throw new IllegalArgumentException("not an LDAP SHA/SSHA hash: " + prefixOf(stored));
}
boolean ok = ldapEncoder.matches(raw, stored);

Try / catch

try {
    ok = ldapEncoder.matches(raw, stored);
} catch (IllegalArgumentException e) {
    // unsupported prefix: route to the appropriate encoder or flag the record
}

Prevention

When it happens

Trigger: Calling matches(rawPassword, encodedPassword) where encodedPassword has a different or unrecognized brace prefix such as {bcrypt}, {CRYPT}, or no prefix format the encoder understands.

Common situations: A password store migrated between encoders (e.g. from bcrypt to LDAP SHA or vice versa) with stale entries; manually edited seed data with a typo in the prefix; DelegatingPasswordEncoder-style {id} hashes passed to this encoder directly.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ad727b44283d48fa. Report an issue: GitHub.