quarkusio/quarkus · error · UnresolvableKeyException

Thumprint of the root chain certificate is invalid

Error message

Thumprint of the root chain certificate is invalid

What it means

When verifying a token whose JWS carries an x5c certificate chain, the resolver always checks that the thumbprint of the last certificate in the chain (the root) matches one of the thumbprints loaded from the configured truststore. If it does not, the chain is not anchored to a trusted root and an UnresolvableKeyException is thrown, failing token verification.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java:72

                return null;
            }

            // General certificate chain validation
            //TODO: support revocation lists
            CertificateHelper.checkValidity(chain, null);
            if (chain.size() == 1) {
                // CertificateHelper.checkValidity does not currently
                // verify the certificate signature if it is a single certificate chain
                final X509Certificate root = chain.get(0);
                root.verify(root.getPublicKey());
            }

            // Always do the root certificate thumbprint check
            LOG.debug("Checking a thumbprint of the root chain certificate");
            String rootThumbprint = TrustStoreUtils.calculateThumprint(chain.get(chain.size() - 1));
            if (!thumbprints.contains(rootThumbprint)) {
                LOG.error("Thumprint of the root chain certificate is invalid");
                throw new UnresolvableKeyException("Thumprint of the root chain certificate is invalid");
            }

            // Run custom validators if any
            if (!certificateValidators.isEmpty()) {
                LOG.debug("Running custom TokenCertificateValidators");
                for (TokenCertificateValidator validator : certificateValidators) {
                    validator.validate(oidcConfig, chain, jws.getUnverifiedPayload());
                }
            }

            // Finally, check the leaf certificate if required
            if (expectedLeafCertificateName.isPresent()) {
                // Compare the leaf certificate common name against the configured value
                String leafCertificateName = HttpSecurityUtils.getCommonName(chain.get(0).getSubjectX500Principal());
                if (!expectedLeafCertificateName.get().equals(leafCertificateName)) {
                    LOG.errorf("Wrong leaf certificate common name: %s", leafCertificateName);
                    throw new UnresolvableKeyException("Wrong leaf certificate common name");
                }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Import the actual root CA certificate of the token issuer into the configured truststore (trust-store-file) and restart.
  2. Confirm the correct truststore file/type is configured for the tenant (trust-store-file-type, e.g. PKCS12 vs JKS).
  3. Regenerate or obtain tokens from the expected certificate chain if the token was issued by the wrong CA.
  4. Check the ROOT_LOG level for the logged 'Thumprint of the root chain certificate is invalid' message to confirm this path.

Example fix

keytool -importcert -alias issuer-root -file issuer-root.crt -keystore truststore.p12 -storetype PKCS12
Defensive patterns

Strategy: validation

Validate before calling

Set<String> trusted = TrustStoreUtils.getTrustedCertificateThumbprints(trustStoreFile, password, alias, fileType);
String rootTp = TrustStoreUtils.calculateThumprint(chain.get(chain.size() - 1));
if (!trusted.contains(rootTp)) {
    throw new IllegalStateException("Token issuer root CA not in truststore; import it first");
}

Try / catch

try {
    return jwtVerify(token);
} catch (UnresolvableKeyException e) {
    if ("Thumprint of the root chain certificate is invalid".equals(e.getMessage())) {
        log.error("Issuer root CA missing from truststore; import the issuer's root certificate");
    }
    throw new AuthenticationFailedException(e);
}

Prevention

When it happens

Trigger: resolveKey() is called with a token containing an x5c header whose chain's root certificate thumbprint is absent from quarkus.oidc.certificate-chain.trust-store-file.

Common situations: Tokens issued by a different OIDC provider or CA than the one whose root was imported into the truststore; truststore not updated after the issuer rotated its root CA; a self-signed test token verified against a production truststore.

Understand the failure class

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/1cce662e354cc8f7. Report an issue: GitHub.