elastic/elasticsearch · error · IOException

Malformed PEM file, PEM footer is invalid or missing

Error message

Malformed PEM file, PEM footer is invalid or missing

What it means

Thrown by parsePKCS8 when, after the '-----BEGIN PRIVATE KEY-----' header, the scanner reaches EOF without finding '-----END PRIVATE KEY-----' or finds a different footer. The base64 body is collected line-by-line until the footer; absence of the footer means the key material is incomplete.

Solutions

  1. Open the file and confirm both the BEGIN and END PRIVATE KEY markers are present and correctly spelled.
  2. Regenerate the PKCS#8 key: 'openssl pkcs8 -topk8 -inkey raw.key -out pkcs8.key -nocrypt'.
  3. Re-transfer the file in binary mode and verify checksums; strip stray characters with 'dos2unix' if the file came from Windows.
Defensive patterns

Strategy: validation

Validate before calling

// Confirm both the PKCS#8 BEGIN and END markers are present
static boolean hasPkcs8Footer(Path p) throws IOException {
    boolean begin = false, end = false;
    try (BufferedReader r = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
        String line;
        while ((line = r.readLine()) != null) {
            if (line.trim().equals("-----BEGIN PRIVATE KEY-----")) begin = true;
            if (line.trim().equals("-----END PRIVATE KEY-----")) end = true;
        }
    }
    return begin && end;
}

Try / catch

try { PemUtils.readPrivateKey(path, passwordSupplier); }
catch (IOException e) { if (e.getMessage().contains("PEM footer is invalid or missing")) { /* re-issue key */ } else throw e; }

Prevention

When it happens

Trigger: A PKCS#8 PEM file whose '-----END PRIVATE KEY-----' line is missing, corrupted, or replaced by another marker; the file was truncated mid-body; the footer has trailing whitespace or CRLF that breaks the trimmed-equals check (note: line.trim() is applied, so plain spaces are tolerated but other invisible characters may not be).

Common situations: Truncated file from interrupted write or copy; templating system that strips END lines; an editor that auto-corrected the dash sequence; a file that was concatenating multiple keys and one was incomplete.

Understand the failure class

Related errors


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

Appendix: source

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

     * PKCS#8
     *
     * @param bReader the {@link BufferedReader} containing the key file contents
     * @return {@link PrivateKey}
     * @throws IOException              if the file can't be read
     * @throws GeneralSecurityException if the private key can't be generated from the {@link PKCS8EncodedKeySpec}
     */
    private static PrivateKey parsePKCS8(BufferedReader bReader) throws IOException, GeneralSecurityException {
        StringBuilder sb = new StringBuilder();
        String line = bReader.readLine();
        while (line != null) {
            if (PKCS8_FOOTER.equals(line.trim())) {
                break;
            }
            sb.append(line.trim());
            line = bReader.readLine();
        }
        if (null == line || PKCS8_FOOTER.equals(line.trim()) == false) {
            throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
        }
        return parsePKCS8PemString(sb.toString());
    }

    /**
     * Creates a {@link PrivateKey} from a String that contains the PEM encoded representation of a plaintext private key encoded in PKCS8
     * @param pemString the PEM encoded representation of a plaintext private key encoded in PKCS8
     * @return {@link PrivateKey}
     * @throws IOException if the algorithm identifier can not be parsed from DER
     * @throws GeneralSecurityException if the private key can't be generated from the {@link PKCS8EncodedKeySpec}
     */
    public static PrivateKey parsePKCS8PemString(String pemString) throws IOException, GeneralSecurityException {
        byte[] keyBytes = Base64.getDecoder().decode(pemString);
        String keyAlgo = getKeyAlgorithmIdentifier(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(keyAlgo);
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
    }

View on GitHub (pinned to db6a809a66)