apereo/cas · error · FailedLoginException

Certificate subject does not match pattern

Error message

Certificate subject does not match pattern 

What it means

X509CredentialsAuthenticationHandler.validate() only accepts end-entity certificates (basicConstraints pathLength < 0) whose subject DN matches the configured regExSubjectDnPattern. If the certificate's subject DN does not match, the credential is rejected with FailedLoginException.

Solutions

  1. Log the exact subject DN and update regExSubjectDnPattern to match it (mind component order and escaping of commas/spaces).
  2. Issue/obtain a client certificate whose subject DN conforms to the configured pattern.
  3. Use a permissive pattern such as '.*' if all subjects should be accepted.
  4. Enable debug logging on the handler to see the evaluated DN string.

Example fix

// before
cas.authn.x509.reg-ex-subject-dn-pattern=CN=John Doe,OU=IT
// after
cas.authn.x509.reg-ex-subject-dn-pattern=CN=[^,]+,.*OU=IT.*
Defensive patterns

Strategy: validation

Validate before calling

String dn = cert.getSubjectX500Principal().getName();
if (dn == null || !Pattern.compile(configuredRegex).matcher(dn).matches()) { reject("subject DN not allowed"); }

Type guard

boolean subjectAllowed(X509Certificate cert, Pattern p) {
    return p.matcher(cert.getSubjectX500Principal().getName()).matches();
}

Try / catch

try {
    handler.authenticate(credential);
} catch (FailedLoginException e) {
    // subject DN pattern mismatch: inspect cert.getSubjectX500Principal() vs configured regex
}

Prevention

When it happens

Trigger: A client presents a non-CA certificate whose subject DN does not match cas.authn.x509.regExSubjectDnPattern while isCertificateAllowed() is evaluated during authentication.

Common situations: Regex written with wrong DN component order (JDK renders DNs as 'CN=..., OU=..., O=...') or unescaped commas/spaces; a CA re-issues certs with different subject layout; a test client cert is used against a production pattern.

Understand the failure class

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/d1172c93cf2bda12. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-x509-core/src/main/java/org/apereo/cas/adaptors/x509/authentication/handler/support/X509CredentialsAuthenticationHandler.java:238

        if (hasTrustedIssuer && clientCert != null) {
            x509Credential.setCertificate(clientCert);
            return new DefaultAuthenticationHandlerExecutionResult(this, x509Credential,
                this.principalFactory.createPrincipal(x509Credential.getId()));
        }
        LOGGER.warn("Either client certificate could not be determined, or a trusted issuer could not be located");
        throw new FailedLoginException();
    }

    private void validate(final X509Certificate cert) throws GeneralSecurityException {
        cert.checkValidity();
        this.revocationChecker.check(cert);

        val pathLength = cert.getBasicConstraints();
        if (pathLength < 0) {
            if (!isCertificateAllowed(cert)) {
                val msg = "Certificate subject does not match pattern " + this.regExSubjectDnPattern.pattern();
                LOGGER.error(msg);
                throw new FailedLoginException(msg);
            }
            if (this.checkKeyUsage && !isValidKeyUsage(cert)) {
                val msg = "Certificate keyUsage constraint forbids SSL client authentication.";
                LOGGER.error(msg);
                throw new FailedLoginException(msg);
            }
        } else {
            if (pathLength == Integer.MAX_VALUE && !this.maxPathLengthAllowUnspecified) {
                val msg = "Unlimited certificate path length not allowed by configuration.";
                LOGGER.error(msg);
                throw new FailedLoginException(msg);
            }
            if (pathLength > this.maxPathLength && pathLength < Integer.MAX_VALUE) {
                val msg = String.format("Certificate path length %s exceeds maximum value %s.", pathLength, this.maxPathLength);
                LOGGER.error(msg);
                throw new FailedLoginException(msg);
            }
        }

View on GitHub (pinned to e7288fc434)