By Manny Fernandez

September 30, 2021

Converting Certificates using OpenSSL

No fluff. Just the config that works.

Converting certificates using OpenSSL is one of those jobs every firewall, web and identity admin ends up doing on a Friday afternoon. A CA hands you a .p7b, the load balancer wants a .pfx, the FortiGate wants a certificate plus a key file, and a Java app wants a keystore. This guide covers every conversion you are likely to need, tested on OpenSSL 3, plus the checks that prove the result is right before you deploy it.

Executive Summary

  • Objective: convert certificates and private keys between every format you are likely to meet, and verify the result before you deploy it.
  • Audience: network, security and systems engineers who install certificates on firewalls, web servers, load balancers, Windows hosts and Java apps.
  • Covers: PEM, DER, PKCS#7, PKCS#12 (including legacy files on OpenSSL 3), private key formats (PKCS#8, PKCS#1, SEC1), CSRs, CRLs, Java keystores, Windows options, FortiGate import notes and post-quantum certificates.

Prerequisites and Lab

  • OpenSSL 3.0 or newer. Check with openssl version. Every command below was tested on OpenSSL 3.0.13. OpenSSL 3.5 is the current LTS line.
  • For Java keystores: any JDK with keytool (JDK 9 or newer recommended).
  • A lab CA, leaf certificate and key. The examples use certname.*, privateKey.key and CACert.cer.
  • Treat every unencrypted key file as a secret. Set chmod 600 on it and delete the temporary copies when you are done.

If you want to follow along with throwaway files, this builds a tiny root CA and a leaf signed by it:

openssl req -x509 -newkey rsa:2048 -noenc -keyout ca.key -out CACert.cer \
  -days 30 -subj "/CN=Lab Root CA"
openssl req -newkey rsa:2048 -noenc -keyout privateKey.key -out certname.csr \
  -subj "/CN=www.lab.local"
openssl x509 -req -in certname.csr -CA CACert.cer -CAkey ca.key -CAcreateserial \
  -out certname.pem -days 30

The Format Map

Most confusion comes from mixing up three separate things: the encoding (PEM text or DER binary), the container (a single certificate, a PKCS#7 bundle, a PKCS#12 file, a Java keystore) and the key format (PKCS#8, PKCS#1, SEC1). File extensions tell you almost nothing. A .cer can be PEM or DER, and a .key can be any of three key formats.

Figure 1: Certificate formats and the commands that move between them

Map of certificate formats: PEM and DER encodings, PKCS7, PKCS12 and JKS containers, and the OpenSSL commands between them

Format Encoding Typical extensions Holds PEM label
X.509 certificate PEM or DER .pem .crt .cer .der One certificate (PEM files can hold several) CERTIFICATE
PKCS#7 / P7B PEM or DER .p7b .p7c Certificates and chain, never a key PKCS7
PKCS#12 / PFX DER only .pfx .p12 Key, certificate and chain, password protected none
PKCS#8 private key PEM or DER .key .pk8 Any private key type (RSA, EC, Ed25519, ML-DSA) PRIVATE KEY or ENCRYPTED PRIVATE KEY
PKCS#1 RSA key (traditional) PEM or DER .key RSA private key only RSA PRIVATE KEY
SEC1 EC key (traditional) PEM or DER .key EC private key only EC PRIVATE KEY
PKCS#10 CSR PEM or DER .csr .req Certificate signing request CERTIFICATE REQUEST
CRL PEM or DER .crl Certificate revocation list X509 CRL
Public key (SPKI) PEM or DER .pub .pem Public key only PUBLIC KEY
Java KeyStore Binary .jks .keystore Java-specific key and trust store none

The PEM labels come from RFC 7468, which also says the label on the END line must match the BEGIN line.

Step 1: Identify What You Actually Have

Goal: know the encoding and container before you convert anything. Action: look at the first line, then ask OpenSSL to parse it.

head -1 mystery.cer
# -----BEGIN CERTIFICATE-----   means PEM certificate
# -----BEGIN PKCS7-----         means PEM P7B bundle
# unreadable binary             means DER (or a PFX)

openssl x509 -in mystery.cer -noout -subject -issuer
openssl x509 -inform DER -in mystery.cer -noout -subject -issuer
openssl pkcs7 -in mystery.p7b -print_certs -noout
openssl pkcs12 -in mystery.pfx -info -noout

# OpenSSL 3 universal reader: tries every known format
openssl storeutl -noout -text mystery.cer

Verification: the right command prints subject= and issuer= lines. The wrong one fails with Could not read certificate or Unable to load certificate.

Gotcha: Do not trust the file command for this. On my Ubuntu lab box it labeled a normal PKCS#8 PEM key as an “OpenSSH private key” and a P7B as plain “ASCII text”. Use OpenSSL to identify certificate files.

Step 2: Convert Between PEM and DER

DER to PEM

openssl x509 -inform DER -in certname.der -out certname.pem

PEM to DER

openssl x509 -in certname.pem -outform DER -out certname.der

A PEM file is the DER bytes Base64 encoded with BEGIN/END lines, so you can paste it into a GUI field or an email. A DER file is the raw binary ASN.1. One PEM file can hold several certificates back to back; one DER file holds exactly one, so converting a PEM chain to DER only keeps the first certificate.

X.509 is the certificate standard itself; PEM and DER are just two ways to encode it. OpenSSL 3 auto-detects DER input for openssl x509, but OpenSSL 1.1.1 does not, so always pass -inform DER for binary files.

Verification: openssl x509 -inform DER -in certname.der -noout -fingerprint -sha256 and the same on the PEM file must print the same fingerprint.

Step 3: PKCS#7 (P7B) Bundles

PEM certificate plus chain to P7B

openssl crl2pkcs7 -nocrl -certfile certname.pem -certfile CACert.cer \
  -out certname.p7b

You can repeat -certfile once per certificate. Add -outform DER if the target wants a binary P7B.

P7B to PEM

openssl pkcs7 -print_certs -in certname.p7b -out certname.pem
# binary P7B:
openssl pkcs7 -inform DER -print_certs -in certname.p7b -out certname.pem

PKCS#7 holds certificates and the chain only, never the private key. Windows and Java-based servers like Tomcat commonly hand these out or ask for them. The output PEM contains every certificate in the bundle, each preceded by subject= and issuer= lines that importers ignore.

Step 4: PKCS#12 (PFX) Files

PKCS#12 is the one file that carries the private key, the certificate and the chain together, protected by a password. IIS, Windows, Exchange, Azure, many load balancers and the FortiGate PKCS#12 import all use it.

PFX to PEM, split into the pieces you need

# leaf certificate only
openssl pkcs12 -in certname.pfx -clcerts -nokeys -out certname.crt
# chain (intermediates and root) only
openssl pkcs12 -in certname.pfx -cacerts -nokeys -out chain.crt
# private key, unencrypted PKCS#8
openssl pkcs12 -in certname.pfx -nocerts -noenc -out privateKey.key
# private key, encrypted (you are prompted for a new pass phrase)
openssl pkcs12 -in certname.pfx -nocerts -out privateKey.enc.key

The one-liner openssl pkcs12 -in certname.pfx -out certname.pem also works, but it dumps the key and every certificate into one file, and it encrypts the key unless you add -noenc. Split files are what most devices actually ask for.

PEM certificate and key to PFX

openssl pkcs12 -export -in certname.pem -inkey privateKey.key -certfile CACert.cer \
  -name "www.lab.local" -out certname.pfx

On OpenSSL 3 the default export encryption is AES-256-CBC with PBKDF2 and a SHA-256 MAC. That is what you want, but some older importers (older Windows Server releases, older Java and some appliances) cannot read it.

PFX for older importers

openssl pkcs12 -export -in certname.pem -inkey privateKey.key -certfile CACert.cer \
  -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 -out certname-compat.pfx

Use this only when the target rejects the modern file with a bad password or invalid file error even though the password is right. 3DES with SHA-1 is weak, so keep that file off shared drives and delete it after the import.

Reading an old PFX on OpenSSL 3

Files made by older OpenSSL builds and older Windows exports often use RC2-40 encryption. OpenSSL 3 moved those algorithms into the legacy provider, which is not loaded by default, so reading them fails with unsupported and RC2-40-CBC in the error. Add -legacy:

openssl pkcs12 -in old.pfx -legacy -info -noout

# re-encrypt it with modern algorithms so you never need -legacy again
openssl pkcs12 -in old.pfx -legacy -noenc -out tmp.pem
openssl pkcs12 -export -in tmp.pem -out new.pfx
rm tmp.pem

This re-encrypt approach is the same fix Heroku recommends for apps that hit this error after moving to OpenSSL 3.

P7B plus private key to PFX

openssl pkcs7 -print_certs -in certname.p7b -out certname.cer
openssl pkcs12 -export -in certname.cer -inkey privateKey.key -out certname.pfx

OpenSSL finds the certificate that matches the key and stores the rest as the chain. Only add -certfile cacert.cer if the P7B did not include the CA, otherwise the CA ends up in the PFX twice.

Step 5: Private Key Formats

Keys have their own format zoo. Modern OpenSSL writes PKCS#8 (BEGIN PRIVATE KEY) for every algorithm. Older tools and some appliances still want the traditional per-algorithm formats.

PKCS#8 to traditional PKCS#1 (RSA) or SEC1 (EC)

openssl pkey -in privateKey.key -traditional -out privateKey.rsa.key
# result: -----BEGIN RSA PRIVATE KEY-----
openssl ec -in ec.key -out ec.sec1.key
# result: -----BEGIN EC PRIVATE KEY-----

Traditional to PKCS#8

openssl pkey -in privateKey.rsa.key -out privateKey.key

Encrypted PKCS#8, and DER PKCS#8

openssl pkcs8 -topk8 -in privateKey.key -v2 aes-256-cbc -out privateKey.enc.pk8
# result: -----BEGIN ENCRYPTED PRIVATE KEY-----
openssl pkcs8 -topk8 -in privateKey.key -nocrypt -outform DER -out privateKey.pk8

Remove a pass phrase from a key

openssl pkey -in privateKey.enc.key -out privateKey.key

openssl pkey works for RSA, EC, Ed25519 and newer key types. The algorithm-specific commands (openssl rsa, openssl ec) still work but only for their own algorithm.

Step 6: CSRs, CRLs and Public Keys

# CSR PEM to DER and back
openssl req -in certname.csr -outform DER -out certname.csr.der
openssl req -inform DER -in certname.csr.der -out certname.csr

# CRL PEM to DER and back
openssl crl -in lab.crl -outform DER -out lab.crl.der
openssl crl -inform DER -in lab.crl.der -out lab.crl

# extract the public key from a certificate or a private key
openssl x509 -in certname.pem -noout -pubkey > certname.pub
openssl pkey -in privateKey.key -pubout -out certname.pub

Step 7: Java Keystores (JKS and PKCS#12)

Since JDK 9, PKCS#12 is the default keystore type; JKS is a Java-only format. keytool -importkeystore converts in both directions, and modern keytool warns you every time you touch a JKS file.

# JKS to PKCS#12 (then use OpenSSL on the .p12)
keytool -importkeystore -srckeystore old.jks -srcstoretype JKS \
  -destkeystore new.p12 -deststoretype PKCS12

# PFX from OpenSSL into a Java keystore
keytool -importkeystore -srckeystore certname.pfx -srcstoretype PKCS12 \
  -destkeystore app.p12 -deststoretype PKCS12

# trusted CA only (no key)
keytool -importcert -alias labroot -file CACert.cer \
  -keystore truststore.p12 -storetype PKCS12

If an application truly needs JKS, set -deststoretype JKS. For anything new, stay on PKCS#12 so the same file works with OpenSSL.

Step 8: Windows Options

  • WSL on Windows 11: install Ubuntu and use every command in this post unchanged.
  • Git for Windows: Git Bash ships an openssl binary, so many admins already have it.
  • Built-in certutil: good for PEM and DER jobs without installing anything.
certutil -encode certname.der certname.pem
certutil -decode certname.pem certname.der
certutil -dump certname.pfx

See the certutil reference for the full verb list. For PKCS#12 export with a specific algorithm, OpenSSL is still the easier tool.

Step 9: Importing on a FortiGate

FortiOS accepts a local certificate as either PKCS #12 or a certificate with key file. In practice:

  • PKCS #12: upload the .pfx and enter its password. Include the chain in the PFX so the FortiGate can present the full chain.
  • Certificate plus key: upload the PEM certificate and the PEM private key. If the key is encrypted, enter its pass phrase.
  • Import intermediates separately as CA certificates when you use the certificate plus key method, otherwise clients may see an incomplete chain.
  • “Certificate file is duplicated” usually means the certificate was renewed with a key the FortiGate does not hold. Import the renewal together with its key, or generate the CSR on the FortiGate (Fortinet tech tip).

A Note on Post-Quantum Certificates

OpenSSL 3.5, an LTS release from April 2025, added ML-KEM, ML-DSA and SLH-DSA. ML-DSA and SLH-DSA certificates are still X.509, still PEM or DER, and their keys are still PKCS#8 PRIVATE KEY blocks, so every conversion in this post applies unchanged. What changes is the size: expect much larger certificates and keys, and older importers that do not recognize the new algorithm identifiers.

Verification and Validation

Never install a converted file without these three checks.

1. The key matches the certificate

openssl x509 -in certname.pem -noout -pubkey | openssl sha256
openssl pkey -in privateKey.key -pubout | openssl sha256

Expected: both hashes are identical. This works for RSA, EC and Ed25519. The older -modulus | openssl md5 trick works for RSA only.

2. The chain is complete

openssl verify -CAfile CACert.cer certname.pem

Expected: certname.pem: OK. With an intermediate, use -untrusted intermediate.pem and the root as -CAfile.

3. The PFX opens and holds what you think

openssl pkcs12 -in certname.pfx -info -noout
openssl pkcs12 -in certname.pfx -nokeys | grep -E "subject|issuer"

Expected: one Shrouded Keybag, one leaf and each chain certificate exactly once.

Troubleshooting and Gotchas

unsupported … RC2-40-CBC when reading a PFX

Cause: a legacy-encrypted PKCS#12 on OpenSSL 3. Fix: add -legacy, then re-export without it so the new file uses AES-256.

openssl pkcs12 -in old.pfx -legacy -info -noout

The device says the PFX password is wrong, but it is not

Cause: the importer does not support AES-256/PBKDF2 PKCS#12. Fix: re-export with -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1, import, then delete that file.

Could not read certificate / Unable to load certificate

Cause: the wrong command for the format, usually DER fed to a tool expecting PEM, or a P7B fed to openssl x509. Fix: identify the file first (Step 1), then use -inform DER or openssl pkcs7 -print_certs.

openssl storeutl -noout -text mystery.cer

Clients see an incomplete chain

Cause: only the leaf was installed. Fix: build a full chain file in order, leaf first, then intermediates. The root is optional.

cat certname.pem intermediate.pem > fullchain.pem

Quick Reference: Converting Certificates Using OpenSSL

From To Command
DER PEM openssl x509 -inform DER -in c.der -out c.pem
PEM DER openssl x509 -in c.pem -outform DER -out c.der
PEM + chain P7B openssl crl2pkcs7 -nocrl -certfile c.pem -certfile ca.cer -out c.p7b
P7B PEM openssl pkcs7 -print_certs -in c.p7b -out c.pem
PFX PEM key openssl pkcs12 -in c.pfx -nocerts -noenc -out k.key
PFX PEM cert openssl pkcs12 -in c.pfx -clcerts -nokeys -out c.crt
PEM + key PFX openssl pkcs12 -export -in c.pem -inkey k.key -certfile ca.cer -out c.pfx
Old PFX Readable openssl pkcs12 -in old.pfx -legacy -info -noout
PKCS#8 PKCS#1 openssl pkey -in k.key -traditional -out k.rsa.key
PKCS#1 PKCS#8 openssl pkey -in k.rsa.key -out k.key
JKS PKCS#12 keytool -importkeystore -srckeystore a.jks -destkeystore a.p12 -deststoretype PKCS12

Sources

InfoSecMonkey. No fluff. Just the config that works.

One reply on “Converting Certificates using OpenSSL”

Comments are closed.

Recent posts

  • If you've spent any time configuring user authentication on... Full Story

  • DNS is one of those technologies that quietly underpins... Full Story

  • BGP issues on FortiGate firewalls usually trace back to... Full Story

  • Every time your laptop talks to your router, a... Full Story

  • If you've spent any time configuring NAT on a... Full Story

  • If you have spent any time configuring firewall policies... Full Story

  • High availability on FortiGate is one of those features... Full Story

  • If you've configured SD-WAN on a FortiGate, you've almost... Full Story

  • FortiLink is the management protocol that turns a FortiSwitch... Full Story

  • FortiSwitches are pretty rock solid from Mean Time Between... Full Story

  • This is a quicky tip.  Have you ever gone... Full Story

  • DNS is one of those quiet pieces of internet... Full Story

  • This article is an updated version of the previous... Full Story

  • You will add ns2 as a secondary (slave) BIND9... Full Story

  • In the process of deploying my lab, I needed... Full Story

  • RFC 8805, used to be known as Self-Correcting IP... Full Story

  • Years back, I wrote an article about certificate pinning. ... Full Story

  • FortiGates have the ability to send alerts to Microsoft... Full Story

  • In this post, I am going to walk through... Full Story

  • Troubleshooting VoIP on a FortiGate can feel like trying... Full Story

  • Prior to FortiOS 7.0, there were three commands to... Full Story

  • In this post, I am going to go over... Full Story

  • What we are going to do:  We are going... Full Story

  • Choosing between FGCP (FortiGate Clustering Protocol) and FGSP (FortiGate... Full Story

  • Creating a VLAN on macOS (The "Pro" Move) A... Full Story

  • This blog post explores the logic behind how macOS... Full Story

  • Pretty Fly for a Wi-Fi Tell My Wi-Fi Love... Full Story

  • Part of my daily gig is creating BoMs (Bill-of-Materials)... Full Story

  • ICMP introduces several security risks, but careful filtering, rate... Full Story

  • The command diag debug application dhcps -1 enables full... Full Story

  • In the world of FortiOS, execute tac report is... Full Story

  • LLDP; What is it The Link Layer Discovery Protocol... Full Story

  • What it actually does When you run diagnose fdsm... Full Story

  • Monkey Bites are bite-sized, high-impact security insights designed for... Full Story

  • I have run macOS in macOS with Parallels but... Full Story

  • Don't be confused with my other FortiNAC posts where... Full Story

  • This is the third session in a multi-part article... Full Story

  • Today I was configuring key-based authentication on a FortiGate... Full Story

  • Netcat, often called the "Swiss Army knife" of networking,... Full Story

  • At its core, IEEE 802.1X is a network layer... Full Story

  • In case you did not see the previous FortiNAC... Full Story

  • This is our 5th session where we are going... Full Story

  • Now that we have Wireshark installed and somewhat configured,... Full Story

  • The Philosophy of Packet Analysis Troubleshooting isn't about looking... Full Story

  • No fluff. Just the config that works. Executive Summary... Full Story

  • No fluff. Just the config that works. FortiGate can... Full Story

  • Executive Summary A routine external scan of a customer's... Full Story