{"record":{"id":"1cce662e354cc8f7","repo":"quarkusio/quarkus","slug":"thumprint-of-the-root-chain-certificate-is-invalid","errorCode":null,"errorMessage":"Thumprint of the root chain certificate is invalid","messagePattern":"Thumprint of the root chain certificate is invalid","errorType":"exception","errorClass":"UnresolvableKeyException","httpStatus":401,"severity":"error","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java","lineNumber":72,"sourceCode":"                return null;\n            }\n\n            // General certificate chain validation\n            //TODO: support revocation lists\n            CertificateHelper.checkValidity(chain, null);\n            if (chain.size() == 1) {\n                // CertificateHelper.checkValidity does not currently\n                // verify the certificate signature if it is a single certificate chain\n                final X509Certificate root = chain.get(0);\n                root.verify(root.getPublicKey());\n            }\n\n            // Always do the root certificate thumbprint check\n            LOG.debug(\"Checking a thumbprint of the root chain certificate\");\n            String rootThumbprint = TrustStoreUtils.calculateThumprint(chain.get(chain.size() - 1));\n            if (!thumbprints.contains(rootThumbprint)) {\n                LOG.error(\"Thumprint of the root chain certificate is invalid\");\n                throw new UnresolvableKeyException(\"Thumprint of the root chain certificate is invalid\");\n            }\n\n            // Run custom validators if any\n            if (!certificateValidators.isEmpty()) {\n                LOG.debug(\"Running custom TokenCertificateValidators\");\n                for (TokenCertificateValidator validator : certificateValidators) {\n                    validator.validate(oidcConfig, chain, jws.getUnverifiedPayload());\n                }\n            }\n\n            // Finally, check the leaf certificate if required\n            if (expectedLeafCertificateName.isPresent()) {\n                // Compare the leaf certificate common name against the configured value\n                String leafCertificateName = HttpSecurityUtils.getCommonName(chain.get(0).getSubjectX500Principal());\n                if (!expectedLeafCertificateName.get().equals(leafCertificateName)) {\n                    LOG.errorf(\"Wrong leaf certificate common name: %s\", leafCertificateName);\n                    throw new UnresolvableKeyException(\"Wrong leaf certificate common name\");\n                }","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java#L54-L90","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Import the actual root CA certificate of the token issuer into the configured truststore (trust-store-file) and restart.","Confirm the correct truststore file/type is configured for the tenant (trust-store-file-type, e.g. PKCS12 vs JKS).","Regenerate or obtain tokens from the expected certificate chain if the token was issued by the wrong CA.","Check the ROOT_LOG level for the logged 'Thumprint of the root chain certificate is invalid' message to confirm this path."],"exampleFix":"keytool -importcert -alias issuer-root -file issuer-root.crt -keystore truststore.p12 -storetype PKCS12","handlingStrategy":"validation","validationCode":"Set<String> trusted = TrustStoreUtils.getTrustedCertificateThumbprints(trustStoreFile, password, alias, fileType);\nString rootTp = TrustStoreUtils.calculateThumprint(chain.get(chain.size() - 1));\nif (!trusted.contains(rootTp)) {\n    throw new IllegalStateException(\"Token issuer root CA not in truststore; import it first\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return jwtVerify(token);\n} catch (UnresolvableKeyException e) {\n    if (\"Thumprint of the root chain certificate is invalid\".equals(e.getMessage())) {\n        log.error(\"Issuer root CA missing from truststore; import the issuer's root certificate\");\n    }\n    throw new AuthenticationFailedException(e);\n}","preventionTips":["Keep the issuer's root CA in the truststore and refresh it when the issuer rotates CAs.","Track issuer certificate rotation and update truststores before old roots expire.","Test token verification against a freshly issued token in CI."],"tags":["oidc","certificate-chain","thumbprint","truststore"],"backgroundTag":"untrusted-certificate-chain","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}