{"record":{"id":"bdd52bca10b926b3","repo":"quarkusio/quarkus","slug":"x-509-svid-proto-spiffe-id-does-not-match-the-leaf","errorCode":null,"errorMessage":"X.509-SVID proto SPIFFE ID does not match the leaf certificate URI SAN; proto: ${protoSpiffeId}, SAN: ${sanSpiffeId}","messagePattern":"X\\.509-SVID proto SPIFFE ID does not match the leaf certificate URI SAN; proto: (.+?), SAN: (.+?)","errorType":"exception","errorClass":"SpiffeConnectionException","httpStatus":null,"severity":"critical","filePath":"extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java","lineNumber":310,"sourceCode":"        if (svid.getX509Svid().isEmpty()) {\n            throw new SpiffeConnectionException(\"X.509-SVID response from SPIRE agent has empty certificate chain\");\n        }\n        if (svid.getX509SvidKey().isEmpty()) {\n            throw new SpiffeConnectionException(\"X.509-SVID response from SPIRE agent has empty private key\");\n        }\n        if (svid.getBundle().isEmpty()) {\n            throw new SpiffeConnectionException(\"X.509-SVID response from SPIRE agent has empty trust bundle\");\n        }\n\n        List<X509Certificate> certChain = parseCertificates(svid.getX509Svid().toByteArray(), \"certificate chain\");\n        if (certChain.isEmpty()) {\n            throw new SpiffeConnectionException(\"X.509-SVID certificate chain is empty\");\n        }\n\n        X509Certificate leaf = certChain.get(0);\n        String sanSpiffeId = SpiffeValidator.validateLeaf(leaf);\n        if (!protoSpiffeId.equals(sanSpiffeId)) {\n            throw new SpiffeConnectionException(\n                    \"X.509-SVID proto SPIFFE ID does not match the leaf certificate URI SAN; proto: \"\n                            + protoSpiffeId + \", SAN: \" + sanSpiffeId);\n        }\n        for (int i = 1; i < certChain.size(); i++) {\n            SpiffeValidator.validateIntermediate(certChain.get(i));\n        }\n\n        String keyAlgorithm = leaf.getPublicKey().getAlgorithm();\n        PrivateKey privateKey;\n        try {\n            privateKey = KeyFactory.getInstance(keyAlgorithm)\n                    .generatePrivate(new PKCS8EncodedKeySpec(svid.getX509SvidKey().toByteArray()));\n        } catch (Exception e) {\n            throw new SpiffeConnectionException(\"X.509-SVID response from SPIRE agent contains an invalid private key\", e);\n        }\n\n        List<X509Certificate> trustBundle = parseCertificates(svid.getBundle().toByteArray(), \"trust bundle\");\n","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java#L292-L328","documentation":"The SPIFFE ID from the protobuf message does not match the URI SAN (spiffe://...) embedded in the leaf certificate the agent returned. SPIFFE security depends on this consistency: the workload identity claimed in the response must be the identity cryptographically bound to the certificate. A mismatch means the response is untrustworthy or misbuilt.","triggerScenarios":"getWorkloadCertificate when protoSpiffeId from X509SVID.getSpiffeId() differs from the SAN extracted by SpiffeValidator.validateLeaf(leaf).","commonSituations":"MitM/tampering on the Workload API socket; a mock or custom Workload API returning inconsistent data; agent bug after a version upgrade; trust domain reconfiguration where the server re-issued certs under a new domain while the entry still lists the old one.","solutions":["Ensure the Workload API socket is the genuine agent unix socket (check for rogue listeners on the path).","Recreate the SPIRE registration entry so the spiffeID matches the identity actually issued.","Restart the agent and re-request the certificate; check agent/server versions.","Audit the SPIRE server entry spiffeID and trust domain for typos or stale values."],"exampleFix":"// before: entry spiffeID spiffe://old.example.org/ns/prod/sa/app, cert issued for spiffe://example.org/...\n// after: align entry with issued identity\n// spire-server entry update -entryID <id> -spiffeID spiffe://example.org/ns/prod/sa/app","handlingStrategy":"try-catch","validationCode":"// after fetching, compare proto id to cert SAN yourself (fail closed on mismatch):\n// if (!protoId.startsWith(\"spiffe://\" + expectedTrustDomain + \"/\")) abort;","typeGuard":null,"tryCatchPattern":"try {\n    doc = client.getWorkloadCertificate();\n} catch (SpiffeConnectionException e) {\n    if (e.getMessage().contains(\"does not match the leaf certificate URI SAN\")) {\n        throw new SecurityException(\"Possible Workload API tampering or misconfigured SPIRE entry: \" + e.getMessage(), e);\n    } else throw e;\n}","preventionTips":["Mount the agent socket read-only at the documented path and check for rogue listeners","Keep SPIRE entry spiffeIDs aligned with issued identities","Pin and verify trust domain configuration","Do not proxy or intercept the Workload API socket"],"tags":["spiffe","spire","certificate","identity-mismatch","security"],"backgroundTag":"spiffe-id-mismatch","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"}