Unable to access encrypted SAML assertions in custom ResponseValidator after upgrade from 6.3 to 6.4 #16367
Description
Describe the bug
In an application where spring-security-saml2-service-provider
was upgraded from 6.3.x
to 6.4.1
, I am experiencing a change in behaviour where I'm struggling to access the list of encrypted assertions in a validator class. I think this may be a bug.
I'm configuring a response validator in my configuration as Spring intends by doing this:
var authProvider = new OpenSaml4AuthenticationProvider();
authProvider.setResponseValidator(responseValidator);
Having received a SAML Response with a single encrypted assertion, in said responseValidator
, I'd be able to do something like this:
class SamlResponseValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
@Override
public Saml2ResponseValidatorResult convert(ResponseToken responseToken) {
// Without any other overrides, the below gives you the Spring-decrypted assertion:
var assertions = responseToken.getResponse().getAssertions();
// In 6.3, the below would _also_ contain the still-encrypted assertions while
// in 6.4, you'll always just get an empty list, it seems:
var encryptedAssertions = responseToken.getResponse().getEncryptedAssertions();
// (...)
}
}
I can see that this change comes from what is now OpenSaml4Template.OpenSaml4DecryptionConfigurer.decryptResponse(Response response)
which will do this:
// ...
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) {
encrypteds.add(encrypted);
decrypteds.add(decrypted);
}
count++;
} catch (DecryptionException ex) {
throw new Saml2Exception(ex);
}
}
response.getEncryptedAssertions().removeAll(encrypteds); // <-- The cause of what I'm seeing!
response.getAssertions().addAll(decrypteds);
// ...
The Javadoc on the method states that:
The methods that follow are adapted from OpenSAML's [classes].
Previously, response.getEncryptedAssertions().removeAll(encrypteds)
did not exist.
As a result, I believe there to be two bugs:
responseToken.getResponse().getEncryptedAssertions()
no longer contains encrypted assertions when Spring decrypted them for youint count = 0
in OpenSaml4Template.OpenSaml4DecryptionConfigurer.decryptResponse(Response response) results in incorrect log statements such asDecrypting EncryptedAssertion (0/1) in Response [{id}]
when there's only one assertion
To Reproduce
- Create a basic application that uses
spring-security-saml2-service-provider
in which you provide your own response validator with:
var authProvider = new OpenSaml4AuthenticationProvider();
authProvider.setResponseValidator(responseValidator);
- Check assertions in the response validator:
class SamlResponseValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
@Override
public Saml2ResponseValidatorResult convert(ResponseToken responseToken) {
var encryptedAssertions = responseToken.getResponse().getEncryptedAssertions();
// (...)
}
}
- Execute the SAML flow with a SAML response containing an encrypted assertion and assert on/break point on
encryptedAssertions
- Notice that the value will be
0
instead of1
Expected behavior
responseToken.getResponse().getEncryptedAssertions()
should contain encrypted assertions- The log statement
Decrypting EncryptedAssertion (%d/%d) in Response [%s]
should log the correct count i.e.1/1
if one encrypted assertion was found (instead of0/1
)
Sample
Because of 1) how basic this is and 2) this possibly being intentional, I have not included one yet but happy to add it if required.
Before creating this issue, I asked this as a Stack Overflow question but didn't get an answer.
Please tell me if I'm wrong or missing something! If you do agree, I don't mind creating a PR.