{"record":{"id":"6017bf56d52fd931","repo":"apereo/cas","slug":"unable-to-locate-signing-credentials","errorCode":null,"errorMessage":"Unable to locate signing credentials","messagePattern":"Unable to locate signing credentials","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"critical","filePath":"support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/builders/enc/DefaultSamlIdPObjectSigner.java","lineNumber":285,"sourceCode":"        LOGGER.trace(\"Resolved entity id from SAML2 IdP metadata is [{}]\", entityId);\n        criteriaSet.add(new EntityIdCriterion(entityId));\n        criteriaSet.add(new EntityRoleCriterion(IDPSSODescriptor.DEFAULT_ELEMENT_NAME));\n        criteriaSet.add(new SamlIdPSamlRegisteredServiceCriterion(service));\n\n        LOGGER.trace(\"Resolved signing credentials based on criteria [{}]\", criteriaSet);\n        val credentials = Sets.newLinkedHashSet(mdCredentialResolver.resolve(criteriaSet));\n        LOGGER.trace(\"Resolved [{}] signing credentials\", credentials.size());\n\n        val finalCredentials = new ArrayList<Credential>();\n        credentials.stream()\n            .map(creds -> getResolvedSigningCredential(creds, privateKey, service))\n            .filter(Objects::nonNull)\n            .filter(creds -> doesCredentialFingerprintMatch(creds, service))\n            .forEach(finalCredentials::add);\n\n        if (finalCredentials.isEmpty()) {\n            LOGGER.error(\"Unable to locate any signing credentials for service [{}]\", service.getName());\n            throw new IllegalArgumentException(\"Unable to locate signing credentials\");\n        }\n\n        config.setSigningCredentials(finalCredentials);\n        LOGGER.trace(\"Signature signing credentials configured with [{}] credentials\", finalCredentials.size());\n        return config;\n    }\n\n    /**\n     * Gets signing private key.\n     *\n     * @param registeredService the registered service\n     * @return the signing private key\n     * @throws Throwable the throwable\n     */\n    protected PrivateKey getSigningPrivateKey(final SamlRegisteredService registeredService) throws Throwable {\n        val samlIdp = casProperties.getAuthn().getSamlIdp();\n        val signingKey = samlIdPMetadataLocator.resolveSigningKey(Optional.of(registeredService));\n        val privateKeyFactoryBean = new PrivateKeyFactoryBean();","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/builders/enc/DefaultSamlIdPObjectSigner.java#L267-L303","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nservice.setSigningCertificateFingerprint(\"ABCOLD123...\");\n// after\nservice.setSigningCertificateFingerprint(\"3FA9C2...\" /* fingerprint of current signing cert */);","handlingStrategy":"validation","validationCode":"val creds = signingConfiguration.resolveCredentials(service);\nval matching = creds.stream().filter(c -> fingerprintMatches(c, service.getSigningCertificateFingerprint())).count();\nif (matching == 0) throw new IllegalStateException(\"No signing credential matches fingerprint for \" + service.getName());","typeGuard":"boolean hasSigningCredential(SamlRegisteredService s, List<Credential> creds) {\n    return creds != null && !creds.isEmpty() && (s.getSigningCertificateFingerprint() == null\n        || creds.stream().anyMatch(c -> fingerprintMatches(c, s.getSigningCertificateFingerprint())));\n}","tryCatchPattern":"try {\n    val signed = signer.encode(samlObject, service, adaptor, ...);\n} catch (IllegalArgumentException e) {\n    LOGGER.error(\"Signing credentials misconfigured: {}\", e.getMessage());\n    alertOps(\"IdP signing credential configuration error\");\n}","preventionTips":["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."],"tags":["saml","signing","credentials","keystore","fingerprint"],"backgroundTag":"missing-credentials","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}