spring-projects/spring-security · error · Saml2Exception

Saml2Exception wrapping MarshallingException while re-marsha

Error message

Saml2Exception wrapping MarshallingException while re-marshalling response after decryption

What it means

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.

Source

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

					}
					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);
				}
			}
		}

		private void decryptAssertion(Assertion assertion) {
			for (AttributeStatement statement : assertion.getAttributeStatements()) {
				decryptAttributes(statement);
			}
			decryptSubject(assertion.getSubject());
			if (assertion.getConditions() != null) {
				for (Condition c : assertion.getConditions().getConditions()) {
					if (!(c instanceof DelegationRestrictionType delegation)) {
						continue;
					}
					for (Delegate d : delegation.getDelegates()) {
						if (d.getEncryptedID() != null) {
							try {
								NameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());

View on GitHub (pinned to 96852e8860)

Solutions

  1. Ensure the Response is parsed and processed single-threaded and not already marshalled/unmarshalled in a conflicting way
  2. Rebuild or re-parse the Response from its XML source if its object model was mutated externally
  3. Update OpenSAML/spring-security-saml2-service-provider to a version with known marshalling fixes
  4. Catch Saml2Exception and log the wrapped MarshallingException stack for the failing element

Example fix

// before
response.getAssertions().add(customMutableAssertion); // mutated externally
XMLObjectSupport.marshall(response);
// after
response = parseResponse(xmlBytes); // fresh object model before decryption
Defensive patterns

Strategy: try-catch

Try / catch

try { XMLObjectSupport.marshall(response); } catch (Saml2Exception e) { log.error("Re-marshal after decrypt failed", e.getCause()); }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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