{"record":{"id":"60611530ca64ad34","repo":"spring-projects/spring-security","slug":"saml2exception-wrapping-marshallingexception-while-606115","errorCode":null,"errorMessage":"Saml2Exception wrapping MarshallingException while re-marshalling response after decryption","messagePattern":"Saml2Exception wrapping MarshallingException while re-marshalling response after decryption","errorType":"exception","errorClass":"Saml2Exception","httpStatus":null,"severity":"error","filePath":"saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/OpenSaml5Template.java","lineNumber":532,"sourceCode":"\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\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/OpenSaml5Template.java#L514-L550","documentation":"After decrypting embedded encrypted assertions, decryptResponse re-marshalls the Response via XMLObjectSupport.marshall so decrypted ID attributes are re-established at the DOM level; a MarshallingException is wrapped in Saml2Exception. Marshalling converts the OpenSAML object model back to a DOM Element, and it fails if the object tree is inconsistent or unassignable to XML.","triggerScenarios":"decryptResponse() with at least one successfully decrypted assertion calls XMLObjectSupport.marshall(response); this throws MarshallingException if the response's XMLObject/DOM state is inconsistent (e.g. already-owned DOM conflicts, missing namespace/schema info after mutation).","commonSituations":"Custom code mutating the response between parse and decrypt; OpenSAML version upgrades changing marshall behavior; concurrent reuse of a single Response object across threads causing DOM ownership conflicts.","solutions":["Ensure the Response is parsed and processed single-threaded and not already marshalled/unmarshalled in a conflicting way","Rebuild or re-parse the Response from its XML source if its object model was mutated externally","Update OpenSAML/spring-security-saml2-service-provider to a version with known marshalling fixes","Catch Saml2Exception and log the wrapped MarshallingException stack for the failing element"],"exampleFix":"// before\nresponse.getAssertions().add(customMutableAssertion); // mutated externally\nXMLObjectSupport.marshall(response);\n// after\nresponse = parseResponse(xmlBytes); // fresh object model before decryption\n","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { XMLObjectSupport.marshall(response); } catch (Saml2Exception e) { log.error(\"Re-marshal after decrypt failed\", e.getCause()); }","preventionTips":["Do not mutate the Response object model outside the template's decrypt flow","Never share a parsed Response across threads","Pin OpenSAML/spring-security versions together and upgrade in step"],"tags":["saml","marshalling","opensaml","spring-security"],"backgroundTag":"xml-marshalling-failed","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"}