// 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: Mikhail Gorbachev
// 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:
| Marker | What’s inside |
|---|---|
BEGIN CERTIFICATE | certificate |
BEGIN CERTIFICATE REQUEST | CSR |
BEGIN PRIVATE KEY | private key in PKCS#8 format (any algorithm) |
BEGIN ENCRYPTED PRIVATE KEY | PKCS#8 key encrypted with a password |
BEGIN RSA PRIVATE KEY | RSA key in the older PKCS#1 format |
BEGIN EC PRIVATE KEY | ECDSA key in the older format |
BEGIN PKCS7 | PKCS#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
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 # P7BHow many certificates are in a PEM file and which ones:
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -nooutConversion
DER → PEM and back:
openssl x509 -in cert.der -inform der -out cert.pem
openssl x509 -in cert.pem -outform der -out cert.derP7B → PEM (get the chain as regular certificates):
openssl pkcs7 -in chain.p7b -print_certs -out chain.pemThe 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):
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:
openssl pkcs12 -legacy -in site.pfx -nocerts -nodes -out privkey.pemPEM → PFX (from Linux to Windows or Java):
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:
openssl pkcs12 -export -legacy -out site-legacy.pfx -inkey privkey.pem -in cert.pem -certfile chain.pemPFX → JKS (for Java applications that specifically need JKS):
keytool -importkeystore -srckeystore site.pfx -srcstoretype pkcs12 \
-destkeystore site.jks -deststoretype jksKey: remove password, convert to PKCS#8:
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.pemAssembling the chain (fullchain)
Nginx and HAProxy expect a single file: first the site certificate, then intermediates in order; the root is not needed.
cat example.ru.crt intermediate.crt > fullchain.pemErrors when assembling:
- wrong order — on startup Nginx takes the key for the first certificate in the file and gives
key values mismatchif 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.pemshould 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
issuerfield.
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.
# 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:
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 infullchainis not the site certificate. Compare key and certificate:bashopenssl x509 -in cert.pem -noout -pubkey | openssl sha256 openssl pkey -in privkey.pem -pubout | openssl sha256Nginx 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
Thanks to Mikhail for his responsiveness. We spoke by phone; he explained how I could do it myself. This is my second time reaching out — everything’s great and prompt.
Thanks to Mikhail for his responsiveness. We had a call; he explained how I could do it myself. This is my second time contacting him; everything is great and prompt.
Consultation on Nginx Proxy Manager and Portainer
2025-02-25 · ★ 5/5
I want to express my deep gratitude to the specialist who set up SEO-friendly URLs for me on OpenCart. Configuring them turned out to be easy and simple, and I'm glad I finally found a professional who did everything well and without unnecessary complications. Before this I went through four specialists, and each time there were issues with the setup, but this person handled the task perfectly.
I want to express my great gratitude to the specialist who configured SEO-friendly URLs for me on OpenCart. Setting up the URLs turned out to be easy and simple, and I'm glad that I finally found a professional who did …
Excellent work — this isn't the first time I've used them; they find solutions to complex problems. I recommend.
Excellent work, this isn't my first time contacting them; they find solutions to complex problems. I recommend.
Excellent work! He completed the assigned task on time and without errors. It was a pleasure to work with him; I recommend him.
Great job! Completed the assigned task on time and without errors. It was a pleasure to work together, I recommend.
Excellent specialist — he delved into the problem, figured it out, and fixed it. I recommend him.
Excellent specialist, delved into the problem, figured it out, and fixed it. I recommend.
I needed to resolve an SSL certificate issue on a server where the certificate had been issued through Nginx Proxy Manager. Mikhail clarified all the details of my setup and requested access to assess the feasibility of solving the issue, since he hadn't worked with that service before. He quickly figured it out and fixed my problem. Perfect cooperation)
I needed to solve an issue with an SSL certificate on the server that was issued through Ngnix Proxy manager. Mikhail clarified all the details of how everything is set up for me, asked for access to assess the …
Diagnosing Nginx Proxy Manager in a Docker container and resolving the issue
2024-03-15 · ★ 5/5
// 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:
// Related