Zum Inhalt springen
ecdsa.com

PKCS#1 vs. PKCS#8: BEGIN RSA PRIVATE KEY oder PRIVATE KEY

Beide enthalten dieselben RSA-Zahlen. PKCS#8 ergänzt einen Algorithmusbezeichner und optionale Verschlüsselung – deshalb bevorzugt jede moderne Bibliothek PKCS#8, und manche akzeptieren nichts anderes.

Hinter vielen Fehlern der Art „unsupported key format“ steckt die Frage PKCS#1 vs. PKCS#8. Meist tritt sie auf, wenn ein vor Jahren mit openssl genrsa erzeugter Schlüssel auf eine moderne Bibliothek trifft. Eine PKCS#1-Datei (-----BEGIN RSA PRIVATE KEY-----) enthält nur eine RSA-Schlüsselstruktur; eine PKCS#8-Datei (-----BEGIN PRIVATE KEY-----) verpackt den privaten Schlüssel eines beliebigen Algorithmus zusammen mit einem Algorithmusbezeichner, und ihre verschlüsselte Form (-----BEGIN ENCRYPTED PRIVATE KEY-----) ergänzt einen standardisierten Passwortschutz.

Auf einen Blick

PKCS#1 RSAPrivateKeyPKCS#8 PrivateKeyInfo / OneAsymmetricKey
SpezifikationRFC 8017, Anhang A.1.2RFC 5208, RFC 5958
PEM-LabelRSA PRIVATE KEYPRIVATE KEY; verschlüsselt: ENCRYPTED PRIVATE KEY
AlgorithmenNur RSABeliebige: RSA, EC, Ed25519, X25519, ML-DSA, ML-KEM …
AlgorithmusbezeichnerKeiner (ergibt sich aus dem Label)rsaEncryption, id-ecPublicKey, 1.3.101.112 …
VerschlüsselungOpenSSL-spezifische PEM-Header (Proc-Type, DEK-Info), schwache KDFStandardisiertes PBES2 (PBKDF2 plus AES) nach RFC 8018
Ausgabe von OpenSSL 3Nur mit -traditionalStandard bei genpkey, pkey, rsa
Java PKCS8EncodedKeySpecNicht akzeptiertAkzeptiert
Gox509.ParsePKCS1PrivateKeyx509.ParsePKCS8PrivateKey
Node.js createPrivateKeyAkzeptiert (PEM-Label wird erkannt)Akzeptiert
WebCrypto importKeyNicht akzeptiertFormat pkcs8

Die Gegenstücke bei öffentlichen Schlüsseln sind PKCS#1 RSAPublicKey (BEGIN RSA PUBLIC KEY) und SubjectPublicKeyInfo (BEGIN PUBLIC KEY); alles andere erwartet die zweite Form.

Was in den Formaten steckt

Ein privater RSA-Schlüssel nach PKCS#1 ist eine DER-kodierte SEQUENCE aus Version, Modul n, öffentlichem Exponenten e, privatem Exponenten d, den Primzahlen p und q sowie den CRT-Werten dP, dQ und qInv. Weil die Struktur nichts über den Algorithmus aussagt, muss das PEM-Label es tun. Für Schlüssel auf elliptischen Kurven gibt es keine PKCS#1-Form (das analoge OpenSSL-Format BEGIN EC PRIVATE KEY stammt aus SEC 1, RFC 5915).

PKCS#8 verpackt einen algorithmusspezifischen Schlüssel in eine PrivateKeyInfo: eine Version, einen AlgorithmIdentifier und einen OCTET STRING mit dem inneren Schlüssel, der bei RSA genau die PKCS#1-Struktur ist. RFC 5958 (Version 2, OneAsymmetricKey) kann zusätzlich den öffentlichen Schlüssel transportieren. EncryptedPrivateKeyInfo ergänzt einen Verschlüsselungsalgorithmus und das Chiffrat; OpenSSL 3.6 verwendet PBES2 mit PBKDF2-HMAC-SHA256 und AES-256-CBC, wenn du Verschlüsselung anforderst.

Konvertieren

Das folgende Skript, ausgeführt mit OpenSSL 3.6.5, zeigt die Voreinstellungen und eine verlustfreie Konvertierung hin und zurück.

OpenSSL 3.6: PKCS#8, PKCS#1 und verschlüsseltes PKCS#8
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out pkcs8.pem 2>/dev/null
openssl pkey -in pkcs8.pem -traditional -out pkcs1.pem
openssl pkey -in pkcs8.pem -aes256 -passout pass:demo -out enc.pem
head -1 pkcs8.pem; head -1 pkcs1.pem; head -1 enc.pem
openssl pkcs8 -topk8 -nocrypt -in pkcs1.pem -out back.pem && head -1 back.pem
cmp -s pkcs8.pem back.pem && echo "round trip PKCS#1 -> PKCS#8: identical"
cd / && rm -rf "$dir"
Ausgabe
-----BEGIN PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
-----BEGIN ENCRYPTED PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
round trip PKCS#1 -> PKCS#8: identical

Dieselben Befehle konvertieren EC-Schlüssel nach SEC 1 (BEGIN EC PRIVATE KEY) in PKCS#8. Private OpenSSH-Schlüssel (BEGIN OPENSSH PRIVATE KEY) sind ein drittes Format; ssh-keygen -p -m PKCS8 -f key schreibt einen solchen Schlüssel direkt in seiner Datei als PKCS#8 um.

Fehler, die dir begegnen

Java liest am strengsten: PKCS8EncodedKeySpec akzeptiert nur PKCS#8, und der Wortlaut der Fehlermeldung hat sich zwischen den Releases geändert. Das folgende Programm lädt je eine PEM-Datei jeder Art, und die Befehle danach führen es mit Java 17, 21 und 25 in den offiziellen Images von Eclipse Temurin aus.

LoadKey.java: private Schlüssel aus PEM mit PKCS8EncodedKeySpec laden
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class LoadKey {
  public static void main(String[] args) throws Exception {
    System.out.println("== Java " + Runtime.version());
    for (String file : args) {
      String pem = Files.readString(Path.of(file));
      byte[] der = Base64.getMimeDecoder().decode(pem.replaceAll("-----[A-Z ]+-----", ""));
      try {
        KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(der));
        System.out.println(file + ": loaded");
      } catch (Exception e) {
        System.out.println(file + ": " + e);
        for (Throwable t = e.getCause(); t != null; t = t.getCause()) System.out.println("  Caused by: " + t);
      }
    }
  }
}
Unter Java 17, 21 und 25 mit einem PKCS#8- und einem PKCS#1-Schlüssel ausführen
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out pkcs8.pem 2>/dev/null
openssl pkey -in pkcs8.pem -traditional -out pkcs1.pem
for v in 17.0.20.1_1 21.0.12.1_1 25.0.4.1_1; do
  docker run --rm -v "$PWD":/work:ro -w /work "eclipse-temurin:$v-jdk-noble" java LoadKey.java pkcs8.pem pkcs1.pem
done
Ausgabe (20. Oktober 2026)
== Java 17.0.20.1+1
pkcs8.pem: loaded
pkcs1.pem: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
  Caused by: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
== Java 21.0.12.1+1-LTS
pkcs8.pem: loaded
pkcs1.pem: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.io.IOException: algid parse error, not a sequence
== Java 25.0.4.1+1-LTS
pkcs8.pem: loaded
pkcs1.pem: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.io.IOException: algid parse error, not a sequence

Ältere Antworten im Netz zitieren den Text von Java 17; unter Java 21 und 25 lautet die Meldung auf oberster Ebene Unable to decode key, und „algid parse error, not a sequence“ steht nur noch in der Ursache. Durchsuche Logs nach beiden Texten.

Die anderen Laufzeitumgebungen, am selben Tag mit von OpenSSL 3.6.5 erzeugten Schlüsseln nachgestellt:

  • Go: x509.ParsePKCS8PrivateKey gibt x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format) zurück (Go 1.25) und sagt damit genau, was passiert ist.
  • Node.js 26 (createPrivateKey, OpenSSL 3.6.5): PKCS#1-DER, das als type: "pkcs8" deklariert ist, scheitert mit error:068000A8:asn1 encoding routines::wrong tag (Code ERR_OSSL_ASN1_WRONG_TAG); ein PKCS#1-Inhalt unter einem Label BEGIN PRIVATE KEY scheitert mit error:1E08010C:DECODER routines::unsupported; dieselben PKCS#1-Bytes ergeben, als spki an createPublicKey übergeben, Failed to read asymmetric key. Ein verschlüsselter Schlüssel, der ohne Passphrase geladen wird, scheitert mit error:07880109:common libcrypto routines::interrupted or cancelled.
  • Mit veralteten Algorithmen verschlüsselte Schlüssel: Ein traditioneller PEM-Schlüssel, der mit DES verschlüsselt ist, oder ein PKCS#8-Schlüssel unter PBES1 mit MD5 und DES lässt sich in OpenSSL 3 nur mit dem Legacy-Provider laden. Ohne ihn gibt openssl pkey die Meldung error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported aus, gefolgt von dem Algorithmus, den es nicht abrufen konnte (DES-CBC oder bei PBES1 PBKDF1), und Node.js meldet error:0308010C:digital envelope routines::unsupported (ERR_OSSL_EVP_UNSUPPORTED).
  • Python (cryptography 46): load_pem_private_key liest PKCS#1 und PKCS#8 gleichermaßen und löst bei einem verschlüsselten Schlüssel ohne Passwort TypeError: Password was not given but private key is encrypted aus.
  • WebCrypto: importKey("pkcs8", …) mit PKCS#1-Bytes wird mit einem DataError abgelehnt; Node.js 26 formuliert ihn als Invalid keyData, Browser verwenden eigene Texte.

Im Fehlerkatalog gibt es zu jedem dieser Fehler eine eigene Seite; die Links stehen unten.

Dieselbe Zweiteilung bei öffentlichen Schlüsseln

Öffentliche Schlüssel gibt es in denselben zwei Formen. PKCS#1 RSAPublicKey (BEGIN RSA PUBLIC KEY) enthält nur Modul und Exponent; SubjectPublicKeyInfo (BEGIN PUBLIC KEY, RFC 5280) verpackt den Schlüssel mit einem Algorithmusbezeichner, funktioniert für jeden Algorithmus und wird von Zertifikaten, JWK-Konvertern, X509EncodedKeySpec in Java, ParsePKIXPublicKey in Go und dem Import im Format spki bei WebCrypto erwartet. openssl rsa -pubin -in key.pem -RSAPublicKey_out und openssl pkey -pubin -in key.pem -pubout konvertieren zwischen beiden Formen. Die Verwechslung gibt es in beide Richtungen: Tools, die SPKI schreiben, benennen die Datei oft nach dem Muster id_rsa.pub, und die einzeiligen öffentlichen Schlüssel von OpenSSH selbst (ssh-rsa AAAA…) sind noch ein weiteres Format, das sich mit ssh-keygen -e -m PKCS8 konvertieren lässt.

Wann welches Format passt

  • Neue Schlüssel, beliebiger Algorithmus: PKCS#8 (verschlüsselt auf dem Datenträger, unverschlüsselt in einem Secrets-Manager, der selbst schon verschlüsselt).
  • Java, Go, .NET, WebCrypto, Importe in Cloud-KMS: PKCS#8 (DER oder PEM).
  • Alte Software, die nur RSA PRIVATE KEY liest (manche Appliances, älterer Ruby- und PHP-Code, alte SSH-Tools): PKCS#1 über -traditional, idealerweise erst bei der Übergabe an diese Software.
  • Schlüssel plus Zertifikat für Windows, macOS oder Java-Keystores: PKCS#12, das intern PKCS#8-Schlüssel enthält.
  • SSH-Schlüssel: das OpenSSH-Format für ssh selbst; PKCS#8, wenn ein anderes Tool denselben Schlüssel braucht.

Hinweise zur Migration

  1. Finde Dateien in PKCS#1 und SEC 1: mit grep -l "BEGIN RSA PRIVATE KEY\|BEGIN EC PRIVATE KEY" in Konfigurations-Repositorys und Secret Stores.
  2. Konvertiere einmalig mit openssl pkcs8 -topk8 -nocrypt (oder -v2 aes-256-cbc zum Verschlüsseln) und passe die Programme an, die die Schlüssel lesen; das Schlüsselmaterial und das zugehörige Zertifikat ändern sich nicht.
  3. Verschlüssele alte verschlüsselte Schlüssel neu: Die traditionelle PEM-Verschlüsselung (Header Proc-Type und DEK-Info) leitet ihren Schlüssel mit einer einzigen MD5-Iteration ab, und mit DES oder RC2 verschlüsselte Schlüssel brauchen in beiden Formaten den Legacy-Provider von OpenSSL; verschlüssele sie mit PBES2 und AES-256 neu.
  4. Stelle den Code auf die generischen Parser um (ParsePKCS8PrivateKey, load_pem_private_key, createPrivateKey), damit neue Algorithmen, darunter Ed25519 und ML-DSA, ohne Codeänderungen funktionieren.
  5. Prüfe das Ergebnis: Signiere mit dem konvertierten Schlüssel und prüfe die Signatur mit dem bestehenden Zertifikat; der Schlüsselgenerator und der ASN.1-Explorer zeigen die Struktur, ohne etwas hochzuladen.

Quellen

Alle Vergleiche