quarkusio/quarkus · error · SpiffeConnectionException

Signing certificate must have 'keyCertSign' as key usage: ${

Error message

Signing certificate must have 'keyCertSign' as key usage: ${subjectX500Principal}

What it means

A CA certificate in the SVID chain must have the keyCertSign key usage bit (index 5) set, since it signs other certificates. SpiffeValidator.validateIntermediate throws SpiffeConnectionException when keyUsage is absent, too short, or does not include keyCertSign.

Source

Thrown at extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeValidator.java:51

            throw new SpiffeConnectionException("Leaf certificate must not have 'keyCertSign' as key usage");
        }
        if (keyUsage.length > 6 && keyUsage[6]) {
            throw new SpiffeConnectionException("Leaf certificate must not have 'cRLSign' as key usage");
        }

        return extractAndValidateUriSan(leaf);
    }

    // X.509-SVID 3.2 SHOULD: signing cert SHOULD itself be an SVID (not enforced — upstream CA may not be SPIFFE-aware)
    // X.509-SVID 3.2 SHOULD: signing cert SHOULD reside in the trust domain of leaf SVIDs it issues (not enforced — cross-domain signing is allowed)
    static void validateIntermediate(X509Certificate cert) throws SpiffeConnectionException {
        if (cert.getBasicConstraints() < 0) {
            throw new SpiffeConnectionException(
                    "Signing certificate must have CA flag set to true: " + cert.getSubjectX500Principal());
        }
        boolean[] keyUsage = cert.getKeyUsage();
        if (keyUsage == null || keyUsage.length <= 5 || !keyUsage[5]) {
            throw new SpiffeConnectionException(
                    "Signing certificate must have 'keyCertSign' as key usage: " + cert.getSubjectX500Principal());
        }
        // X.509-SVID 3.2 MUST: if signing cert has a SPIFFE ID, it must not have a path component
        String uriSan = extractOptionalUriSan(cert);
        if (uriSan != null && uriSan.startsWith(SPIFFE_URI_PREFIX)) {
            URI uri = URI.create(uriSan);
            String path = uri.getPath();
            if (path != null && !path.isEmpty() && !"/".equals(path)) {
                throw new SpiffeConnectionException(
                        "Signing certificate SPIFFE ID must not have a path component: " + uriSan);
            }
        }
    }

    static void validateSpiffeId(String spiffeId) throws SpiffeConnectionException {
        if (spiffeId == null || spiffeId.isEmpty()) {
            throw new SpiffeConnectionException("SPIFFE ID must not be empty");
        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Reissue the signing certificate with keyUsage = critical, keyCertSign (plus digitalSignature and cRLSign if needed for CRLs).
  2. Fix the CA template used by the upstream PKI to include keyCertSign for CA certs.
  3. Regenerate local test CAs with: openssl req -x509 -addext keyUsage=critical,keyCertSign,cRLSign.

Example fix

// before (openssl config for intermediate)
keyUsage = digitalSignature
// after
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
Defensive patterns

Strategy: validation

Validate before calling

boolean[] ku = cert.getKeyUsage();
if (ku == null || ku.length <= 5 || !ku[5]) {
    throw new IllegalArgumentException("CA cert lacks keyCertSign usage");
}

Try / catch

try {
    connection.establish();
} catch (SpiffeConnectionException e) {
    if (e.getMessage().contains("keyCertSign")) {
        log.error("Signing certificate has invalid key usage; reissue", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Validating a chain whose signing certificate either has no KeyUsage extension at all, or has a KeyUsage that does not include keyCertSign (e.g. only cRLSign or digitalSignature).

Common situations: Intermediate CA issued by an internal PKI with restrictive/incorrect key usage templates; hand-crafted test CAs generated without -keyUsage extensions; legacy CAs predating strict profile enforcement.

Understand the failure class

Related errors


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