{"record":{"id":"891079909c6cd8b4","repo":"quarkusio/quarkus","slug":"leaf-certificate-must-not-have-ca-flag-set-to-true","errorCode":null,"errorMessage":"Leaf certificate must not have CA flag set to true","messagePattern":"Leaf certificate must not have CA flag set to true","errorType":"exception","errorClass":"SpiffeConnectionException","httpStatus":null,"severity":"critical","filePath":"extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeValidator.java","lineNumber":22,"sourceCode":"import java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.quarkus.spiffe.client.SpiffeConnectionException;\n\nfinal class SpiffeValidator {\n\n    private static final String SPIFFE_URI_PREFIX = \"spiffe://\";\n    private static final int URI_SAN_TYPE = 6;\n    private static final int MAX_SPIFFE_ID_LENGTH = 2048;\n    private static final int MAX_TRUST_DOMAIN_LENGTH = 255;\n\n    private SpiffeValidator() {\n    }\n\n    static String validateLeaf(X509Certificate leaf) throws SpiffeConnectionException {\n        if (leaf.getBasicConstraints() != -1) {\n            throw new SpiffeConnectionException(\"Leaf certificate must not have CA flag set to true\");\n        }\n\n        boolean[] keyUsage = leaf.getKeyUsage();\n        if (keyUsage == null) {\n            throw new SpiffeConnectionException(\"Leaf certificate is missing the key usage extension\");\n        }\n        if (keyUsage.length < 1 || !keyUsage[0]) {\n            throw new SpiffeConnectionException(\"Leaf certificate must have 'digitalSignature' as key usage\");\n        }\n        if (keyUsage.length > 5 && keyUsage[5]) {\n            throw new SpiffeConnectionException(\"Leaf certificate must not have 'keyCertSign' as key usage\");\n        }\n        if (keyUsage.length > 6 && keyUsage[6]) {\n            throw new SpiffeConnectionException(\"Leaf certificate must not have 'cRLSign' as key usage\");\n        }\n\n        return extractAndValidateUriSan(leaf);\n    }","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeValidator.java#L4-L40","documentation":"When validating a fetched X.509 SVID, the leaf certificate must be an end-entity certificate. If the leaf has the BasicConstraints CA extension set (getBasicConstraints() != -1), it is a CA certificate and cannot be used as a workload identity, so the validator throws SpiffeConnectionException. The SPIRE agent should never hand out such a chain; seeing this means the identity material is wrong or spoofed.","triggerScenarios":"Calling the SVID validation path (used when trusting fetched certificates for mTLS) with a chain whose first certificate carries CA=true, e.g. an agent serving an intermediate/CA cert as the leaf.","commonSituations":"A misbehaving or compromised SPIRE agent; custom test agents emitting wrong chains; manually constructed trust material being fed through the validator; mixing up bundle (CA) and SVID (leaf) chains in custom code paths.","solutions":["Inspect the served chain with openssl x509 -noout -text and confirm the leaf has CA:FALSE.","Restart/redeploy the SPIRE agent and re-fetch SVIDs; verify with spire-agent api fetch x509.","Ensure you are not swapping the trust bundle (CA certs) and the SVID chain in custom code.","Catch SpiffeConnectionException and treat it as non-retryable security misconfiguration."],"exampleFix":"// before\nX509Certificate leaf = chain.get(0);\nString spiffeId = SpiffeValidator.validateLeaf(leaf); // throws if leaf is a CA\n// after\nX509Certificate leaf = chain.get(0);\nif (leaf.getBasicConstraints() != -1) {\n    throw new IllegalStateException(\"Fetched leaf is a CA certificate; check SPIRE agent registration entries\");\n}\nString spiffeId = SpiffeValidator.validateLeaf(leaf);","handlingStrategy":"validation","validationCode":"static boolean isEndEntity(X509Certificate leaf) {\n    try {\n        return leaf.getBasicConstraints() == -1;\n    } catch (CertificateParsingException e) {\n        return false;\n    }\n}","typeGuard":"static boolean isLeafNotCa(X509Certificate cert) {\n    return cert != null && cert.getBasicConstraints() == -1;\n}","tryCatchPattern":"try {\n    String id = SpiffeValidator.validateLeaf(leaf);\n} catch (SpiffeConnectionException e) {\n    // non-retryable: identity material is invalid\n    throw new SecurityException(\"SPIRE served a CA certificate as leaf: \" + e.getMessage(), e);\n}","preventionTips":["Never pass trust-bundle/CA certs through the leaf validation path","Verify served chains with openssl (CA:FALSE on leaf)","Use genuine SPIRE agents; avoid hand-rolled test chains in prod paths","Treat this error as a security incident, not a transient fault"],"tags":["spiffe","x509","basicconstraints","certificate-validation"],"backgroundTag":"invalid-certificate-chain","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}