spring-projects/spring-security · error · Saml2AuthenticationException
decryption_error
decryption_error
Error message
decryptionError(ex.getMessage())
What it means
The default response elements decrypter throws decryption_error when SAML response decryption with the configured decryption keys fails. Encrypted assertions/NameIDs cannot be decrypted, typically because the SP lacks the correct private key corresponding to the certificate the IdP used for encryption.
Source
Thrown at saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/BaseOpenSamlAuthenticationProvider.java:429
Collection<Saml2X509Credential> credentials = details.getVerificationX509Credentials();
Collection<Saml2Error> errors = this.saml.withVerificationKeys(credentials)
.entityId(details.getEntityId())
.verify(response);
return Saml2ResponseValidatorResult.failure(errors);
}
return Saml2ResponseValidatorResult.success();
};
}
private Consumer<ResponseToken> createDefaultResponseElementsDecrypter() {
return (responseToken) -> {
Response response = responseToken.getResponse();
RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration();
try {
this.saml.withDecryptionKeys(registration.getDecryptionX509Credentials()).decrypt(response);
}
catch (Exception ex) {
throw new Saml2AuthenticationException(Saml2Error.decryptionError(ex.getMessage()), ex);
}
};
}
private Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() {
return (assertionToken) -> {
RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration();
Assertion assertion = assertionToken.getAssertion();
if (assertion.isSigned()) {
AssertingPartyMetadata details = registration.getAssertingPartyMetadata();
Collection<Saml2X509Credential> credentials = details.getVerificationX509Credentials();
Collection<Saml2Error> errors = this.saml.withVerificationKeys(credentials)
.entityId(details.getEntityId())
.verify(assertion);
return Saml2ResponseValidatorResult.failure(errors);
}
return Saml2ResponseValidatorResult.success();
};View on GitHub (pinned to 96852e8860)
Solutions
- Add the private key whose certificate the IdP uses for encryption to the RelyingPartyRegistration's decryption credentials
- During key rotation, keep the old decryption key alongside the new one until the IdP has switched
- Verify the private key loads correctly (correct format/password) by decrypting a sample payload in a test
- Align encryption algorithms between IdP and SP, or upgrade OpenSAML/Spring Security for newer algorithm support
Example fix
// before
.registrationId("idp")
.signingX509Credentials((c) -> c.add(signingCert)) // only signing key
// after
.decryptionX509Credentials((c) -> c.add(new RsaKeyConverter()
.setPrivateKey(privateKeyPem).getX509Credential()))
.signingX509Credentials((c) -> c.add(signingCert)) Defensive patterns
Strategy: validation
Validate before calling
// Verify the decryption key can be loaded and pairs with the IdP encryption cert
X509Certificate idpEncryptionCert = fetchFromMetadata(idpMetadataUrl);
X509Certificate spCert = loadSpCertificate(spPrivateKey);
if (!idpEncryptionCert.equals(spCert)) {
throw new IllegalStateException(
"SP decryption cert does not match the IdP's encryption certificate");
} Try / catch
try {
Authentication result = provider.authenticate(token);
} catch (Saml2AuthenticationException e) {
if (Saml2ErrorCodes.DECRYPTION_ERROR.equals(e.getError().getErrorCode())) {
logger.error("SAML decryption failed — check decryption keys", e.getCause());
throw e;
}
} Prevention
- Configure decryptionX509Credentials explicitly, not just signing credentials
- During certificate rotation keep old + new decryption keys until the IdP switches
- Verify private key files decrypt correctly (format, password) before deployment
- Compare your SP certificate against the IdP metadata encryption certificate regularly
When it happens
Trigger: The IdP encrypts assertions (or the EncryptedID) and this.saml.withDecryptionKeys(...).decrypt(response) fails — no matching private key in registration.getDecryptionX509Credentials(), wrong key format, corrupted XML encryption elements, or unsupported encryption algorithm.
Common situations: After rotating certificates the old decryption key was removed before the IdP switched; keystore only holds the signing key, not the encryption key; PEM/PKCS12 conversion issues producing an unusable private key; IdP switched to an encryption algorithm OpenSAML is not configured for.
Related errors
- Saml2Exception wrapping DecryptionException during encrypted
- Saml2Exception wrapping exception during encrypted attribute
- Saml2Exception wrapping DecryptionException during encrypted
- Saml2Exception wrapping DecryptionException during encrypted
- Saml2Exception wrapping DecryptionException during encrypted
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/a62351f18ade2f24.
Report an issue: GitHub.