apereo/cas · warning

Failed to locate the signing key

Error message

Failed to locate the signing key [{}] for [{}]

What it means

When configuring the MDUI metadata resolver chain, CAS builds a signature validation filter from the configured signing key. If SamlUtils.buildSignatureValidationFilter returns null for the given key/reference, this warning is logged and the metadata resource is excluded (addResource = false).

Solutions

  1. Verify the signing key resource path exists and is readable by CAS (ResourceUtils.doesResourceExist).
  2. Check the key format is one OpenSAML's SignatureValidationFilter supports (X.509 PEM cert or keystore).
  3. Rebuild the signing key path or embed the certificate in metadata trust.
  4. If signature validation is not needed, remove the signing key config so the filter branch is skipped.

Example fix

// before
cas.saml-metadata-ui.signing-key[0]=file:/etc/cas/mdui/missing-cert.pem
// after
cas.saml-metadata-ui.signing-key[0]=file:/etc/cas/mdui/federation-signer.pem
Defensive patterns

Strategy: validation

Validate before calling

var keyRes = applicationContext.getResource(signingKey);
if (!keyRes.exists()) {
    LOGGER.warn("Signing key missing: {}", signingKey);
    // fix path or disable signature validation
}

Prevention

When it happens

Trigger: configureResource has a signing key configured for the metadataReference, but the key cannot be loaded/located — bad path, unreadable keystore/cert file, unsupported key format — so buildSignatureValidationFilter yields null.

Common situations: Typo in signing key path in cas.saml-metadata-ui config; certificate file missing in container image; key in unsupported format (e.g. DER vs PEM); wrong keystore password handled upstream producing null instead of throw.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/05965c228910608a. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-saml-mdui/src/main/java/org/apereo/cas/config/SamlMetadataUIConfiguration.java:89

            val arr = Splitter.on(DEFAULT_SEPARATOR).splitToList(entry);
            val metadataReference = arr.getFirst();
            val signingKey = arr.size() > 1 ? arr.get(1) : null;
            val filters = new ArrayList<MetadataFilter>();
            if (casProperties.getSamlMetadataUi().getMaxValidity() > 0) {
                val filter = new RequiredValidUntilFilter();
                filter.setMaxValidityInterval(Duration.ofSeconds(casProperties.getSamlMetadataUi().getMaxValidity()));
                filter.initialize();
                filters.add(filter);
            }
            var addResource = true;
            if (StringUtils.isNotBlank(signingKey)) {
                val sigFilter = SamlUtils.buildSignatureValidationFilter(applicationContext, signingKey);
                if (sigFilter != null) {
                    sigFilter.setRequireSignedRoot(casProperties.getSamlMetadataUi().isRequireSignedRoot());
                    sigFilter.initialize();
                    filters.add(sigFilter);
                } else {
                    LOGGER.warn("Failed to locate the signing key [{}] for [{}]", signingKey, metadataReference);
                    addResource = false;
                }
            }
            chain.setFilters(filters);
            if (addResource && (ResourceUtils.isUrl(metadataReference) || ResourceUtils.doesResourceExist(metadataReference))) {
                val resource = applicationContext.getResource(metadataReference);
                resources.put(resource, chain);
            } else {
                LOGGER.warn("Skipping metadata [{}]; Either the resource cannot be retrieved or its signing key is missing", metadataReference);
            }
        }));
    }

    @ConditionalOnMissingBean(name = "chainingSamlMetadataUIMetadataResolverAdapter")
    @Bean
    @RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
    public MetadataResolverAdapter chainingSamlMetadataUIMetadataResolverAdapter(
        final CasConfigurationProperties casProperties,

View on GitHub (pinned to e7288fc434)