{"record":{"id":"58c37f53fb603c54","repo":"spring-projects/spring-security","slug":"saml2exception-wrapping-decryptionexception-during-58c37f","errorCode":null,"errorMessage":"Saml2Exception wrapping DecryptionException during encrypted assertion decryption","messagePattern":"Saml2Exception wrapping DecryptionException during encrypted assertion decryption","errorType":"exception","errorClass":"Saml2Exception","httpStatus":null,"severity":"error","filePath":"saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/registration/OpenSaml5Template.java","lineNumber":518,"sourceCode":"\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n","sourceCodeStart":500,"sourceCodeEnd":536,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/registration/OpenSaml5Template.java#L500-L536","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the decryption credential private key matches the certificate published to the IdP (the one it encrypts to)","Inspect ex.getCause() for the specific DecryptionException reason (no matching key, algorithm, malformed data)","Add/restore the old key as an additional decryption credential during key rotation instead of replacing it","Confirm the IdP's encryption algorithm (e.g. AES-256-GCM, RSA-OAEP) is supported by your JVM providers"],"exampleFix":"// before\n.decryptionX509Credentials(c -> c.add(new X509Certificate(newCert, newPrivateKey))) // IdP still encrypts with oldCert\n// after\n.decryptionX509Credentials(c -> c.add(new X509Certificate(newCert, newPrivateKey))\n                                     .add(new X509Certificate(oldCert, oldPrivateKey))); // keep old key during rotation","handlingStrategy":"try-catch","validationCode":"// at startup: confirm decryption key matches the SP certificate published to the IdP\nAssert.notNull(decryptionPrivateKey, \"Decryption private key must be configured\");\nAssert.isTrue(certMatches(decryptionCert, spMetadataCert), \"Decryption cert must match SP metadata\");","typeGuard":null,"tryCatchPattern":"try {\n    Response decrypted = template.decrypt(response);\n} catch (Saml2Exception ex) {\n    logger.error(\"Assertion decryption failed; check decryption keys/rotation: \" + ex.getCause(), ex);\n    throw ex;\n}","preventionTips":["Keep old decryption keys alongside new ones during key rotation","Verify the IdP encrypts to the certificate published in your SP metadata","Test decryption with production-like IdP responses before rollout"],"tags":["saml2","opensaml","decryption","encrypted-assertion","credential"],"backgroundTag":"missing-credentials","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}