The certificate cheat sheet

Every command you are likely to need for TLS certificates, on one page. 44 commands covering inspection, generation, format conversion, verification, live servers, key handling and running your own CA. Each one is executed against OpenSSL 3.5.7 on every build of this site, so nothing here is a copy of a guide that stopped working three major versions ago.

Inspect #

Read what is actually in a file, or on a server, before changing anything.

Read a certificate explain →
openssl x509 -in certificate.crt -noout -text
Subject, issuer and dates only explain →
openssl x509 -in certificate.crt -noout -subject -issuer -dates
Subject alternative names only explain →
openssl x509 -in certificate.crt -noout -ext subjectAltName

The only field browsers match hostnames against.

When does it expire? explain →
openssl x509 -in certificate.crt -noout -enddate
Will it still be valid in 30 days? (exit code) explain →
openssl x509 -in certificate.crt -noout -checkend 2592000

Exits 0 if yes, 1 if no. The form you want in monitoring.

Read a CSR explain →
openssl req -in request.csr -noout -text
Read a private key explain →
openssl pkey -in private.key -noout -text
SHA-256 fingerprint
openssl x509 -in certificate.crt -noout -fingerprint -sha256
Every certificate in a bundle explain →
openssl crl2pkcs7 -nocrl -certfile fullchain.crt | openssl pkcs7 -print_certs -noout
What is in a PFX? explain →
openssl pkcs12 -in certificate.pfx -info -noout

Generate #

Keys, requests and self-signed certificates.

RSA 2048 private key explain →
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.key
EC P-256 private key explain →
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.key

Smaller and faster than RSA at equivalent strength.

Key and CSR together, with SANs explain →
openssl req -new -newkey rsa:2048 -nodes \
  -keyout example.com.key -out example.com.csr \
  -subj "/C=US/O=Example Inc/CN=example.com" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.com"

Omit the SANs and every browser rejects the certificate.

CSR from a key you already have explain →
openssl req -new -key private.key -out request.csr \
  -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com"
Self-signed certificate explain →
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout private.key -out certificate.crt -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
Extract the public key explain →
openssl pkey -in private.key -pubout -out public.pem

Convert #

Between PEM, DER, PKCS#7, PKCS#12 and Java keystores.

PEM → DER explain →
openssl x509 -in certificate.pem -outform der -out certificate.der
DER → PEM explain →
openssl x509 -inform der -in certificate.der -out certificate.pem
PEM → PFX (with chain) explain →
openssl pkcs12 -export -out certificate.pfx \
  -inkey private.key -in certificate.crt -certfile chain.crt

Add -legacy if Windows or older Java rejects the result.

PFX → key explain →
openssl pkcs12 -in certificate.pfx -nocerts -nodes -out private.key
PFX → certificate explain →
openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out certificate.crt
PFX → chain explain →
openssl pkcs12 -in certificate.pfx -cacerts -nokeys -out chain.crt
PEM → P7B explain →
openssl crl2pkcs7 -nocrl -certfile certificate.crt -certfile chain.crt -out bundle.p7b
P7B → PEM explain →
openssl pkcs7 -print_certs -in bundle.p7b -out certificates.pem
PKCS#1 → PKCS#8 explain →
openssl pkcs8 -topk8 -in pkcs1.key -out pkcs8.key -nocrypt

Java and Go accept PKCS#8 only.

PKCS#8 → PKCS#1 explain →
openssl rsa -in pkcs8.key -traditional -out pkcs1.key

-traditional is required on OpenSSL 3.

PFX → JKS explain →
keytool -importkeystore \
  -srckeystore certificate.pfx -srcstoretype PKCS12 \
  -destkeystore keystore.jks -deststoretype JKS

Verify #

Prove things belong together before a deploy fails.

Does this key match this certificate? explain →
openssl pkey -in private.key -pubout | openssl sha256
openssl x509 -in certificate.crt -noout -pubkey | openssl sha256

Same hash means yes. Works for EC keys, unlike comparing moduli.

Does the CSR match the key? explain →
openssl req -in request.csr -noout -pubkey | openssl sha256
openssl pkey -in private.key -pubout | openssl sha256
Does the chain reach a trusted root? explain →
openssl verify -CAfile root-ca.crt -untrusted intermediate.crt certificate.crt
Is the CSR self-signature intact? explain →
openssl req -in request.csr -noout -verify

Live servers #

What a host is actually serving right now.

Fetch a server's certificate explain →
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Without -servername you may inspect the wrong certificate.

Did the chain validate? explain →
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | grep "Verify return code"
Is the intermediate being sent? explain →
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
  | grep -c "BEGIN CERTIFICATE"

1 means the chain is incomplete.

Save the whole chain explain →
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
  | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > chain.pem
SMTP, IMAP and other STARTTLS protocols explain →
openssl s_client -connect mail.example.com:587 -starttls smtp </dev/null

Key handling #

Passphrases and formats.

Remove a passphrase explain →
openssl pkey -in encrypted.key -out plain.key
Add a passphrase explain →
openssl pkey -in plain.key -aes256 -out encrypted.key
Which key format is this? explain →
head -n1 private.key

The header line names the format.

PEM or DER? explain →
head -c 2 mystery.crt | xxd

3082 is DER; a dash is PEM.

Lock down a key file explain →
chmod 600 private.key

Run your own CA #

Trust a root once instead of trusting certificates one at a time.

Create a root CA explain →
openssl req -x509 -new -key ca.key -out ca.crt -days 3650 -sha256 \
  -subj "/CN=Example Internal Root CA" \
  -addext "basicConstraints=critical,CA:TRUE" \
  -addext "keyUsage=critical,keyCertSign,cRLSign"
Sign a CSR explain →
openssl x509 -req -in request.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out certificate.crt -days 397 -sha256 -extfile ext.cnf

Without -extfile or -copy_extensions the SANs are silently dropped.

Build the chain file for a server explain →
cat certificate.crt intermediate.crt > fullchain.crt

Leaf first. Do not append the root.

Decoding things without a terminal

The same operations, in your browser. Nothing you paste is uploaded — the tool pages block network requests with a Content Security Policy.