eclipse-vertx/vert.x · error · RuntimeException

Missing -----BEGIN PRIVATE KEY----- or -----BEGIN RSA PRIVAT

Error message

Missing -----BEGIN PRIVATE KEY----- or -----BEGIN RSA PRIVATE KEY----- or -----BEGIN EC PRIVATE KEY----- delimiter

What it means

Same site family as the empty-message variant: loadPrivateKey() found no PEM private key block in the provided buffer and throws because the parsed key list is empty. It explicitly names the three delimiters it supports: BEGIN PRIVATE KEY, BEGIN RSA PRIVATE KEY, BEGIN EC PRIVATE KEY.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/net/impl/KeyStoreHelper.java:322

          case "PRIVATE KEY":
            // in PKCS#8 the key algorithm is indicated at the beginning of the ASN.1 structure
            // so we can use the corresponding key factory once we know the algorithm name
            String algorithm = PrivateKeyParser.getPKCS8EncodedKeyAlgorithm(content);
            if (rsaKeyFactory.getAlgorithm().equals(algorithm)) {
              return Collections.singletonList(rsaKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(content)));
            } else if (ecKeyFactory != null && ecKeyFactory.getAlgorithm().equals(algorithm)) {
              return Collections.singletonList(ecKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(content)));
            }
            // fall through if ECC is not supported by JVM
          default:
            return Collections.emptyList();
        }
      } catch (InvalidKeySpecException e) {
        throw new VertxException(e);
      }
    });
    if (pems.isEmpty()) {
      throw new RuntimeException("Missing -----BEGIN PRIVATE KEY----- or -----BEGIN RSA PRIVATE KEY----- or -----BEGIN EC PRIVATE KEY----- delimiter");
    }
    return pems.get(0);
  }

  private static KeyFactory getECKeyFactory() {
    try {
      return KeyFactory.getInstance("EC");
    } catch (NoSuchAlgorithmException e) {
      // support for ECC is not mandatory in JVM
      return null;
    }
  }

  public static KeyStore loadCA(Stream<Buffer> certValues) throws Exception {
    final KeyStore keyStore = createEmptyKeyStore();
    keyStore.load(null, null);
    int count = 0;
    Iterable<Buffer> iterable = certValues::iterator;

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Confirm the file contains -----BEGIN PRIVATE KEY----- (PKCS#8), -----BEGIN RSA PRIVATE KEY-----, or -----BEGIN EC PRIVATE KEY-----.
  2. Convert PKCS#1 to PKCS#8 with: openssl pkcs8 -topk8 -nocrypt -in rsakey.pem -out pkcs8key.pem.
  3. Decrypt encrypted keys first (remove ENCRYPTED PRIVATE KEY / Proc-Type headers) or load them via a JKS/PKCS12 keystore instead.
  4. Ensure the key path/Buffer is correct and not the certificate file.

Example fix

// before
options.setKeyPath("chain-ca.pem"); // no key block
// after
options.setKeyPath("server.key"); // -----BEGIN PRIVATE KEY-----
Defensive patterns

Strategy: validation

Validate before calling

String pem = Files.readString(Path.of(keyPath));
boolean ok = pem.lines().anyMatch(l -> l.startsWith("-----BEGIN") && l.contains("PRIVATE KEY"));
if (!ok) throw new IllegalStateException(keyPath + " has no private key PEM block");

Type guard

boolean hasPrivateKeyBlock(String s) {
  return s != null && s.contains("-----BEGIN PRIVATE KEY-----") ||
         s != null && s.contains("-----BEGIN RSA PRIVATE KEY-----") ||
         s != null && s.contains("-----BEGIN EC PRIVATE KEY-----");
}

Prevention

When it happens

Trigger: keyCertOptions.setKeyPath/setKeyValue receiving a PEM buffer whose only blocks are certificates, or whose key block type is unsupported (e.g. ENCRYPTED PRIVATE KEY), or a truncated file with BEGIN but no key data.

Common situations: Swapping cert and key paths; using an encrypted PKCS#8 key with a password the parser cannot supply; concatenating only the CA chain into the key option.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/23a5699f9fb7d7d1. Report an issue: GitHub.