spring-projects/spring-security · error · OAuth2AuthenticationException
invalid_client
invalid_client
Error message
Client authentication failed: x509_certificate_issuer
What it means
This error is thrown during X.509 self-signed certificate client authentication when the presented certificate's issuer DN does not equal its subject DN. A self-signed certificate must have identical issuer and subject principals; anything else is not self-signed and cannot be used with this authenticator. The server rejects the client with an invalid_client OAuth2 error naming the failing parameter 'x509_certificate_issuer'.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/X509SelfSignedCertificateVerifier.java:85
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
private static final JWKMatcher HAS_X509_CERT_CHAIN_MATCHER = new JWKMatcher.Builder().hasX509CertChain(true)
.build();
private final Function<RegisteredClient, JWKSet> jwkSetSupplier = new JwkSetSupplier();
@Override
public void accept(OAuth2ClientAuthenticationContext clientAuthenticationContext) {
OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();
RegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();
X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
Assert.notEmpty(clientCertificateChain, "clientCertificateChain cannot be empty");
X509Certificate clientCertificate = clientCertificateChain[0];
X500Principal issuer = clientCertificate.getIssuerX500Principal();
X500Principal subject = clientCertificate.getSubjectX500Principal();
if (issuer == null || !issuer.equals(subject)) {
throw invalidClient("x509_certificate_issuer");
}
JWKSet jwkSet = this.jwkSetSupplier.apply(registeredClient);
boolean publicKeyMatches = false;
for (JWK jwk : jwkSet.filter(HAS_X509_CERT_CHAIN_MATCHER).getKeys()) {
X509Certificate x509Certificate = jwk.getParsedX509CertChain().get(0);
PublicKey publicKey = x509Certificate.getPublicKey();
if (Arrays.equals(clientCertificate.getPublicKey().getEncoded(), publicKey.getEncoded())) {
publicKeyMatches = true;
break;
}
}
if (!publicKeyMatches) {
throw invalidClient("x509_certificate");
}
}View on GitHub (pinned to 96852e8860)
Solutions
- Generate a truly self-signed certificate (keytool -genkeypair -keyalg RSA without -issuer, or openssl req -x509) and register it in the client's JWK Set via the jwk_set_url client setting
- Verify the certificate with: cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal()) before configuring it
- If you need CA-issued certificates, use a client authentication method that supports them instead of the self-signed verifier
Example fix
// before certificate issued by Corporate CA -> presented as self-signed // after keytool -genkeypair -alias client -keyalg RSA -keystore client.p12 -dname "CN=client" # self-signed by construction
Defensive patterns
Strategy: validation
Validate before calling
if (cert.getIssuerX500Principal() == null || !cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal())) {
throw new IllegalArgumentException("certificate is not self-signed: issuer != subject");
} Try / catch
try {
authenticator.accept(context);
} catch (OAuth2AuthenticationException ex) {
// check ex.getError().getErrorCode() == "invalid_client" and description for x509_certificate_issuer
} Prevention
- Use keytool -genkeypair or openssl req -x509 so issuer==subject by construction
- Never submit CA-issued certificates to a self-signed certificate verifier
- Log certificate issuer/subject DN at startup to confirm the right keystore entry is used
When it happens
Trigger: Calling OAuth2 client authentication with an X.509 certificate chain whose first certificate has issuer != subject, e.g. a CA-signed (end-entity) certificate submitted to X509SelfSignedCertificateVerifier.
Common situations: Developers configure a certificate issued by an internal CA (or a chained PKCS#12 entry) but register the client with a self-signed-certificate client authentication method; also happens when the wrong entry from a keystore is presented instead of the self-signed one.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/42972ed05247e1cd.
Report an issue: GitHub.