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.
The structure #
A PKCS#8 key is two fields: an algorithm identifier and the key material itself.
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 #
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:
openssl pkcs8 -topk8 -in plain.key -out encrypted.key -v2 aes-256-cbc 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 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 sayBEGIN RSA PRIVATE KEYinstead. - 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
- RFC 5958: asymmetric key packages — PKCS#8, the self-describing private key container
- RFC 8017: PKCS #1 RSA cryptography — the RSA-only key format that PKCS#8 generalised