apereo/cas · error · CredentialsException
Unable to accept certificate
Error message
Unable to accept certificate
What it means
Thrown by the X.509 client-certificate authenticator when one or more certificate attributes (configured required attributes) extracted from the presented TLS certificate do not match the acceptable values defined by the registered service. The certificate itself parsed and a profile was built, but attribute-level policy rejected it.
Solutions
- Compare the actual certificate attributes (openssl x509 -text) against the required attribute map in the registered service and update the registry to match the real DN format
- Normalize DN configuration (ordering of RDNs, spacing) to the exact string CAS extracts
- Reissue or obtain a client certificate containing the required attributes (e.g. correct email SAN)
- Relax or correct the acceptable-attribute configuration if it is unnecessarily strict
Example fix
// before
"requiredAttributes": { "subjectDN": "CN=Alice, OU=Org, O=Company" }
// after
"requiredAttributes": { "subjectDN": "CN=Alice,OU=Org,O=Company" } Defensive patterns
Strategy: validation
Validate before calling
const cert = req.socket.getPeerCertificate();
if (!cert || !cert.subject) throw new Error('mTLS client certificate required and must carry the attributes configured on the service'); Prevention
- Match the registered service's expected DN strings exactly to the issued certificate format
- Include required SANs (e.g. email) when reissuing certificates
- Re-verify certificate contents after any CA or issuance-policy change
When it happens
Trigger: Mutual-TLS request presents a client certificate whose subject/issuer/SAN/RFC822 attributes fail isAcceptableX509Attribute checks against the registered service's required certificate attribute map — any non-matching attribute makes the allMatch predicate false.
Common situations: Certificate reissued with a different subject DN or email SAN than what the service requires; DN formatting differences (spaces, ordering, escaped characters) breaking exact-string comparison; service registry configured with overly strict expected attribute values; expired/reissued certs after CA migration.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- No Certificates provided
- Expired or invalid certificate in metadata for
- Certificate subject does not match pattern
- Certificate keyUsage constraint forbids SSL client…
- Unlimited certificate path length not allowed by…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/0a8397089d3c5486.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/authenticator/OAuth20X509Authenticator.java:79
val profile = result.get().getUserProfile();
val certificate = ((X509Credentials) credentials).getCertificate();
val digest = EncodingUtils.encodeBase64(DigestUtils.digest("SHA-256", certificate.getPublicKey().getEncoded()));
profile.addAttribute(OAuth20Constants.X509_CERTIFICATE_DIGEST, digest);
profile.addAttribute(AuthenticationManager.AUTHENTICATION_METHOD_ATTRIBUTE, "X.509");
profile.addAttribute(OAuth20Constants.CLIENT_ID, registeredService.getClientId());
val attributeMap = CollectionUtils.<String, String>wrap(
"x509-sanEmail", registeredService.getTlsClientAuthSanEmail(),
"x509-sanDNS", registeredService.getTlsClientAuthSanDns(),
"x509-sanIP", registeredService.getTlsClientAuthSanIp(),
"x509-sanURI", registeredService.getTlsClientAuthSanUri()
);
val accepted = attributeMap
.entrySet()
.stream()
.allMatch(entry -> isAcceptableX509Attribute(profile, entry.getKey(), entry.getValue()));
if (!accepted) {
throw new CredentialsException("Unable to accept certificate");
}
}
return result;
}
protected @Nullable OAuthRegisteredService locateRegisteredService(
final CallContext ctx, final Credentials credentials) {
if (credentials instanceof final X509Credentials x509Credentials) {
val certificate = x509Credentials.getCertificate();
val subjectAltNames = FunctionUtils.doUnchecked(certificate::getSubjectAlternativeNames);
if (subjectAltNames != null) {
val spiffeEntries = new ArrayList<String>();
subjectAltNames.forEach(altName -> {
altName.stream()
.filter(String.class::isInstance)
.map(String.class::cast)
.filter(name -> Strings.CI.startsWith(name, SUBJECT_ALT_NAME_SPIFFE_PREFIX))View on GitHub (pinned to e7288fc434)