spring-projects/spring-security · error · Saml2Exception

Saml2Exception wrapping DecryptionException during encrypted

Error message

Saml2Exception wrapping DecryptionException during encrypted assertion decryption

What it means

decryptResponse() iterates the encrypted assertions/encrypted elements of a Response and decrypts them with an OpenSAML Decrypter; a DecryptionException on any element is wrapped in this Saml2Exception. It means the SP could not decrypt the IdP's encrypted assertion with the configured decryption credentials.

Source

Thrown at saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/registration/OpenSaml5Template.java:518

		 */

		private void decryptResponse(Response response) {
			Collection<Assertion> decrypteds = new ArrayList<>();

			int count = 0;
			int size = response.getEncryptedAssertions().size();
			for (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {
				logger.trace(String.format("Decrypting EncryptedAssertion (%d/%d) in Response [%s]", count, size,
						response.getID()));
				try {
					Assertion decrypted = this.decrypter.decrypt(encrypted);
					if (decrypted != null) {
						decrypteds.add(decrypted);
					}
					count++;
				}
				catch (DecryptionException ex) {
					throw new Saml2Exception(ex);
				}
			}

			response.getAssertions().addAll(decrypteds);

			// Re-marshall the response so that any ID attributes within the decrypted
			// Assertions
			// will have their ID-ness re-established at the DOM level.
			if (!decrypteds.isEmpty()) {
				try {
					XMLObjectSupport.marshall(response);
				}
				catch (final MarshallingException ex) {
					throw new Saml2Exception(ex);
				}
			}
		}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Verify the decryption credential private key matches the certificate published to the IdP (the one it encrypts to)
  2. Inspect ex.getCause() for the specific DecryptionException reason (no matching key, algorithm, malformed data)
  3. Add/restore the old key as an additional decryption credential during key rotation instead of replacing it
  4. Confirm the IdP's encryption algorithm (e.g. AES-256-GCM, RSA-OAEP) is supported by your JVM providers

Example fix

// before
.decryptionX509Credentials(c -> c.add(new X509Certificate(newCert, newPrivateKey))) // IdP still encrypts with oldCert
// after
.decryptionX509Credentials(c -> c.add(new X509Certificate(newCert, newPrivateKey))
                                     .add(new X509Certificate(oldCert, oldPrivateKey))); // keep old key during rotation
Defensive patterns

Strategy: try-catch

Validate before calling

// at startup: confirm decryption key matches the SP certificate published to the IdP
Assert.notNull(decryptionPrivateKey, "Decryption private key must be configured");
Assert.isTrue(certMatches(decryptionCert, spMetadataCert), "Decryption cert must match SP metadata");

Try / catch

try {
    Response decrypted = template.decrypt(response);
} catch (Saml2Exception ex) {
    logger.error("Assertion decryption failed; check decryption keys/rotation: " + ex.getCause(), ex);
    throw ex;
}

Prevention

When it happens

Trigger: Processing a Response containing EncryptedAssertion when the decryption credential's private key doesn't match the certificate the IdP encrypted to, the key is unavailable, or the encrypted data uses an unsupported algorithm/KEK; also when the encrypted element is malformed.

Common situations: Rotated SP keys where the IdP still encrypts with the old (now removed) certificate; uploading the wrong private key in RelyingPartyRegistration.decryptionX509Credentials; IdP using an encryption algorithm the JVM/provider doesn't support; clock/KEK mismatches in ECDH key agreement.

Related errors


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