Certificates
Certificate formats explained: PEM, DER, PFX, P7B and JKS
Certificate files differ in two ways: the encoding (binary DER or Base64 PEM text) and the container (one certificate, a PKCS#7 bundle, a password-protected PKCS#12 archive with the key, or a Java keystore). Nginx, Apache and most Linux software want PEM; Windows and IIS want PKCS#12 (.pfx); Java accepts PKCS#12 or JKS. File extensions are only a hint.
Encoding versus container
The certificate itself is always the same X.509 structure defined in RFC 5280; the formats only change how it is written down and what travels with it. The following table covers the five formats you will meet in practice.
| Format | Encoding | Can contain | Private key? | Usual extensions | Specification |
|---|---|---|---|---|---|
| DER | Binary ASN.1 | One certificate, key or CRL | Only as a separate file | .der, .cer, .crt | ITU-T X.690, RFC 5280 |
| PEM | Base64 text with header lines | Any number of certificates, keys, CSRs or CRLs | Yes, as its own block | .pem, .crt, .cer, .key | RFC 7468 |
| PKCS#7 | DER or PEM | Certificates and CRLs | No | .p7b, .p7c | RFC 2315 |
| PKCS#12 | Binary | Key, certificate and chain, encrypted with a password | Yes | .pfx, .p12 | RFC 7292 |
| JKS | Binary, Java-specific | Keys and certificates in a keystore | Yes | .jks, .keystore | Java documentation |
The relationship is easier to see as layers:
X.509 certificate (ASN.1 structure, RFC 5280)
|
+-- written as binary DER .............................. .der / .cer
|
+-- Base64 + "-----BEGIN CERTIFICATE-----" ........ PEM (.pem / .crt)
+-- several certificates in one envelope .......... PKCS#7 (.p7b)
+-- certificates + private key, password-protected PKCS#12 (.pfx / .p12)
PEM: the text format most servers use
PEM is DER encoded in Base64, wrapped at 64 characters per line, between a BEGIN line and an END line that name the content. RFC 7468 standardized the labels that had been in use for decades:
| Label | Content |
|---|---|
CERTIFICATE |
An X.509 certificate |
PRIVATE KEY |
An unencrypted PKCS#8 private key (any algorithm) |
ENCRYPTED PRIVATE KEY |
A password-protected PKCS#8 private key |
RSA PRIVATE KEY / EC PRIVATE KEY |
Older algorithm-specific key formats (PKCS#1, SEC 1) |
CERTIFICATE REQUEST |
A CSR |
X509 CRL |
A certificate revocation list |
PKCS7 |
A PKCS#7 bundle in PEM form |
Since OpenSSL 3.0, openssl genrsa writes -----BEGIN PRIVATE KEY----- (PKCS#8) by default instead of the older RSA PRIVATE KEY; both work in Nginx and Apache.
Because PEM is text, you can concatenate blocks. A fullchain.pem is the leaf certificate followed by its intermediates, and HAProxy expects the leaf, the intermediates and the key in one file. Common PEM problems are Windows line endings, a byte-order mark added by an editor, missing line breaks between blocks after a copy-paste, and an -----END line cut off. Text outside the blocks, such as the Bag Attributes lines that openssl pkcs12 writes, is ignored by OpenSSL-based servers.
DER: the binary form
DER is the canonical binary encoding of the ASN.1 structure. A DER certificate almost always starts with the bytes 30 82, an ASN.1 SEQUENCE with a two-byte length. Windows certificate exports offer both "DER encoded binary X.509 (.CER)" and "Base-64 encoded X.509 (.CER)", so two files with the same .cer extension can be in different formats. Java's keytool -exportcert also writes DER unless you add -rfc.
# DER to PEM, and back
openssl x509 -inform der -in certificate.cer -out certificate.pem
openssl x509 -in certificate.pem -outform der -out certificate.der
PKCS#7 (.p7b): certificates without the key
A PKCS#7 bundle holds certificates (and optionally CRLs) inside a signed-data envelope with no signature and no key. Some CAs deliver the intermediate chain this way, and Windows and Java can import it in one step. Because a .p7b never contains a private key, it cannot be used on its own to configure a server.
# Extract all certificates from a P7B as PEM
openssl pkcs7 -in chain.p7b -print_certs -out chain.pem
# Build a P7B from a PEM chain
openssl crl2pkcs7 -nocrl -certfile fullchain.pem -out chain.p7b
PKCS#12 (.pfx/.p12): key and chain in one encrypted file
PKCS#12 is the format for moving a private key together with its certificate and chain. It stores each item in a "bag", encrypts the key (and usually the certificates) with a password-derived key, and protects the whole file with a MAC. Windows, IIS, Azure and many Java and macOS tools import it directly. The name PFX comes from Microsoft's earlier Personal Information Exchange format, which PKCS#12 replaced.
The OpenSSL 3.0 default change
OpenSSL 3.0 changed how openssl pkcs12 -export protects new files. Older versions used RC2-40 or 3DES with SHA-1, algorithms that OpenSSL 3.0 moved into its legacy provider. With OpenSSL 3.x, the default looks like this:
openssl pkcs12 -export -inkey example.com.key -in example.com.crt \
-certfile intermediate.pem -out example.com.pfx
openssl pkcs12 -info -in example.com.pfx -noout
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 16
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
Certificate bag
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
This causes problems in two directions:
- Old importers cannot read new files. Older Windows Server releases, older Java 8 updates and older macOS Keychain versions may reject an AES-256 PKCS#12 file, often with a misleading "wrong password" error. Create the file with
-legacy, which uses 3DES for the key, RC2 for the certificates and a SHA-1 MAC. - OpenSSL 3 cannot read old files without help. Opening a PFX exported years ago by Windows or OpenSSL 1.x fails with an
unsupportederror namingRC2-40-CBC. Add-legacyto the command to load the legacy provider.
Error outputting keys and certificates
...:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:...
Algorithm (RC2-40-CBC : 0), Properties ()
-nodes is deprecated since OpenSSL 3.0; the new spelling for "do not encrypt the extracted key" is -noenc. The PKCS#12 cookbook has ready-to-use commands for both directions.
JKS and Java keystores
JKS is a proprietary Java keystore format with weak integrity protection, and modern Java no longer creates it by default. Since JDK 9 (JEP 229) the default keystore type is PKCS12, which means a .p12 file made with OpenSSL can be used as a Java keystore directly. To convert an existing JKS:
keytool -importkeystore -srckeystore server.jks -srcstoretype jks \
-destkeystore server.p12 -deststoretype pkcs12
keytool -list -v -keystore server.p12
keytool prints a warning recommending migration to PKCS12 whenever it opens a JKS file. Older servlet containers and some appliances still require JKS; for everything else, prefer PKCS12.
Private key formats
The private key has its own format choices, and a key in the wrong one is a common reason a server refuses to start. Three PEM variants are in everyday use:
| Header | Structure | Notes |
|---|---|---|
BEGIN PRIVATE KEY |
PKCS#8, unencrypted | Default output of OpenSSL 3.x for all key types |
BEGIN ENCRYPTED PRIVATE KEY |
PKCS#8, password-protected | The server needs the password at startup |
BEGIN RSA PRIVATE KEY / BEGIN EC PRIVATE KEY |
PKCS#1 / SEC 1 | Older OpenSSL output; still widely accepted |
openssl pkey converts between them without changing the key itself:
# Any supported key to unencrypted PKCS#8
openssl pkey -in old.key -out new.key
# Show the algorithm and size
openssl pkey -in new.key -noout -text | head -1
Web servers such as Nginx can read an encrypted key only if you provide the password through ssl_password_file, so most deployments keep the key unencrypted and protect it with file permissions (chmod 600, owned by root). Treat a PKCS#12 file the same way: the password protects it in transit, but anyone with the file and the password has your key. Send the password through a different channel from the file, and do not upload private keys or PFX files to websites that process them on a server.
How to identify an unknown file
Look at the first bytes, then ask OpenSSL to parse the file in the format you suspect. Extensions are not reliable.
| What you see | Format | Try |
|---|---|---|
-----BEGIN CERTIFICATE----- |
PEM certificate | openssl x509 -in f -noout -text |
-----BEGIN PKCS7----- |
PEM PKCS#7 | openssl pkcs7 -in f -print_certs |
-----BEGIN PRIVATE KEY----- |
PEM PKCS#8 key | openssl pkey -in f -noout -text |
-----BEGIN CERTIFICATE REQUEST----- |
PEM CSR | openssl req -in f -noout -text |
Binary, starts with 30 82 |
DER certificate, PKCS#12 or DER PKCS#7 | try x509 -inform der, then pkcs12 -info, then pkcs7 -inform der |
The file command on Linux and macOS recognizes some of these:
file leaf.der site.pfx chain.p7b leaf.pem
leaf.der: Certificate, Version=3
site.pfx: data
chain.p7b: ASCII text
leaf.pem: PEM certificate
A PKCS#12 file shows up as plain data, so if a binary file is not a DER certificate, try openssl pkcs12 -info -in file -noout next. The certificate decoder does the same detection in the browser for public certificates.
Converting between formats
Most conversions are one OpenSSL command, and the private key never needs to leave your machine. A quick reference:
| From | To | Command |
|---|---|---|
| DER | PEM | openssl x509 -inform der -in cert.der -out cert.pem |
| PEM | DER | openssl x509 -in cert.pem -outform der -out cert.der |
| P7B | PEM | openssl pkcs7 -in chain.p7b -print_certs -out chain.pem |
| PEM | P7B | openssl crl2pkcs7 -nocrl -certfile fullchain.pem -out chain.p7b |
| PEM + key | PFX | openssl pkcs12 -export -inkey key.pem -in fullchain.pem -out site.pfx |
| PFX | PEM | openssl pkcs12 -in site.pfx -out all.pem -noenc |
| JKS | PKCS12 | keytool -importkeystore -srckeystore a.jks -destkeystore a.p12 -deststoretype pkcs12 |
The format conversion cookbook adds output examples and edge cases, and the SSL converter handles common conversions in the browser. After any conversion, confirm that the key still matches the certificate with the key matcher; a mismatch is what Nginx reports as key values mismatch.
What each server expects
Choose the format by the software that will load it. The table lists common targets.
| Platform | Certificate | Private key | Chain |
|---|---|---|---|
| Nginx | PEM (ssl_certificate) |
PEM (ssl_certificate_key) |
In the certificate file, after the leaf |
| Apache 2.4.8+ | PEM (SSLCertificateFile) |
PEM (SSLCertificateKeyFile) |
In the certificate file; SSLCertificateChainFile is deprecated |
| HAProxy | One PEM file | Same file | Same file |
| IIS / Windows | PKCS#12 (.pfx) import | Inside the PFX | Inside the PFX |
| Tomcat | PKCS#12 or JKS keystore; recent versions also accept PEM files | In the keystore or PEM | In the keystore or PEM |
| Node.js | PEM strings (cert, key) or a PFX (pfx) |
PEM or PFX | Appended to cert |
| Cloud load balancers | Usually PEM body, chain and key pasted separately | PEM | Separate field |
When a CA or hosting panel lets you download a ZIP "for Nginx", "for IIS" or "for Tomcat", it is giving you the same certificate in these different containers. Whatever you download, the chain must be complete: see certificate chains for why the intermediates matter.
FAQ
Is a .crt file the same as a .pem file?
Often, but not always. Both extensions are commonly used for PEM text files, yet some CAs and Windows tools save binary DER certificates as .crt or .cer. Open the file in a text editor: if it starts with -----BEGIN CERTIFICATE----- it is PEM, otherwise it is probably DER.
Why does Windows or Java reject my .pfx password when I know it is correct?
The file was probably created with OpenSSL 3.x, which encrypts PKCS#12 files with AES-256 and PBKDF2 by default. Some older importers cannot read that and report a wrong password. Re-create the file with openssl pkcs12 -export -legacy ... (3DES for the key, RC2 for the certificates, SHA-1 MAC).
Does a .p7b file contain my private key?
No. PKCS#7 bundles carry certificates and CRLs only. If you need to move a key, use PKCS#12 (.pfx/.p12) or a separate PEM key file.
Which format does Nginx need?
PEM. ssl_certificate points to a PEM file with the leaf certificate followed by the intermediates, and ssl_certificate_key points to the PEM private key. If you only have a .pfx, extract both with openssl pkcs12.
Sources
- RFC 7468: Textual Encodings of PKIX, PKCS, and CMS Structures (PEM)
- RFC 7292: PKCS #12 Personal Information Exchange Syntax v1.1
- RFC 2315: PKCS #7 Cryptographic Message Syntax v1.5
- RFC 5280: Internet X.509 PKI Certificate and CRL Profile
- OpenSSL 3.0 manual: openssl-pkcs12
- OpenSSL 3.0 migration guide
- OpenJDK JEP 229: Create PKCS12 Keystores by Default