elastic/elasticsearch · error · IOException
cannot read encrypted key without a password
Error message
cannot read encrypted key without a password
What it means
Thrown by possiblyDecryptPKCS1Key when the PEM is marked 'Proc-Type: 4,ENCRYPTED' and a DEK-Info header exists, but the password supplier returns null. The library cannot decrypt an OpenSSL-format encrypted key without a password, so it aborts rather than attempting a no-op decrypt.
Source
Thrown at libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java:480
* @param passwordSupplier A password supplier for the encrypted (password protected) key
* @return the decrypted key bytes
* @throws GeneralSecurityException if the key can't be decrypted
* @throws IOException if the PEM headers are missing or malformed
*/
private static byte[] possiblyDecryptPKCS1Key(Map<String, String> pemHeaders, String keyContents, Supplier<char[]> passwordSupplier)
throws GeneralSecurityException, IOException {
byte[] keyBytes = Base64.getDecoder().decode(keyContents);
String procType = pemHeaders.get("Proc-Type");
if ("4,ENCRYPTED".equals(procType)) {
// We only handle PEM encryption
String encryptionParameters = pemHeaders.get("DEK-Info");
if (null == encryptionParameters) {
// malformed pem
throw new IOException("Malformed PEM File, DEK-Info header is missing");
}
char[] password = passwordSupplier.get();
if (password == null) {
throw new IOException("cannot read encrypted key without a password");
}
Cipher cipher = getCipherFromParameters(encryptionParameters, password);
byte[] decryptedKeyBytes = cipher.doFinal(keyBytes);
return decryptedKeyBytes;
}
return keyBytes;
}
/**
* Creates a {@link Cipher} from the contents of the DEK-Info header of a PEM file. RFC 1421 indicates that supported algorithms are
* defined in RFC 1423. RFC 1423 only defines DES-CBS and triple DES (EDE) in CBC mode. AES in CBC mode is also widely used though ( 3
* different variants of 128, 192, 256 bit keys )
*
* @param dekHeaderValue The value of the DEK-Info PEM header
* @param password The password with which the key is encrypted
* @return a cipher of the appropriate algorithm and parameters to be used for decryption
* @throws GeneralSecurityException if the algorithm is not available in the used security provider, or if the key is inappropriate
* for the cipherView on GitHub (pinned to db6a809a66)
Solutions
- Configure the password: in Elasticsearch, store it with 'bin/elasticsearch-keystore add <setting>.secure_key_pass' (e.g. xpack.security.transport.ssl.secure_key_pass).
- Ensure the password supplier returns a non-null char[] for encrypted keys.
- If the key is not meant to be encrypted, regenerate it without a passphrase: 'openssl rsa -in enc.key -out plain.key'.
- Verify the running process has access to whatever source the password supplier reads (env var, secret store, file permissions).
Example fix
// before: supplier returns null for an encrypted key
Supplier<char[]> pw = () -> null;
PemUtils.readPrivateKey(keyPath, pw);
// after: supplier resolves the password (e.g. from a secure keystore)
Supplier<char[]> pw = () -> keystore.getCharArray("xpack.security.transport.ssl.secure_key_pass");
PemUtils.readPrivateKey(keyPath, pw); Defensive patterns
Strategy: validation
Validate before calling
// Ensure the password supplier returns a non-null char[] before reading an encrypted key
char[] pw = passwordSupplier.get();
if (pw == null || pw.length == 0) {
throw new IllegalStateException("Encrypted key requires a non-empty password; configure xpack.*.secure_key_pass in the keystore");
} Try / catch
try { PemUtils.readPrivateKey(path, passwordSupplier); }
catch (IOException e) { if (e.getMessage().contains("cannot read encrypted key without a password")) { /* provision password */ } else throw e; } Prevention
- Provision the key password via 'bin/elasticsearch-keystore add <setting>.secure_key_pass'.
- Test the password supplier in isolation before wiring it into SSL config.
- If the key is not meant to be encrypted, export it unencrypted to remove the dependency.
When it happens
Trigger: Calling PemUtils.readPrivateKey(path, passwordSupplier) where passwordSupplier.get() returns null for an encrypted OpenSSL-format key; the Elasticsearch SSL keystore password setting is empty or unset while the key file is encrypted.
Common situations: Misconfigured xpack.ssl.secure_key_pass / keystore.password; the password was added to the wrong keystore entry; the password supplier reads from an environment variable that is unset in the running process; deploying an encrypted key but forgetting to provision the secret.
Related errors
- cannot read encrypted key [{}] without a password
- Malformed PEM File, DEK-Info header is missing
- Malformed PEM file, DEK-Info PEM header is invalid
- Malformed PEM file, DEK-Info IV is invalid
- Private Key encrypted with unsupported algorithm [{}]
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/5c8406897c64af8f.
Report an issue: GitHub.