OpenSSL Cheat Sheet

Quick reference

Version and build options
openssl version -a
Text format, base64 wrapped
PEM
Binary format
DER
Key plus certificate in one file
PKCS#12 (.pfx, .p12)
Certificate request
CSR
No passphrase on output
OpenSSL 3.x: -noenc, 1.x: -nodes
PEM is the one you can read: A PEM file is base64 between BEGIN and END lines, which is why you can paste one into a config file or a web form. DER is the same data in binary. Everything below converts freely between them.
OpenSSL 3.0 changed two things that bite: -nodes was deprecated in favour of -noenc (both still work), and PKCS#12 export now defaults to AES-256-CBC with PBKDF2 instead of the old 3DES and RC2. Older software that cannot read the new default needs -legacy on export, or explicit -certpbe and -keypbe values.

Private keys

TaskCommand
RSA 2048 keyopenssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server.key
RSA key, classic syntaxopenssl genrsa -out server.key 2048
EC key on P-256openssl ecparam -genkey -name prime256v1 -out server.key
Key with a passphraseopenssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes256 -out server.key
Remove a passphraseopenssl rsa -in server.key -out server-nopass.key
Add a passphraseopenssl rsa -in server.key -aes256 -out server-enc.key
Inspect a keyopenssl pkey -in server.key -noout -text
Extract the public keyopenssl pkey -in server.key -pubout -out server.pub
Check a key is intactopenssl rsa -in server.key -check -noout
A key with a passphrase stops a service from starting unattended: nginx, Apache and most agents will sit at a password prompt on boot. Either store the key without a passphrase and protect it with file permissions, or wire up a passphrase supply the service can use. Discovering this during a maintenance window is a bad time.

Requests and self-signed certificates

TaskCommand
CSR from an existing keyopenssl req -new -key server.key -out server.csr
Key and CSR in one stepopenssl req -new -newkey rsa:2048 -noenc -keyout server.key -out server.csr
Non-interactive subjectopenssl req -new -key server.key -out server.csr -subj "/C=IT/O=Example/CN=example.com"
CSR with subject alternative namesopenssl req -new -key server.key -out server.csr -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
Self-signed certificate, one yearopenssl req -x509 -newkey rsa:2048 -noenc -keyout server.key -out server.crt -days 365 -subj "/CN=example.com"
Self-signed with SANsopenssl req -x509 -newkey rsa:2048 -noenc -keyout server.key -out server.crt -days 365 -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,IP:10.0.0.10"
Read back a CSRopenssl req -in server.csr -noout -text
Just the subject of a CSRopenssl req -in server.csr -noout -subject
Verify a CSR signatureopenssl req -in server.csr -noout -verify
Browsers ignore the common name: Since 2017 every mainstream browser matches the hostname against subjectAltName only. A certificate with a CN and no SAN will be rejected, so -addext "subjectAltName=..." is not optional on anything that a browser will visit.

Inspecting a certificate

TaskCommand
Everything, in textopenssl x509 -in server.crt -noout -text
Subject and issueropenssl x509 -in server.crt -noout -subject -issuer
Validity datesopenssl x509 -in server.crt -noout -dates
Has it expiredopenssl x509 -in server.crt -noout -checkend 0
Will it expire within 30 daysopenssl x509 -in server.crt -noout -checkend 2592000
SHA-256 fingerprintopenssl x509 -in server.crt -noout -fingerprint -sha256
Serial numberopenssl x509 -in server.crt -noout -serial
Subject alternative namesopenssl x509 -in server.crt -noout -ext subjectAltName
Every certificate in a bundleopenssl crl2pkcs7 -nocrl -certfile chain.pem | openssl pkcs7 -print_certs -noout
checkend returns an exit code: -checkend prints a line and exits 0 if the certificate is still valid for that many seconds, or 1 if it is not. That makes it the whole body of a monitoring check: openssl x509 -in server.crt -noout -checkend 2592000 || echo EXPIRING.

Converting between formats

From and toCommand
PEM to DERopenssl x509 -in cert.pem -outform der -out cert.der
DER to PEMopenssl x509 -inform der -in cert.der -out cert.pem
Key: PEM to DERopenssl pkey -in key.pem -outform der -out key.der
PEM to PKCS#12openssl pkcs12 -export -out bundle.pfx -inkey server.key -in server.crt -certfile chain.crt
PKCS#12 to PEM, everythingopenssl pkcs12 -in bundle.pfx -out all.pem -noenc
PKCS#12, private key onlyopenssl pkcs12 -in bundle.pfx -nocerts -noenc -out key.pem
PKCS#12, certificate onlyopenssl pkcs12 -in bundle.pfx -clcerts -nokeys -out cert.pem
PKCS#12, CA chain onlyopenssl pkcs12 -in bundle.pfx -cacerts -nokeys -out chain.pem
Read an old PKCS#12 on OpenSSL 3.xopenssl pkcs12 -in old.pfx -out all.pem -noenc -legacy
Export for older softwareopenssl pkcs12 -export -out bundle.pfx -inkey server.key -in server.crt -legacy
“unsupported” when opening a .pfx: OpenSSL 3.x does not load the legacy provider by default, so a PKCS#12 file encrypted with RC2 or 3DES by an older tool fails to open. Add -legacy. The same flag on export produces a file that older Java and Windows tooling can still read.

Testing a live server

TaskCommand
Open a TLS sessionopenssl s_client -connect example.com:443 -servername example.com
Show the full chain the server sendsopenssl s_client -connect example.com:443 -showcerts
Expiry dates of the live certificateecho | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
Subject and issuer of the live certificateecho | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer
Force a protocol versionopenssl s_client -connect example.com:443 -tls1_2
Check TLS 1.3 is offeredopenssl s_client -connect example.com:443 -tls1_3
SMTP with STARTTLSopenssl s_client -connect smtp.example.com:587 -starttls smtp
IMAP with STARTTLSopenssl s_client -connect imap.example.com:143 -starttls imap
-servername matters: Without it OpenSSL does not send SNI, and a host serving several sites from one address will hand back the wrong certificate. If a certificate looks wrong from the command line but right in a browser, this is almost always why. Press Ctrl+C or send Q to close the session.

Verifying and matching

TaskCommand
Verify against a CA fileopenssl verify -CAfile ca.crt server.crt
Verify with an intermediateopenssl verify -CAfile root.crt -untrusted intermediate.crt server.crt
Does the key match the certificate (RSA)openssl x509 -in server.crt -noout -modulus | openssl sha256
… compare with the keyopenssl rsa -in server.key -noout -modulus | openssl sha256
Key and certificate match, any algorithmopenssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl sha256
… compare with the keyopenssl pkey -in server.key -pubout -outform der | openssl sha256
Does the CSR match the keyopenssl req -in server.csr -pubkey -noout | openssl pkey -pubin -outform der | openssl sha256
The modulus trick, generalised: Two files belong together when their public keys hash to the same value. The modulus comparison only works for RSA; the pkey -pubout form works for RSA and EC alike, which is why it is worth learning as the default.

Hashes, random values and file encryption

TaskCommand
SHA-256 of a fileopenssl dgst -sha256 install.iso
SHA-512 of a fileopenssl dgst -sha512 install.iso
32 random bytes, base64openssl rand -base64 32
16 random bytes, hexopenssl rand -hex 16
A SHA-512 crypt password hashopenssl passwd -6 'correct horse battery staple'
Encrypt a fileopenssl enc -aes-256-cbc -pbkdf2 -salt -in secrets.txt -out secrets.enc
Decrypt itopenssl enc -d -aes-256-cbc -pbkdf2 -in secrets.enc -out secrets.txt
Base64 encodeopenssl base64 -in file.bin -out file.b64
Base64 decodeopenssl base64 -d -in file.b64 -out file.bin
Always pass -pbkdf2: Without it openssl enc derives the key with a single MD5 pass, which is weak enough to matter. -pbkdf2 is available from OpenSSL 1.1.1 and costs nothing. A file encrypted with it must also be decrypted with it.

FAQ

-nodes or -noenc?
They do the same thing: write the private key without a passphrase. -nodes was deprecated in OpenSSL 3.0 in favour of -noenc, but both are still accepted. Use -noenc in new work and expect -nodes in every older runbook you inherit.
A .pfx that used to open now fails on this server
The server has OpenSSL 3.x, which does not load the legacy provider by default. The file was encrypted with RC2 or 3DES by older tooling. Add -legacy to the command.
The certificate looks correct but the browser still complains
Check the subject alternative names with openssl x509 -in server.crt -noout -ext subjectAltName. Browsers ignore the common name entirely and match the hostname against SAN only. Also confirm the server sends its intermediate certificates, with openssl s_client -connect host:443 -showcerts.
How do I check a certificate without downloading it?
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -subject -issuer reads the live certificate straight off the server. Include -servername or a multi-site host will return the wrong one.
Which key size and algorithm should I use?
RSA 2048 remains the safe default for compatibility and is accepted by every public CA. ECDSA on P-256 gives equivalent strength with smaller keys and faster handshakes, and is well supported by modern clients. RSA 4096 costs noticeably more CPU per handshake for a benefit most environments do not need.
Where do I put the intermediate certificate?
Most servers want the leaf certificate and the intermediates in one PEM file, leaf first, in order up the chain. The root is not included; clients already have it. If openssl verify -CAfile root.crt server.crt fails but succeeds with -untrusted intermediate.crt, the chain is what your server is missing.