apereo/cas · error · SamlException
Signing credentials for validation could not be resolved
Error message
Signing credentials for validation could not be resolved
What it means
During SAML SSO, the IdP's SamlObjectSignatureValidator must find the SAML peer (SP) signing credential from the SP metadata via the role descriptor resolver before verifying the AuthnRequest signature. If getSigningCredential returns no credentials for the peer entity ID, signature validation cannot proceed and this SamlException is thrown in validateSignatureOnAuthenticationRequest. It is a metadata/config problem, not a bad signature per se.
Solutions
- Verify the SP metadata contains a signed KeyDescriptor (use=signing) for the requesting entity ID
- Check the SamlRegisteredService entry's metadata location/URL actually serves that entity ID
- Refresh or reload the SP metadata (force metadata resolver reload) and confirm the cert is present
- Confirm the SP signs its AuthnRequests and sends the correct entityID
- If the SP genuinely has no signing key, disable AuthnRequest signature requirement for that service in CAS service config
Example fix
// before: SP metadata with no signing key
<SPSSODescriptor ...> <AssertionConsumerService .../> </SPSSODescriptor>
// after: add signing KeyDescriptor
<SPSSODescriptor AuthnRequestsSigned="true" ...>
<KeyDescriptor use="signing">
<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIID...</ds:X509Certificate></ds:X509Data></ds:KeyInfo>
</KeyDescriptor>
</SPSSODescriptor> Defensive patterns
Strategy: validation
Validate before calling
// before CAS validates the AuthnRequest, confirm the SP metadata exposes a signing credential
var spDescriptor = roleDescriptorResolver.resolve(new CriteriaSet(
new EntityIdCriterion(entityId), new RolesDescriptorCriterion(SPSSODescriptor.DEFAULT_ELEMENT_NAME)));
if (spDescriptor == null || spDescriptor.getKeyDescriptors(KeyUsageType.SIGNING).isEmpty()) {
throw new ConfigurationException("No signing KeyDescriptor in SP metadata for " + entityId);
} Prevention
- Publish a signing KeyDescriptor (use="signing") in every SP's metadata
- Keep SP metadata feeds fresh and monitor metadata reload logs
- Verify entity IDs match exactly between SP requests and CAS service registrations
- Test new SP onboarding against a staging IdP before production
When it happens
Trigger: verifySamlProfileRequest -> validateSignatureOnAuthenticationRequest when getSigningCredential(roleDescriptorResolver, profileRequest) returns empty: SP metadata lacks a signing KeyDescriptor, entity ID mismatch, metadata resolver not loaded/expired, or the SP is unknown to the IdP.
Common situations: SP registered without signing keys in its metadata; stale or expired SP metadata feed; wrong entity ID configured on the SP (points at a metadata record with no signing cert); metadata resource path misconfigured so the SP's descriptor never resolves.
Related errors
- Signing credentials for validation could not be resolved…
- Unable to locate signing credentials
- Unable to identify the public key from the signing…
- Logout request is not signed but should be for service
- Resource [ ] cannot be located
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/bf5064e9711c9acc.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/builders/enc/validate/SamlObjectSignatureValidator.java:160
LOGGER.debug("Building security parameters context for signature validation of [{}]", peerEntityId);
val secCtx = context.ensureSubcontext(SecurityParametersContext.class);
val validationParams = new SignatureValidationParameters();
if (overrideBlockedSignatureAlgorithms != null && !overrideBlockedSignatureAlgorithms.isEmpty()) {
validationParams.setExcludedAlgorithms(this.overrideBlockedSignatureAlgorithms);
LOGGER.debug("Validation override blocked algorithms are [{}]", this.overrideAllowedAlgorithms);
}
if (overrideAllowedAlgorithms != null && !overrideAllowedAlgorithms.isEmpty()) {
validationParams.setIncludedAlgorithms(this.overrideAllowedAlgorithms);
LOGGER.debug("Validation override allowed algorithms are [{}]", this.overrideAllowedAlgorithms);
}
LOGGER.debug("Resolving signing credentials for [{}]", peerEntityId);
val credentials = getSigningCredential(roleDescriptorResolver, profileRequest);
if (credentials.isEmpty()) {
throw new SamlException("Signing credentials for validation could not be resolved");
}
var foundValidCredential = false;
val it = credentials.iterator();
while (!foundValidCredential && it.hasNext()) {
foundValidCredential = FunctionUtils.doAndHandle(() -> {
val handler = new SAML2HTTPRedirectDeflateSignatureSecurityHandler();
val credential = it.next();
val resolver = new StaticCredentialResolver(credential);
val keyResolver = new StaticKeyInfoCredentialResolver(credential);
val trustEngine = new ExplicitKeySignatureTrustEngine(resolver, keyResolver);
validationParams.setSignatureTrustEngine(trustEngine);
secCtx.setSignatureValidationParameters(validationParams);
handler.setHttpServletRequestSupplier(() -> request);
LOGGER.debug("Initializing [{}] to execute signature validation for [{}]", handler.getClass().getSimpleName(), peerEntityId);
handler.initialize();
LOGGER.debug("Invoking [{}] to handle signature validation for [{}]", handler.getClass().getSimpleName(), peerEntityId);View on GitHub (pinned to e7288fc434)