apache/pulsar · error · AuthenticationException

Client unable to authenticate with TLS certificate

Error message

Client unable to authenticate with TLS certificate

What it means

AuthenticationProviderTls.authenticate() parses the subject DN of the client's X.509 certificate and uses the CN as the authenticated role. It throws this AuthenticationException when no CN could be extracted (commonName == null) — the certificate exists but its subject has no usable CommonName, so the client's role cannot be determined.

Source

Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTls.java:111

                // CN=Steve Kille,O=Isode Limited,C=GB
                Certificate[] certs = authData.getTlsCertificates();
                if (null == certs) {
                    errorCode = ErrorCode.INVALID_CERTS;
                    throw new AuthenticationException("Failed to get TLS certificates from client");
                }
                String distinguishedName = ((X509Certificate) certs[0]).getSubjectX500Principal().getName();
                for (String keyValueStr : distinguishedName.split(",")) {
                    String[] keyValue = keyValueStr.split("=", 2);
                    if (keyValue.length == 2 && "CN".equals(keyValue[0]) && !keyValue[1].isEmpty()) {
                        commonName = keyValue[1];
                        break;
                    }
                }
            }

            if (commonName == null) {
                errorCode = ErrorCode.INVALID_CN;
                throw new AuthenticationException("Client unable to authenticate with TLS certificate");
            }
            authenticationMetrics.recordSuccess();
        } catch (AuthenticationException exception) {
            incrementFailureMetric(errorCode);
            throw exception;
        }
        return commonName;
    }

}

View on GitHub (pinned to 820761864e)

Solutions

  1. Issue the client certificate with a non-empty CN in the subject (this CN becomes the auth role)
  2. Alternatively enable the topic-level authorization mapping via DN if CN is intentionally absent (configure tlsCommonNameRequiredEquivalent or use a different auth provider)
  3. Verify with openssl x509 -noout -subject that the cert actually contains CN=

Example fix

// before: cert subject without CN
openssl req -new -subj "/O=Acme" -key client.key -out client.csr
// after: include CN
openssl req -new -subj "/CN=admin/O=Acme" -key client.key -out client.csr
Defensive patterns

Strategy: validation

Validate before calling

X509Certificate x509 = (X509Certificate) certs[0];
String cn = x509.getSubjectX500Principal().getName();
if (!cn.contains("CN=")) {
    throw new AuthenticationException("Certificate subject has no CN");
}

Try / catch

try {
    role = provider.authenticate(authData);
} catch (AuthenticationException e) {
    log.warn("TLS auth failed: certificate has no CN; inspect with openssl x509 -noout -subject", e);
}

Prevention

When it happens

Trigger: Calling authenticate() with a certificate whose X.509 subject DN contains no CN attribute, or an empty CN — the distinguishedName split finds no 'CN=<value>' pair.

Common situations: Generating client certificates with only O/OU fields and no CN; using SAN-only certificates without CN; certificates issued by an internal CA with a custom subject template.

Understand the failure class

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/8f5b33ce2497f3c9. Report an issue: GitHub.