elastic/elasticsearch · error · CertificateException

This trust manager is for client use only and cannot trust…

Error message

This trust manager is for client use only and cannot trust other clients

What it means

Thrown by the same custom X509TrustManager's `checkClientTrusted` override. The trust manager built by `buildTrustManagerFromLeafCertificates` is purpose-built for CLIENT-side verification of a SERVER (leaf-pinning); it explicitly refuses to act as a server-side trust manager that validates inbound client certs. Calling `checkClientTrusted` is a misuse of this manager.

Solutions

  1. Use CA-based or trustStore-based trust (`certificateAuthorities` / `trustStoreFile`) instead of `serverCertificate` when client-cert verification is needed — those build standard `X509TrustManager`s that support both directions.
  2. If you only need server verification, ensure no code path triggers client-cert checking (don't install this manager on a server socket).
  3. Re-architect the test to use the testclusters framework's built-in SSL setup rather than a hand-rolled context.

Example fix

// before: leaf mode cannot do mTLS
testClusters.c.serverCertificate = file('leaf.pem')
// after: CA mode supports both directions
testClusters.c.certificateAuthorities = [file('ca.pem')]
Defensive patterns

Strategy: validation

Validate before calling

// Detect leaf-pinning mode and refuse server-side mTLS use
if (serverCertificate != null && needsClientCertValidation) {
  throw new IllegalStateException("Leaf-pin trust manager cannot validate client certs; use CA mode.");
}

Prevention

When it happens

Trigger: This trust manager (built from `serverCertificate` leaf config) is installed into a context performing server-side client-cert verification (mutual TLS where ES acts as server validating client certs). The resolver's leaf mode only supports one direction.

Common situations: A test inadvertently uses leaf-pinning trust config in a context that needs bidirectional TLS; wiring the test client's trust manager into a server socket factory; custom networking code that calls `sslContext.init(..., trustManager, ...)` and then the runtime invokes `checkClientTrusted` during an mTLS handshake.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/6de4981cf8829a03. Report an issue: GitHub.

Appendix: source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/SslTrustResolver.java:177

        var trustManager = new X509TrustManager() {
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                final X509Certificate leaf = chain[0];
                if (trusted.contains(leaf) == false) {
                    throw new CertificateException("Untrusted leaf certificate: " + leaf.getSubjectX500Principal());
                }
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                // This doesn't apply when trusting leaf certs, and is only really needed for server trust managers anyways
                return new X509Certificate[0];
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                throw new CertificateException("This trust manager is for client use only and cannot trust other clients");
            }

        };
        return new TrustManager[] { trustManager };
    }

    private static Collection<Certificate> readCertificatesFromKeystore(File file, String password) throws GeneralSecurityException,
        IOException {
        var keyStore = readKeyStoreFromFile(file, password);
        final Set<Certificate> certificates = new HashSet<>(keyStore.size());
        var enumeration = keyStore.aliases();
        while (enumeration.hasMoreElements()) {
            var alias = enumeration.nextElement();
            if (keyStore.isKeyEntry(alias)) {
                certificates.add(keyStore.getCertificate(alias));
            }
        }
        return certificates;

View on GitHub (pinned to db6a809a66)