elastic/elasticsearch · error · SslConfigException

cannot read encrypted key [{}] without a password

Error message

cannot read encrypted key [{}] without a password

What it means

Thrown by PemUtils.parsePrivateKey() when the file's first PEM header is `-----BEGIN ENCRYPTED PRIVATE KEY-----` (PKCS#8 encrypted) but the password supplier returned null. PKCS#8 encrypted keys cannot be decrypted without a password, so parsing aborts immediately.

Source

Thrown at libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java:141

     * encoded formats of encrypted and plaintext RSA, DSA and EC(secp256r1) keys
     *
     * @param keyPath           the path for the key file
     * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key
     * @return a private key from the contents of the file
     */
    static PrivateKey parsePrivateKey(Path keyPath, Supplier<char[]> passwordSupplier) throws IOException, GeneralSecurityException {
        try (BufferedReader bReader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
            String line = bReader.readLine();
            while (null != line && line.startsWith(HEADER) == false) {
                line = bReader.readLine();
            }
            if (null == line) {
                throw new SslConfigException("Error parsing Private Key [" + keyPath.toAbsolutePath() + "], file is empty");
            }
            if (PKCS8_ENCRYPTED_HEADER.equals(line.trim())) {
                char[] password = passwordSupplier.get();
                if (password == null) {
                    throw new SslConfigException("cannot read encrypted key [" + keyPath.toAbsolutePath() + "] without a password");
                }
                return parsePKCS8Encrypted(bReader, password);
            } else if (PKCS8_HEADER.equals(line.trim())) {
                return parsePKCS8(bReader);
            } else if (PKCS1_HEADER.equals(line.trim())) {
                return parsePKCS1Rsa(bReader, passwordSupplier);
            } else if (OPENSSL_DSA_HEADER.equals(line.trim())) {
                return parseOpenSslDsa(bReader, passwordSupplier);
            } else if (OPENSSL_DSA_PARAMS_HEADER.equals(line.trim())) {
                return parseOpenSslDsa(removeDsaHeaders(bReader), passwordSupplier);
            } else if (OPENSSL_EC_HEADER.equals(line.trim())) {
                return parseOpenSslEC(bReader, passwordSupplier);
            } else if (OPENSSL_EC_PARAMS_HEADER.equals(line.trim())) {
                return parseOpenSslEC(removeECHeaders(bReader), passwordSupplier);
            } else {
                throw new SslConfigException(
                    "cannot read PEM private key ["
                        + keyPath.toAbsolutePath()

View on GitHub (pinned to db6a809a66)

Solutions

  1. Add the key password to the Elasticsearch keystore: `bin/elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password` (or the equivalent ssl.key_passphrase setting for PEM).
  2. If you do not need encryption, re-export the key unencrypted: `openssl pkcs8 -topk8 -in encrypted.pem -out plain.pem -nocrypt`.
  3. Verify the password setting name matches the SSL context (transport vs http) you are configuring.
  4. Confirm the password is non-empty and matches the one used to encrypt the key.

Example fix

# before: encrypted PKCS#8 key but no password configured
# elasticsearch.yml
xpack.security.http.ssl:
  enabled: true
  key: encrypted.pk8.pem
  certificate: cert.pem

# after: add the passphrase via the keystore
bin/elasticsearch-keystore add xpack.security.http.ssl.key.secure_password
# (enter the password when prompted; restart Elasticsearch)
# or re-export the key unencrypted:
openssl pkcs8 -topk8 -in encrypted.pk8.pem -out plain.pk8.pem -nocrypt
Defensive patterns

Strategy: validation

Validate before calling

// Detect encrypted PKCS#8 keys and ensure a password is configured before parsing.
public static boolean isEncryptedPkcs8(Path p) throws IOException {
    try (BufferedReader r = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
        String line;
        while ((line = r.readLine()) != null) {
            if (line.startsWith("-----BEGIN ENCRYPTED PRIVATE KEY-----")) return true;
            if (line.startsWith("-----BEGIN")) return false;
        }
    }
    return false;
}

// Configure Elasticsearch keystore:
// bin/elasticsearch-keystore add xpack.security.http.ssl.key.secure_password

Prevention

When it happens

Trigger: parsePrivateKey detects PKCS8_ENCRYPTED_HEADER, calls passwordSupplier.get(), and the supplier returns null. Typical when Elasticsearch is configured with an encrypted PEM key but no `ssl.key_passphrase` / secure setting is provided, or when the keystore setting that should hold the passphrase is missing.

Common situations: Encrypted PKCS#8 key configured without a corresponding key password in elasticsearch.yml or the Elasticsearch keystore; password setting typo (`key_passphrase` vs `key_password`); password stored in a keystore entry that was not loaded; or the password supplier is wired to return null on missing config.

Related errors


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