spring-projects/spring-security · error · BadCredentialsException
No matching pattern was found in subject DN
Error message
No matching pattern was found in subject DN: {0} What it means
SubjectX500PrincipalExtractor looks for the RDN whose type equals the configured subjectDnType (default 'CN') in the subject DN and returns its value. If no RDN of that type exists it throws BadCredentialsException with 'No matching pattern was found in subject DN: {0}'.
Solutions
- Run 'openssl x509 -in cert.pem -noout -subject' to see which RDN types your certificates actually contain.
- Call extractor.setSubjectDnType() with the exact type string present in the DN (case-sensitive, e.g. "CN", "UID", "emailAddress").
- If the identifier lives in an RDN not expressible as a simple type match, use SubjectDnX509PrincipalExtractor with a custom regex instead.
- Ensure the certificate template/CA issues certs that include the expected RDN.
Example fix
// before
SubjectX500PrincipalExtractor extractor = new SubjectX500PrincipalExtractor(); // expects CN
// after
SubjectX500PrincipalExtractor extractor = new SubjectX500PrincipalExtractor();
extractor.setSubjectDnType("UID"); // match the RDN type actually present in the certificate DN Defensive patterns
Strategy: validation
Validate before calling
String dn = cert.getSubjectX500Principal().getName(X500Principal.RFC2253);
boolean hasType = new LdapName(dn).getRdns().stream()
.anyMatch(rdn -> rdn.getType().equalsIgnoreCase("CN"));
if (!hasType) throw new IllegalArgumentException("Certificate DN lacks expected RDN type"); Type guard
boolean dnContainsRdnType(String dn, String type) {
try {
return new LdapName(dn).getRdns().stream()
.anyMatch(r -> r.getType().equals(type));
} catch (InvalidNameException e) { return false; }
} Try / catch
try {
return extractor.extractPrincipal(cert);
} catch (BadCredentialsException e) {
log.warn("DN missing RDN type '{}': {}", expectedType, cert.getSubjectX500Principal());
return null;
} Prevention
- Match subjectDnType to a type actually present in your certs (case-sensitive: "CN", "UID")
- Check certs with openssl x509 -noout -subject before configuring
- Ensure the CA template always includes the identifier RDN
- Consider case-insensitive pre-validation since the extractor compares with equals
When it happens
Trigger: Calling getSubject (via principalName) when the DN contains no RDN whose type matches the configured type — e.g. the extractor expects 'CN' but the certificate only has emailAddress/UID/O attributes, or subjectDnType was set with wrong case ('cn' vs 'CN') so equals fails.
Common situations: Certificates from a CA that uses UID or emailAddress instead of CN; misconfigured extractor.setSubjectDnType("cn") while the DN uses 'CN' (case-sensitive comparison); certificates with an empty or minimal subject.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Failed to parse client certificate
- No matching pattern was found in subject DN
- AccountStatusUserDetailsChecker.disabled
- AccountStatusUserDetailsChecker.expired
- An Authentication object was not found in the…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/e5e3b405cd7e9618.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectX500PrincipalExtractor.java:94
try {
// read most-specific first, see gh-19254
List<Rdn> rdns = new ArrayList<>(new LdapName(subjectDn).getRdns());
Collections.reverse(rdns);
return rdns;
}
catch (InvalidNameException ex) {
throw new BadCredentialsException("Failed to parse client certificate", ex);
}
}
private String getSubject(String subjectDn) {
for (Rdn rdn : getDns(subjectDn)) {
String type = rdn.getType();
if (this.subjectDnType.equals(type)) {
return String.valueOf(rdn.getValue());
}
}
throw new BadCredentialsException(this.messages.getMessage("SubjectX500PrincipalExtractor.noMatching",
new Object[] { subjectDn }, "No matching pattern was found in subject DN: {0}"));
}
@Override
public void setMessageSource(MessageSource messageSource) {
Assert.notNull(messageSource, "messageSource cannot be null");
this.messages = new MessageSourceAccessor(messageSource);
}
/**
* Sets if the principal name should be extracted from the emailAddress or CN
* attribute (default).
*
* By default, the format {@link X500Principal#RFC2253} is passed to
* {@link X500Principal#getName(String)} and the principal is extracted from the CN
* attribute as defined in
* <a href="https://datatracker.ietf.org/doc/html/rfc2253#section-2.3">Converting
* AttributeTypeAndValue of RFC2253</a>.View on GitHub (pinned to 96852e8860)