Summary
AttestationValidatorUtil.verifierFromX5CChain() skips PKIX certificate chain validation when x5c[0] is self-signed (subject == issuer). An attacker generates a software key, creates a self-signed cert, and forges a key attestation JWT claiming hardware-level security (VAN.5). Keycloak accepts it and issues a Verifiable Credential.
Affected: Keycloak <= 26.5.6 (latest stable), all versions with oid4vc-vci feature
CWE: CWE-295 (Improper Certificate Validation)
Acknowledgement: The Keycloak Security team acknowledges Trung Nguyen from CyStack Security for reporting this.
As OID4VCI is in experimental mode, this is handled as a regular bug in upstream, and not a CVE.
The automated reproducer is not attached to the public issue.
Vulnerable Code
services/.../oid4vc/issuance/keybinding/AttestationValidatorUtil.java:332-347
// Check if this is a self-signed certificate (for test environments)
X509Certificate firstCert = certChain.get(0);
boolean isSelfSigned = firstCert.getSubjectX500Principal()
.equals(firstCert.getIssuerX500Principal());
// Only validate the certificate chain if it's not a self-signed certificate in a test environment
if (!isSelfSigned) {
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
PKIXParameters params = new PKIXParameters(getTrustAnchors());
params.setRevocationEnabled(false);
validator.validate(certPath, params);
}
PublicKey publicKey = certChain.get(0).getPublicKey(); // attacker's key trusted
Comment says "for test environments" but there is no runtime check. Bypass is always active.
Execution Path
POST /protocol/oid4vc/credential
AttestationProofValidator.validateProof()
AttestationValidatorUtil.validateAttestationJwt()
validateJwsHeader() -- alg/typ check passes (ES256)
verifierFromX5CChain() -- PKIX SKIPPED for self-signed cert
verify JWT signature -- passes (attacker signed with own key)
validateAttestationPayload() -- nonce/resistance checks pass
return attested_keys -- attacker's software JWK
credential issued
No secondary defense: no SPI intercept, no realm-level trust store, no event listener.
Confirmed
| Test |
x5c cert |
HTTP |
Result |
Self-signed (subject == issuer) |
CN=Fake WSCD / CN=Fake WSCD |
200 |
Credential issued |
Non-self-signed (subject != issuer) |
CN=Leaf / CN=Fake CA |
400 |
PKIX rejects |
Tested on Keycloak 26.5.6 (latest stable) and nightly (2026-03-30). Both jwt_vc_json and dc+sd-jwt formats exploitable.
Impact
Key attestation proves a credential holder's private key resides in secure hardware. This bypass allows:
- Forge hardware attestation -- Software key claims
iso_18045_high (VAN.5). Server cannot distinguish from genuine hardware.
- Clone credentials -- Software key is exportable. Hardware-backed keys are not.
- Identity fraud -- In eIDAS 2.0 / EU Digital Identity Wallet, government-issued credentials (national ID, driving licence) can be obtained with forged hardware guarantees, then cloned and shared.
- LoA bypass -- Relying parties requiring "high" assurance accept forged credentials.
Fix
if (isSelfSigned) {
throw new VCIssuerException(ErrorType.INVALID_PROOF,
"Self-signed certificates are not accepted for key attestation");
}
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
PKIXParameters params = new PKIXParameters(getTrustAnchors());
params.setRevocationEnabled(false);
validator.validate(certPath, params);
This issue was originally tracked in the private repository. Migrated by @ahus1.
Summary
AttestationValidatorUtil.verifierFromX5CChain()skips PKIX certificate chain validation whenx5c[0]is self-signed (subject == issuer). An attacker generates a software key, creates a self-signed cert, and forges a key attestation JWT claiming hardware-level security (VAN.5). Keycloak accepts it and issues a Verifiable Credential.Affected: Keycloak <= 26.5.6 (latest stable), all versions with
oid4vc-vcifeatureCWE: CWE-295 (Improper Certificate Validation)
Acknowledgement: The Keycloak Security team acknowledges Trung Nguyen from CyStack Security for reporting this.
As OID4VCI is in experimental mode, this is handled as a regular bug in upstream, and not a CVE.
The automated reproducer is not attached to the public issue.
Vulnerable Code
services/.../oid4vc/issuance/keybinding/AttestationValidatorUtil.java:332-347Comment says "for test environments" but there is no runtime check. Bypass is always active.
Execution Path
No secondary defense: no SPI intercept, no realm-level trust store, no event listener.
Confirmed
subject == issuer)CN=Fake WSCD/CN=Fake WSCDsubject != issuer)CN=Leaf/CN=Fake CATested on Keycloak 26.5.6 (latest stable) and nightly (2026-03-30). Both
jwt_vc_jsonanddc+sd-jwtformats exploitable.Impact
Key attestation proves a credential holder's private key resides in secure hardware. This bypass allows:
iso_18045_high(VAN.5). Server cannot distinguish from genuine hardware.Fix
This issue was originally tracked in the private repository. Migrated by @ahus1.