PEM takes its name from Privacy-Enhanced Mail (RFC 1421, 1993), whose message format
it borrowed. The form used today for keys and certificates is defined in RFC 7468,
Textual Encodings of PKIX, PKCS, and CMS Structures (2015): a label between
two boundary lines, and base64 of the DER data between
them. Sources: RFC 7468 for the rules and standard labels, OpenSSL’s
include/openssl/pem.h and manual pages for the other labels, and RFC 1421
with OpenSSL’s PEM_read_bio_PrivateKey(3) page for the legacy headers.
Encoding rules
-----BEGIN CERTIFICATE-----
MII… (base64 of the DER certificate, 64 characters per line)
…
-----END CERTIFICATE-----
From RFC 7468 sections 2 and 3:
- The first line is five hyphens,
BEGIN, one space, the label and five hyphens; the last line the same withEND. Writers must use the same label in both; readers may disregard a mismatch. - Labels are formally case-sensitive and upper case, may contain spaces and hyphens, but not two in a row, and do not start or end with either.
- Between the lines is base64 (RFC 4648, section 4) of the data. Writers break it into lines of exactly 64 characters, the last line shorter, and add no other whitespace. Readers should ignore whitespace, must handle any line ending (LF, CR LF or CR) and may handle other line lengths.
- The data is BER; for most labels DER is preferred or strongly preferred (sections 5 to 13).
- Data before the BEGIN line is allowed, and parsers must not fail on it; section 5.2 describes explanatory text around certificates. A file may hold several blocks, as when it holds several certificates.
- RFC 7468 does not allow header lines inside the block; see legacy headers for the older form that has them.
Standard labels
The labels of RFC 7468 (summary table in section 4, details in sections 5 to 13):
| Label | Contents (ASN.1 type) | Defined in |
|---|---|---|
CERTIFICATE | An X.509 certificate (Certificate) | RFC 5280 |
X509 CRL | A certificate revocation list (CertificateList) | RFC 5280 |
CERTIFICATE REQUEST | A PKCS #10 certificate signing request (CertificationRequest) | RFC 2986 |
PKCS7 | A PKCS #7 message (ContentInfo); “long been superseded by CMS”, and writers should not use it when CMS will do | RFC 2315 |
CMS | A CMS message (ContentInfo) | RFC 5652 |
PRIVATE KEY | A PKCS #8 private key of any algorithm (PrivateKeyInfo or OneAsymmetricKey) | RFC 5208, RFC 5958 |
ENCRYPTED PRIVATE KEY | A password-encrypted PKCS #8 key (EncryptedPrivateKeyInfo) | RFC 5208, RFC 5958 |
ATTRIBUTE CERTIFICATE | An attribute certificate (AttributeCertificate) | RFC 5755 |
PUBLIC KEY | A public key of any algorithm (SubjectPublicKeyInfo) | RFC 5280 |
RFC 7468 also names labels in other use that conforming writers must not produce:
| Label | Status |
|---|---|
X509 CERTIFICATE, X.509 CERTIFICATE | Historical names for a certificate; must not be written. Readers should not treat them as CERTIFICATE, except perhaps for backwards compatibility (section 5.1). |
CRL | Rarely used; must not be written, and readers should not treat it as X509 CRL (section 6). |
NEW CERTIFICATE REQUEST | In wide use; writers must write CERTIFICATE REQUEST, and readers may treat the two as the same (section 7). |
CERTIFICATE CHAIN | A degenerate PKCS #7 structure holding only certificates; must not be written, and readers should not treat it as PKCS7 (section 8). |
Other labels in use
Software uses labels that RFC 7468 does not define. OpenSSL lists the labels it
knows as PEM_STRING_* constants in include/openssl/pem.h;
besides the RFC 7468 ones, the old X509 CERTIFICATE and
NEW CERTIFICATE REQUEST, and two reading patterns described after the
table, they are:
| Label | Contents |
|---|---|
RSA PRIVATE KEY | An RSA key in the “traditional” PKCS #1 form (RSAPrivateKey, RFC 8017); openssl-rsa(1), option -traditional |
RSA PUBLIC KEY | An RSA public key as a PKCS #1 RSAPublicKey (PEM_read_bio_PrivateKey(3), RSAPublicKey functions) |
DSA PRIVATE KEY | A DSA key in OpenSSL’s “traditional” format (PEM_read_bio_PrivateKey(3), DSAPrivateKey functions) |
DSA PUBLIC KEY | A DSA public key; its contents are not described in the OpenSSL manual pages read for this page (unknown) |
DSA PARAMETERS | DSA parameters as a Dss-Parms structure of RFC 3370 (DSAparams functions) |
EC PRIVATE KEY | An elliptic-curve key (ECPrivateKey); RFC 5915 section 4 itself shows this label |
EC PARAMETERS | Elliptic-curve domain parameters: by default a named curve given by its OID, or explicit parameters (openssl-ecparam(1), option -param_enc) |
ECDSA PUBLIC KEY | Contents not described in the OpenSSL manual pages read for this page (unknown) |
DH PARAMETERS | Diffie–Hellman parameters as a PKCS #3 DHparameter structure (DHparams functions) |
X9.42 DH PARAMETERS | X9.42 Diffie–Hellman parameters; accepted when DH parameters are read (check_pem in crypto/pem/pem_lib.c) |
TRUSTED CERTIFICATE | A certificate with OpenSSL’s trust settings attached, such as permitted and prohibited uses and an alias (openssl-x509(1), “Trust Settings”; read with the X509_AUX functions) |
PKCS #7 SIGNED DATA | Read as PKCS7 (check_pem); contents otherwise not described |
SSL SESSION PARAMETERS | Named in pem.h; contents not described in the sources read (unknown) |
SM2 PRIVATE KEY, SM2 PARAMETERS | Named in pem.h; contents not described in the sources read (unknown) |
ECHCONFIG | Named in pem.h; contents not described in the sources read (unknown) |
Two more constants are not labels found in files but patterns for reading:
ANY PRIVATE KEY (PEM_STRING_EVP_PKEY) matches
PRIVATE KEY, ENCRYPTED PRIVATE KEY and any
type PRIVATE KEY label of a known key type, and
PARAMETERS matches any type PARAMETERS label of a
known type (check_pem in crypto/pem/pem_lib.c). The same
function lets readers accept X509 CERTIFICATE as
CERTIFICATE and NEW CERTIFICATE REQUEST as
CERTIFICATE REQUEST; OpenSSL’s X509_REQ_NEW write functions
still write NEW CERTIFICATE REQUEST “as required by some CAs”
(PEM_read_bio_PrivateKey(3)).
OpenSSH writes its own private keys as OPENSSH PRIVATE KEY
(MARK_BEGIN in OpenSSH’s sshkey.c), around a format that is not
ASN.1 and is described in OpenSSH’s PROTOCOL.key (see
OpenSSH keys).
Legacy headers
OpenSSL’s old “traditional” private key routines encrypt with a password by keeping
RFC 1421’s header lines after the BEGIN line, followed by a blank line
(PEM_read_bio_PrivateKey(3), “PEM ENCRYPTION FORMAT”):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,3F1C5A0E9B7D2C4486A1E0F37B9C2D51
(base64 of the encrypted PKCS #1 key)
-----END RSA PRIVATE KEY-----
In RFC 1421, Proc-Type (section 4.6.1.1) must be the first header; its
first subfield, 4, is the version of the PEM rules, and its second,
ENCRYPTED, says the text is encrypted. DEK-Info (section
4.6.1.3) is required for encrypted messages and names the encryption algorithm and
mode, followed by its parameters. In OpenSSL’s use, DEK-Info holds the
cipher name as known to EVP_get_cipherbyname() and the initialisation
vector in hex.
The key is derived from the password with OpenSSL’s EVP_BytesToKey,
using MD5 and an iteration count of 1, with the IV passed as the salt, of which
PKCS5_SALT_LEN (8) bytes are used (PEM_read_bio_PrivateKey(3);
EVP_md5() in crypto/pem/pem_lib.c;
PKCS5_SALT_LEN in include/openssl/evp.h).
EVP_BytesToKey(3) defines the derivation: Di =
HASHcount(Di−1 ‖ password ‖ salt), concatenated until there are
enough bytes, key first. The same manual page says new applications should write
PKCS #8 keys instead (ENCRYPTED PRIVATE KEY), which use PKCS #5 v2.0
encryption and an iteration count of 2048 rather than 1.