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

  1. Add the private key whose certificate the IdP uses for encryption to the RelyingPartyRegistration's decryption credentials
  2. During key rotation, keep the old decryption key alongside the new one until the IdP has switched
  3. Verify the private key loads correctly (correct format/password) by decrypting a sample payload in a test
  4. 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

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


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