Converting certificate formats with OpenSSL#
SSL/TLS certificates do not come in one format - depending on what system generated them, you end up with a mix of .pfx, .p7b, .pem, .crt, .key, .der. Nginx expects .pem/.crt + .key, IIS usually produces .pfx, some Windows CAs hand you a .p7b. This page covers converting between the three.
Know your formats first#
| Extension | Contains | Carries the private key |
|---|---|---|
.pem / .crt / .cer |
Base64 text, single certificate or chain | No (usually) |
.key |
Base64 text, private key | Yes - do not share this file with anyone |
.pfx / .p12 |
Binary, certificate + private key + chain together, password-protected | Yes |
.p7b / .p7c |
Binary or Base64, certificate(s) only - carries the chain but not the private key | No |
.der |
Binary, a single certificate or key (the binary form of PEM) | Depends on contents |
.pfx and .p7b look similar but this is the core difference: .pfx carries the private key too and is protected by a password, .p7b carries only the public certificate chain.
Extract the private key and certificate from a PFX#
The most common need when moving an IIS/Windows certificate onto a server that expects PEM, like nginx or Apache.
# Extract the private key
openssl pkcs12 -in certificate.pfx -nocerts -out private.key -nodes
# Extract the certificate
openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out certificate.crt
# Extract the intermediate chain (if present)
openssl pkcs12 -in certificate.pfx -cacerts -nokeys -chain -out ca-chain.crt
-nodes (no DES) extracts the private key unencrypted - needed so nginx can read the key file on every start without a passphrase prompt. Drop -nodes if you want to keep the key encrypted, but then you will need to enter the password on every nginx -s reload.
Don't forget permissions
Lock down private.key's permissions the moment you extract it:
chmod 600 private.key
chown root:root private.key
A world-readable private key ends the security of everything that certificate protects.
Convert P7B to PEM/PFX#
A P7B does not carry the private key - to convert it you also need your private key file separately (the one paired with the CSR you generated when you first requested the certificate).
# If the P7B is Base64/PEM (opens as text)
openssl pkcs7 -print_certs -in certificate.p7b -out certificate.pem
# If the P7B is binary/DER - openssl pkcs7 expects PEM by default, and a binary
# file without -inform DER fails with "no start line"
openssl pkcs7 -inform DER -print_certs -in certificate.p7b -out certificate.pem
# Package the PEM certificate plus your separate private key into a PFX
openssl pkcs12 -export -in certificate.pem -inkey private.key -out certificate.pfx -name "server"
If you don't know which format it is
file certificate.p7b usually tells you: "PEM certificate" or plain text starting with -----BEGIN means Base64, "data" or binary-looking characters means DER.
Package PEM into PFX#
If you have the certificate, chain and private key as separate files, bundle them all into one .pfx (for IIS/Windows):
openssl pkcs12 -export \
-out certificate.pfx \
-inkey private.key \
-in certificate.crt \
-certfile ca-chain.crt
The command will prompt for an "Export Password" - send that password to whoever is importing the .pfx over a secure channel, not email.
Does this certificate match this private key#
When you have lost track of which certificate belongs to which key, compare their public keys:
openssl x509 -pubkey -noout -in certificate.crt | openssl md5
openssl pkey -pubout -in private.key | openssl md5
If the two outputs (md5 hashes) are identical, the certificate and the key match.
Why not openssl rsa
openssl rsa -noout -modulus only works on RSA keys - it stops with "Not an RSA key" on an EC (ECDSA) private key, and most modern Let's Encrypt/ACME setups issue ECDSA by default now. openssl pkey -pubout works for either key type, which is why it's the one to reach for by default. If you know you're on RSA, openssl rsa -noout -modulus gives the same result.
With Morpheus
"extract the private key and certificate from this .pfx and put them in the format nginx expects"
Morpheus extracts all three files and places them with the right permissions - but it will ask you for the .pfx password rather than guessing at one hidden in chat.
Checklist#
- [ ] The extracted
private.keywas locked down withchmod 600 - [ ] The certificate and private key's modulus match
- [ ] The intermediate chain (if any) was included - a missing chain causes "untrusted certificate" errors on some clients
- [ ] The
.pfxpassword was sent over a secure channel