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

  1. Run 'openssl x509 -in cert.pem -noout -subject' to see which RDN types your certificates actually contain.
  2. Call extractor.setSubjectDnType() with the exact type string present in the DN (case-sensitive, e.g. "CN", "UID", "emailAddress").
  3. If the identifier lives in an RDN not expressible as a simple type match, use SubjectDnX509PrincipalExtractor with a custom regex instead.
  4. 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

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


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)