apereo/cas · critical · IllegalArgumentException
Unable to locate signing credentials
Error message
Unable to locate signing credentials
What it means
When building the signature signing configuration for outbound IdP messages (responses/assertions), credentials from the configured keystore/credential source are filtered by the service's allowed fingerprint(s). If nothing survives the filters — no credentials loaded or fingerprint mismatch — the signer has no way to sign and IllegalArgumentException is thrown.
Solutions
- Verify the IdP signing keystore path, password, and alias resolve to loadable credentials.
- Update the service's configured signing fingerprint to match the actual signing certificate (recompute SHA-1/SHA-256 fingerprint of the current cert).
- Remove an incorrect fingerprint filter from the service config so credentials are accepted.
- Ensure the signing credential bean is defined and reachable in the signing configuration properties.
Example fix
// before
service.setSigningCertificateFingerprint("ABCOLD123...");
// after
service.setSigningCertificateFingerprint("3FA9C2..." /* fingerprint of current signing cert */); Defensive patterns
Strategy: validation
Validate before calling
val creds = signingConfiguration.resolveCredentials(service);
val matching = creds.stream().filter(c -> fingerprintMatches(c, service.getSigningCertificateFingerprint())).count();
if (matching == 0) throw new IllegalStateException("No signing credential matches fingerprint for " + service.getName()); Type guard
boolean hasSigningCredential(SamlRegisteredService s, List<Credential> creds) {
return creds != null && !creds.isEmpty() && (s.getSigningCertificateFingerprint() == null
|| creds.stream().anyMatch(c -> fingerprintMatches(c, s.getSigningCertificateFingerprint())));
} Try / catch
try {
val signed = signer.encode(samlObject, service, adaptor, ...);
} catch (IllegalArgumentException e) {
LOGGER.error("Signing credentials misconfigured: {}", e.getMessage());
alertOps("IdP signing credential configuration error");
} Prevention
- Verify fingerprints with: openssl x509 -fingerprint -in cert.pem whenever the IdP key rotates.
- Confirm keystore path/password/alias resolve in the target environment.
- Log loaded credential fingerprints at startup and diff against service configs.
- Test response signing in staging after any certificate change.
When it happens
Trigger: getSignatureSigningConfiguration is invoked during response signing and finalCredentials ends up empty: the IdP signing keystore is not configured/unloadable, or SamlRegisteredServiceSigningEncryptionParamters/fingerprint filters exclude every credential via doesCredentialFingerprintMatch.
Common situations: Service configured with a signing certificate fingerprint that doesn't match the actual IdP key (common after key rotation); keystore path/password wrong so credentials list is empty; whitespace/case issues in fingerprint comparison.
Related errors
- Unable to identify the public key from the signing…
- Unable to resolve SignatureSigningParameters, response…
- Public and private keys do not match
- Could not decode provided KeyFile
- Signing credentials for validation could not be resolved
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/6017bf56d52fd931.
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/DefaultSamlIdPObjectSigner.java:285
LOGGER.trace("Resolved entity id from SAML2 IdP metadata is [{}]", entityId);
criteriaSet.add(new EntityIdCriterion(entityId));
criteriaSet.add(new EntityRoleCriterion(IDPSSODescriptor.DEFAULT_ELEMENT_NAME));
criteriaSet.add(new SamlIdPSamlRegisteredServiceCriterion(service));
LOGGER.trace("Resolved signing credentials based on criteria [{}]", criteriaSet);
val credentials = Sets.newLinkedHashSet(mdCredentialResolver.resolve(criteriaSet));
LOGGER.trace("Resolved [{}] signing credentials", credentials.size());
val finalCredentials = new ArrayList<Credential>();
credentials.stream()
.map(creds -> getResolvedSigningCredential(creds, privateKey, service))
.filter(Objects::nonNull)
.filter(creds -> doesCredentialFingerprintMatch(creds, service))
.forEach(finalCredentials::add);
if (finalCredentials.isEmpty()) {
LOGGER.error("Unable to locate any signing credentials for service [{}]", service.getName());
throw new IllegalArgumentException("Unable to locate signing credentials");
}
config.setSigningCredentials(finalCredentials);
LOGGER.trace("Signature signing credentials configured with [{}] credentials", finalCredentials.size());
return config;
}
/**
* Gets signing private key.
*
* @param registeredService the registered service
* @return the signing private key
* @throws Throwable the throwable
*/
protected PrivateKey getSigningPrivateKey(final SamlRegisteredService registeredService) throws Throwable {
val samlIdp = casProperties.getAuthn().getSamlIdp();
val signingKey = samlIdPMetadataLocator.resolveSigningKey(Optional.of(registeredService));
val privateKeyFactoryBean = new PrivateKeyFactoryBean();View on GitHub (pinned to e7288fc434)