// Engineering Log

HTTPS and Web Servers in Practice: Part 2 — Certificate Formats PEM, DER, PFX, and CSR: How They Differ and How to Convert

Published on 2026-10-30 · Author:

// Fast route

This article belongs to the topic Security and protection.

The certificate authority sent an archive with .crt, .ca-bundle and .p7b files, and the server asks for .pfx. Or the other way around: someone exported a .pfx from Windows, and Nginx needs the certificate and key separately. All these files can contain the same entities — certificate, private key, intermediate certificates — they differ only by packaging. Below — how to find out what’s inside a file, how to convert it to the needed format, and how not to make a mistake when assembling the chain. How a certificate and the trust chain work — in the article “What is HTTPS and how the TLS handshake works”.

What’s inside

  • Certificate — the public key with owner data, signed by the CA. Not a secret; the server gives it to every client.
  • Private key — secret. Whoever holds it can impersonate your site.
  • Intermediate certificates (chain, bundle, CA bundle) — links between your certificate and the CA root.
  • Certificate Signing Request (CSR) — the public key and names you send to the CA to get a certificate.

Formats

PEM — Base64 text between marker lines. The most common format on Linux: used by Nginx, Apache, HAProxy, mail servers. One file can contain several blocks in sequence.

-----BEGIN CERTIFICATE-----
MIIFBTCCA+2gAwIBAgISA...
-----END CERTIFICATE-----

Extensions — .pem, .crt, .cer, .key, .ca-bundle. An extension guarantees nothing: .crt can be either PEM or DER. The content type is determined by the marker line:

MarkerWhat’s inside
BEGIN CERTIFICATEcertificate
BEGIN CERTIFICATE REQUESTCSR
BEGIN PRIVATE KEYprivate key in PKCS#8 format (any algorithm)
BEGIN ENCRYPTED PRIVATE KEYPKCS#8 key encrypted with a password
BEGIN RSA PRIVATE KEYRSA key in the older PKCS#1 format
BEGIN EC PRIVATE KEYECDSA key in the older format
BEGIN PKCS7PKCS#7 container with a chain

DER — the same certificate in binary form, without Base64. Extensions .der, .cer. Common in Windows and Java. In a text editor it looks like garbage.

PKCS#7 (.p7b, .p7c) — a container with one or more certificates, usually a chain. It does not include a private key. Many commercial CAs and Windows export the chain this way.

PKCS#12 (.pfx, .p12) — a container with everything at once: certificate, private key and chain, protected by a password. Used in Windows (IIS, Exchange, RDP), in Java, and for importing client certificates into browsers and phones.

JKS — Java KeyStore. Since Java 9 PKCS#12 is used by default, and JKS appears only in legacy systems.

How to tell what’s in a file

bash
head -1 file.crt                                   # PEM — the BEGIN line is visible
openssl x509 -in file.crt -noout -subject -issuer -dates         # certificate PEM
openssl x509 -in file.cer -inform der -noout -subject -dates     # certificate DER
openssl req -in file.csr -noout -text -verify                    # CSR
openssl pkey -in file.key -noout -text | head -1                 # key: algorithm and size
openssl pkcs12 -in file.pfx -info -noout                         # PFX (will ask for password)
openssl pkcs7 -in file.p7b -print_certs -noout                   # P7B

How many certificates are in a PEM file and which ones:

bash
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout

Conversion

DER → PEM and back:

bash
openssl x509 -in cert.der -inform der -out cert.pem
openssl x509 -in cert.pem -outform der -out cert.der

P7B → PEM (get the chain as regular certificates):

bash
openssl pkcs7 -in chain.p7b -print_certs -out chain.pem

The output includes lines subject= and issuer= before each certificate — Nginx ignores them, but you can remove them for tidiness.

PFX → PEM (from Windows to Linux):

bash
openssl pkcs12 -in site.pfx -clcerts -nokeys -out cert.pem          # site certificate
openssl pkcs12 -in site.pfx -cacerts -nokeys -out chain.pem         # intermediates
openssl pkcs12 -in site.pfx -nocerts -nodes -out privkey.pem        # key without password

-nodes keeps the key unencrypted — otherwise Nginx will ask for the password on every start. Immediately restrict permissions: chmod 600 privkey.pem.

If OpenSSL 3 replies unsupported algorithm (RC2-40-CBC) or Algorithm (RC2-40-CBC : 0), Properties (), the file is encrypted with legacy algorithms that are disabled by default. Add -legacy:

bash
openssl pkcs12 -legacy -in site.pfx -nocerts -nodes -out privkey.pem

PEM → PFX (from Linux to Windows or Java):

bash
openssl pkcs12 -export -out site.pfx \
  -inkey privkey.pem -in cert.pem -certfile chain.pem \
  -name "example.ru"

OpenSSL 3 encrypts PFX with AES-256. Older systems (Windows Server 2016 and earlier, some Java versions) won’t open such a file and report an incorrect password. For them, build a PFX with legacy algorithms:

bash
openssl pkcs12 -export -legacy -out site-legacy.pfx -inkey privkey.pem -in cert.pem -certfile chain.pem

PFX → JKS (for Java applications that specifically need JKS):

bash
keytool -importkeystore -srckeystore site.pfx -srcstoretype pkcs12 \
  -destkeystore site.jks -deststoretype jks

Key: remove password, convert to PKCS#8:

bash
openssl pkey -in encrypted.key -out privkey.pem           # will ask for password, will save without it
openssl pkcs8 -topk8 -nocrypt -in rsa-pkcs1.key -out privkey.pem

Assembling the chain (fullchain)

Nginx and HAProxy expect a single file: first the site certificate, then intermediates in order; the root is not needed.

bash
cat example.ru.crt intermediate.crt > fullchain.pem

Errors when assembling:

  • wrong order — on startup Nginx takes the key for the first certificate in the file and gives key values mismatch if the first is an intermediate;
  • no newline at the end of file — when concatenated you get -----END CERTIFICATE----------BEGIN CERTIFICATE----- on one line, and the second certificate is not read. Check: grep -c 'BEGIN CERTIFICATE' fullchain.pem should match the number of certificates;
  • root added — not an error, but extra bytes in every handshake; and an old expired root can confuse some clients;
  • wrong intermediate — the CA may issue certificates from several intermediates; take the one specified in your certificate’s issuer field.

For HAProxy the certificate, chain and key are put into one file: cat fullchain.pem privkey.pem > /etc/haproxy/certs/example.ru.pem.

How to create a CSR

For a commercial CA or an internal CA you need a key and a request. The key is generated by you and is not sent anywhere.

bash
# ECDSA P-256 key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out example.ru.key

# CSR with SAN names (OpenSSL 1.1.1+)
openssl req -new -key example.ru.key -out example.ru.csr \
  -subj "/CN=example.ru" \
  -addext "subjectAltName=DNS:example.ru,DNS:www.example.ru"

For an RSA key: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out example.ru.key.

Put names in the SAN: browsers check only it; the CN field is not used for name verification. CAs sometimes copy SAN from CN, but better not to rely on that.

RSA or ECDSA

  • ECDSA P-256 — a 256-bit key whose security is comparable to RSA 3072, smaller certificate, faster signature during the handshake. Supported by all browsers and OSes of the last ten years.
  • RSA 2048 — needed only for very old clients: embedded devices, old Java and Windows XP.

Nginx can serve both certificates simultaneously and chooses the appropriate one based on the client’s capabilities:

nginx
ssl_certificate     /etc/ssl/example.ru.ecdsa.crt;
ssl_certificate_key /etc/ssl/example.ru.ecdsa.key;
ssl_certificate     /etc/ssl/example.ru.rsa.crt;
ssl_certificate_key /etc/ssl/example.ru.rsa.key;

certbot issues ECDSA by default since version 2.0; for RSA use --key-type rsa.

Nginx errors when loading a certificate

  • PEM_read_bio_X509_AUX() failed (SSL: error:0480006C:PEM routines::no start line) — the file is not PEM: you pointed to DER, PFX or P7B. Convert it.

  • SSL_CTX_use_PrivateKey(...) failed (SSL: error:05800074:x509 certificate routines::key values mismatch) — the key is not for this certificate or the first in fullchain is not the site certificate. Compare key and certificate:

    bash
    openssl x509 -in cert.pem -noout -pubkey | openssl sha256
    openssl pkey -in privkey.pem -pubout | openssl sha256
  • Nginx asks for a password on startup or won’t start via systemd — the key is encrypted; remove the password (openssl pkey) and protect the file with permissions.

  • cannot load certificate ... Permission denied — Nginx reads certificates as root on startup, but check permissions and SELinux context if files are not under /etc.

After any changes: nginx -t && systemctl reload nginx, then check with openssl s_client — details in the article “TLS debugging”.

Storing keys

  • Permissions 600 on the key, owner — root; directory — 700.
  • Do not send the key by email or messengers together with the certificate. If you need to transfer a PFX — send the password via a separate channel.
  • Do not put the key into a repository. If that happened, the key is considered compromised: the certificate should be revoked and a new one issued with a new key.
  • When issuing a new certificate, it’s better to generate a new key.

// Similar task

If you are dealing with something similar

This article belongs to one of the main working topics. You can keep reading on the topic, go to the homepage to understand what I do, or open the service pages directly.

Article topic

Security and protection

SSL, hardening, access control, service protection, and secure configurations.

Typical tasks behind this topic

  • Set up SSL, certificates, and secure connections
  • Restrict access and close unnecessary entry points
  • Harden server and service configuration

// Next step

If you need help with this topic, not just another article, it is better to go straight to the service page. The homepage and topic collection stay available as secondary routes.

Open services

// Reviews

Related reviews

apande

apande

Configuring Nginx and OpenCart

2024-09-07 · ★ 5/5

A very powerful buyer
kireevk

kireevk

Diagnosing Nginx Proxy Manager in a Docker container and resolving the issue

2024-03-15 · ★ 5/5

Settled-in customer

// Contact

Need help?

Get in touch with me and I'll help solve the problem

I reply within one business day (03:00-13:00 GMT)

Or leave a request here:

Confirm that you are not a bot.

Write and get a quick reply