elastic/elasticsearch · error · SslConfigException

the ${keystoreType} keystore [${path}]does not contain a pri

Error message

the ${keystoreType} keystore [${path}]does not contain a private key entry

What it means

A keystore configured as the source of a private key (StoreKeyConfig) loaded successfully but contained no PrivateKey entries. The code walks every alias via keyStore.isKeyEntry(alias) and, if none qualifies, throws. Without a key entry the keystore cannot present a client or server identity during the TLS handshake.

Source

Thrown at libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java:206

    }

    /**
     * Verifies that the keystore contains at least 1 private key entry.
     */
    private static void checkKeyStore(KeyStore keyStore, Path path) throws KeyStoreException {
        Enumeration<String> aliases = keyStore.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (keyStore.isKeyEntry(alias)) {
                return;
            }
        }
        String message = "the " + keyStore.getType() + " keystore";
        if (path != null) {
            message += " [" + path + "]";
        }
        message += "does not contain a private key entry";
        throw new SslConfigException(message);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
        sb.append('{');

        String path = keystorePath;
        if (path != null) {
            sb.append("path=").append(path).append(", ");
        }
        sb.append("type=").append(type);
        sb.append(", storePassword=").append(storePassword.length == 0 ? "<empty>" : "<non-empty>");
        sb.append(", keyPassword=");
        if (keyPassword.length == 0) {
            sb.append("<empty>");
        } else if (Arrays.equals(storePassword, keyPassword)) {
            sb.append("<same-as-store-password>");

View on GitHub (pinned to db6a809a66)

Solutions

  1. Run keytool -list -keystore <path> -v and confirm at least one alias is a PrivateKeyEntry
  2. If this file is meant to verify peers (not present identity), move it to the truststore configuration instead of the keystore/key configuration
  3. Regenerate the keystore so it contains the private key and its certificate chain (e.g. openssl pkcs12 -export -in cert.pem -inkey key.pem -out node.jks -chain -CAfile ca.pem)
  4. Check that keystore.type (jks/pkcs12) matches the actual file format

Example fix

// before: file only holds trusted certs
keytool -list -keystore node.jks  # => trustedCertEntry, no PrivateKeyEntry
// after: regenerate with the private key
openssl pkcs12 -export -in node.crt -inkey node.key -out node.p12 -chain -CAfile ca.pem
Defensive patterns

Strategy: validation

Validate before calling

KeyStore ks = KeyStore.getInstance(type);
try (InputStream in = Files.newInputStream(path)) {
    ks.load(in, password);
}
boolean hasKey = Collections.list(ks.aliases()).stream().anyMatch(ks::isKeyEntry);
if (!hasKey) throw new IllegalStateException("keystore has no private key entry: " + path);

Type guard

static boolean keystoreHasKeyEntry(KeyStore ks) throws KeyStoreException {
    return Collections.list(ks.aliases()).stream().anyMatch(ks::isKeyEntry);
}

Try / catch

try {
    new StoreKeyConfig(...);
} catch (SslConfigException e) {
    // re-create keystore with a PrivateKeyEntry before retrying
    throw e;
}

Prevention

When it happens

Trigger: Constructing StoreKeyConfig for a keystore whose aliases are all trustedCertificateEntry (or empty). The configured keystore.path / keystore.type is loaded, then the alias scan runs and finds no key entry. The message interpolates keystoreType and the path (path is omitted from the bracketed segment when null).

Common situations: Pointed keystore.path at a truststore that only contains CA certs; created the keystore with keytool -genkey missing or with -importcert instead of importing the key; the private key lives in a PKCS#12 exported from a browser but the wrong file was referenced; keystore.password unlocks a different file.

Related errors


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