Learn / SSL formats

SSL File Formats

GUIDE

8 min read · 1,664 words

Same underlying data, different wrappers. Most confusion comes from the file extensions, which are used inconsistently.

FormatExtensionsNotes
PEM.pem .crt .cer .keyBase64 text with BEGIN/END lines. The usual format on Linux with Apache and Nginx.
DER.der .cerBinary form of the same data. Common with Java and some Windows tools.
PKCS#7 / P7B.p7b .p7cCertificates and chain only, no private key.
PKCS#12 / PFX.pfx .p12Certificate, chain and private key in one password-protected file. Common on Windows and IIS.
Private key.keyThe secret half of the pair. Never share it or email it.
CSR.csrRequest file you submit to a CA. Contains the public key and names.
Chain / bundleca-bundle.crtIntermediate certificates the server must present with yours.

Certificate files cause more confusion than the cryptography ever does. You are handed a zip containing example.com.crt, ca-bundle.crt and perhaps a .p7b; your server asks for a "PEM chain" or a "PFX"; and the extensions mean different things depending on who made the file.

The underlying data is the same in almost every case: a certificate, perhaps some intermediates, and a private key. What differs is the wrapper: text or binary, one item or several, with or without a password. Same underlying data, different wrappers. Most confusion comes from the file extensions, which are used inconsistently.

This guide shows how to identify what you have, how the pieces fit together, where each server wants them, and the OpenSSL commands that convert between forms. Nothing here needs more than a terminal and a few minutes.

The pieces: certificate, chain, key, request

Four things get mixed up. The private key is generated on your server (or by you) and never leaves it. The CSR, a certificate signing request, is built from the key and sent to the authority. The certificate comes back, signed, tying your public key to your names. The intermediates are the certificates of the authority's own signing keys, which link yours to a root that browsers already trust.

Only the key is secret. Everything else is public and can be pasted into support tickets without harm. The key is the one file you should not email, upload to a chat, or commit to a repository.

Root already in the browser (not sent) Intermediate sent in the chain Your certificate sent first signs signs Private key stays on the server The key pairs with your certificate, but is never part of the chain.
The server sends its own certificate followed by the intermediates; the root is already trusted and the key is never sent.

How to tell what you have

Open the file in a text editor. If it starts with -----BEGIN CERTIFICATE-----, it is PEM, whatever its extension says. If it is unreadable binary, it is probably DER or PFX. Extensions like .crt and .cer are used loosely by different vendors, so the contents matter more than the name.

The header line tells you what kind of item is inside:

A single PEM file can hold several blocks one after another. That is how a chain file works: it is just certificates concatenated. For binary files, ask OpenSSL. This tries the likely formats in turn and tells you which one parses:

openssl x509 -in file -noout -subject -dates              # PEM certificate
openssl x509 -inform der -in file -noout -subject -dates  # DER certificate
openssl pkcs12 -in file -info -noout                      # PFX / PKCS#12

The common formats side by side

NameLooks likeUsually containsTypical extensionsUsed by
PEMText with BEGIN and END linesOne or more certificates or keys.pem .crt .cer .keyApache, Nginx, most Linux tools
DERBinaryA single certificate.der .cerJava, some appliances
PKCS#12 / PFXBinary, usually password protectedCertificate, chain and key together.pfx .p12Windows, IIS, many import dialogs
PKCS#7Text or binaryCertificates only, no key.p7b .p7cWindows, Tomcat

The order matters

When a server needs a chain file, certificates go in order: yours first, then the intermediate that signed it, then any further intermediates. The root is normally omitted, since clients already have it. Putting them in the wrong order is a common reason a certificate appears to install but fails verification on some devices. Desktop browsers often forgive it or fetch a missing intermediate on their own; older phones, command line tools and API clients do not.

If your authority gives you the pieces separately, build the chain file yourself and check it:

cat example.com.crt intermediate.crt > fullchain.pem
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout

The second command lists the subject and issuer of every certificate in order. Each certificate's issuer should be the subject of the next one down.

Apache

Apache uses three settings: SSLCertificateFile for your certificate, SSLCertificateKeyFile for the private key, and SSLCertificateChainFile (or the chain included in the main file in newer versions) for intermediates. In Apache 2.4.8 and later you can put your certificate and the intermediates together in the file named by SSLCertificateFile, and the separate chain directive is deprecated.

<VirtualHost *:443>
    ServerName example.com
    SSLEngine on
    SSLCertificateFile    /etc/ssl/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
</VirtualHost>

Test the configuration with apachectl configtest before reloading, so a typo in a path does not take every site on the machine down.

Nginx

Nginx wants one file containing your certificate followed by the intermediates, referenced by ssl_certificate, and the key in ssl_certificate_key.

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;
}

Run nginx -t and then reload. A frequent mistake is pointing ssl_certificate at the lone certificate file, which works in some browsers and fails in others, because the intermediate is not sent.

Windows and IIS

These generally use PFX files. You import it into the certificate store and bind it to the site. The wrapper holds the key, so the file needs a password when you create it, and you will be asked for that password at import. Mark the key as non-exportable if you can, so it cannot later be copied off the machine. If you are given a certificate and a key as separate PEM files, build a PFX with the command in the next section first.

On shared hosting you rarely handle any of this: the control panel has a form where you paste the certificate, the key and the chain into separate boxes, in PEM form. The panel then writes the right files for the underlying server.

PEM (text) DER (binary) PFX (bundle) x509 -outform der x509 -inform der pkcs12 -export pkcs12 -nodes
PEM is the hub: most conversions go to or from it, and only PFX carries the key with the certificate.

Handy conversions with OpenSSL

PEM to DER:  openssl x509 -in cert.pem -outform der -out cert.der
DER to PEM:  openssl x509 -inform der -in cert.der -out cert.pem
PFX to PEM:  openssl pkcs12 -in cert.pfx -out all.pem -nodes
PEM to PFX:  openssl pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem -certfile chain.pem
P7B to PEM:  openssl pkcs7 -print_certs -in bundle.p7b -out certs.pem
Check a pair: compare the output of 'openssl x509 -noout -modulus -in cert.pem' with 'openssl rsa -noout -modulus -in key.pem'

The pair check is worth knowing. Both commands print a long hexadecimal value; pipe each through openssl md5 and compare the short hashes. If they differ, the certificate and key are not a pair. For elliptic curve keys, which have no modulus, compare the public key instead with openssl x509 -noout -pubkey and openssl pkey -pubout.

Note that -nodes writes the private key out unencrypted. Use it only to a location you control, and delete the file afterwards.

Treat private keys like passwords. If one is ever exposed, revoke the certificate and issue a new one with a fresh key.

Worked example: a zip from the authority, a server that wants one file

Say the authority sends example.com.crt, intermediate.crt and root.crt, and you generated the key and CSR on the server last week as example.com.key. You run Nginx. The steps:

  1. Confirm the files are PEM with head -1 on each. All three should start with -----BEGIN CERTIFICATE-----.
  2. Confirm the pair: the md5 of the modulus from example.com.crt and from example.com.key must be identical.
  3. Build the chain: cat example.com.crt intermediate.crt > fullchain.pem. Leave root.crt out.
  4. Copy fullchain.pem and the key to /etc/ssl/example.com/, then set the key to mode 600, owned by root.
  5. Edit the two Nginx directives, run nginx -t, then systemctl reload nginx.
  6. From another machine, run openssl s_client -connect example.com:443 -servername example.com and look for Verify return code: 0 (ok) at the end.

If the last line says return code 21 or 20, an intermediate is missing; if it says 10, the certificate has expired. Go back to step 3 and look at the order. The whole job takes about ten minutes, and the check in the last step is the one people skip. It takes ten seconds once you have done it once, and the unsatisfying truth is that most of the delay is waiting for the file to arrive.

Common errors

"Key values mismatch" means the certificate and private key are not a pair. This usually follows a reissue, when the new certificate belongs to a new key you generated and then lost, or a CSR was made on another machine. "Unable to get local issuer certificate" means the intermediate is missing. "Certificate has expired" is exactly what it sounds like, and it is worth checking whether the server is still presenting an older certificate than the one you just installed. A web server often needs a reload, not just a file replacement, to start serving the new one.

Two more that appear regularly: "PEM routines: no start line", which means the file is not PEM or has stray characters at the top (copy-paste from email often adds them), and "bad decrypt" or "mac verify failure", which means a wrong PFX password.

Questions that come up

Is .crt different from .pem?

Not by itself. It is only a name. Both are usually PEM text; look at the first line to be sure.

Do I need the root certificate?

Normally no. Clients already hold the roots they trust, and sending one wastes bytes without helping validation.

Why does my certificate work in Chrome but fail in curl?

Browsers can fetch a missing intermediate on their own. Tools like curl and many API clients cannot, so the chain file is incomplete. See the certificate types guide for background on what the authority signs.

Can I put the key and certificate in one file?

Some servers accept a combined PEM, and PFX always combines them. For Apache and Nginx, keep them separate; it makes permissions simpler.