PKCS#8 private key format

PKCS#8 is the standard private key format, recognisable from its `-----BEGIN PRIVATE KEY-----` header. It wraps the raw key material in a structure that also records an algorithm identifier, so a single parser can read RSA, EC, Ed25519 and future key types alike. The older PKCS#1 format (`BEGIN RSA PRIVATE KEY`) has no such field and is RSA-only. Java, Go and most modern libraries accept PKCS#8 exclusively, which makes converting to it the most common key-format task there is.

Updated August 19, 2026 Verified against OpenSSL 3.5.7

The structure #

A PKCS#8 key is two fields: an algorithm identifier and the key material itself.

See both
openssl asn1parse -in private.key
Expected output
0:d=0  hl=4 l= 630 cons: SEQUENCE
  4:d=1  hl=2 l=   1 prim: INTEGER           :00
  7:d=1  hl=2 l=  13 cons: SEQUENCE
  9:d=2  hl=2 l=   9 prim: OBJECT            :rsaEncryption
 20:d=2  hl=2 l=   0 prim: NULL
 22:d=1  hl=4 l= 608 prim: OCTET STRING

The OBJECT :rsaEncryption line is the part PKCS#1 does not have. It is what lets a parser know what is in the OCTET STRING without being told.

Compared with the older formats #

PKCS#1 SEC1 PKCS#8
PEM header BEGIN RSA PRIVATE KEY BEGIN EC PRIVATE KEY BEGIN PRIVATE KEY
Algorithms RSA only EC only Any
Records its own algorithm No No Yes
Standard encryption Proc-Type header, weak Proc-Type header, weak PBES2 / PBKDF2
Accepted by Java, Go No No Yes

Converting to PKCS#8 #

From PKCS#1, unencrypted output
openssl pkcs8 -topk8 -in pkcs1.key -out pkcs8.key -nocrypt

Full detail at convert PKCS#1 to PKCS#8.

Encrypted PKCS#8 #

The encrypted variant uses PBES2 — PBKDF2 to stretch the passphrase, then a real cipher:

Encrypt with AES-256 rather than the default
openssl pkcs8 -topk8 -in plain.key -out encrypted.key -v2 aes-256-cbc
Inspect what a file was encrypted with
openssl asn1parse -in encrypted.key | head -n 12

Generating PKCS#8 directly #

openssl genpkey writes PKCS#8 natively, so there is nothing to convert:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.key
EC key, same format
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.key

This is why genpkey is preferred over the older genrsa — see generate a private key.

Frequently asked questions

How do I recognise a PKCS#8 key?
The header reads -----BEGIN PRIVATE KEY----- when unencrypted, or -----BEGIN ENCRYPTED PRIVATE KEY----- when it has a passphrase. PKCS#1 keys say BEGIN RSA PRIVATE KEY instead.
Why do Java and Go only accept PKCS#8?
Because it is self-describing. The algorithm identifier inside the structure means one code path loads any key type, rather than requiring the caller to know in advance whether they are holding RSA or EC material.
Is PKCS#8 more secure than PKCS#1?
Not inherently — an unencrypted PKCS#8 key is exactly as exposed as an unencrypted PKCS#1 one. What PKCS#8 adds is a standard way to encrypt the key with a modern KDF and cipher, which PKCS#1 handles through an older and weaker header-based mechanism.
Can PKCS#8 hold an EC key?
Yes, and Ed25519, and anything else with a registered algorithm identifier. That generality is the whole reason it replaced the per-algorithm formats.

References