Quick reference
Version and build options
openssl version -aText format, base64 wrapped
PEMBinary format
DERKey plus certificate in one file
PKCS#12 (.pfx, .p12)Certificate request
CSRNo 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
| Task | Command |
|---|---|
| RSA 2048 key | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server.key |
| RSA key, classic syntax | openssl genrsa -out server.key 2048 |
| EC key on P-256 | openssl ecparam -genkey -name prime256v1 -out server.key |
| Key with a passphrase | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes256 -out server.key |
| Remove a passphrase | openssl rsa -in server.key -out server-nopass.key |
| Add a passphrase | openssl rsa -in server.key -aes256 -out server-enc.key |
| Inspect a key | openssl pkey -in server.key -noout -text |
| Extract the public key | openssl pkey -in server.key -pubout -out server.pub |
| Check a key is intact | openssl 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
| Task | Command |
|---|---|
| CSR from an existing key | openssl req -new -key server.key -out server.csr |
| Key and CSR in one step | openssl req -new -newkey rsa:2048 -noenc -keyout server.key -out server.csr |
| Non-interactive subject | openssl req -new -key server.key -out server.csr -subj "/C=IT/O=Example/CN=example.com" |
| CSR with subject alternative names | openssl 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 year | openssl req -x509 -newkey rsa:2048 -noenc -keyout server.key -out server.crt -days 365 -subj "/CN=example.com" |
| Self-signed with SANs | openssl 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 CSR | openssl req -in server.csr -noout -text |
| Just the subject of a CSR | openssl req -in server.csr -noout -subject |
| Verify a CSR signature | openssl 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
| Task | Command |
|---|---|
| Everything, in text | openssl x509 -in server.crt -noout -text |
| Subject and issuer | openssl x509 -in server.crt -noout -subject -issuer |
| Validity dates | openssl x509 -in server.crt -noout -dates |
| Has it expired | openssl x509 -in server.crt -noout -checkend 0 |
| Will it expire within 30 days | openssl x509 -in server.crt -noout -checkend 2592000 |
| SHA-256 fingerprint | openssl x509 -in server.crt -noout -fingerprint -sha256 |
| Serial number | openssl x509 -in server.crt -noout -serial |
| Subject alternative names | openssl x509 -in server.crt -noout -ext subjectAltName |
| Every certificate in a bundle | openssl 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 to | Command |
|---|---|
| PEM to DER | openssl x509 -in cert.pem -outform der -out cert.der |
| DER to PEM | openssl x509 -inform der -in cert.der -out cert.pem |
| Key: PEM to DER | openssl pkey -in key.pem -outform der -out key.der |
| PEM to PKCS#12 | openssl pkcs12 -export -out bundle.pfx -inkey server.key -in server.crt -certfile chain.crt |
| PKCS#12 to PEM, everything | openssl pkcs12 -in bundle.pfx -out all.pem -noenc |
| PKCS#12, private key only | openssl pkcs12 -in bundle.pfx -nocerts -noenc -out key.pem |
| PKCS#12, certificate only | openssl pkcs12 -in bundle.pfx -clcerts -nokeys -out cert.pem |
| PKCS#12, CA chain only | openssl pkcs12 -in bundle.pfx -cacerts -nokeys -out chain.pem |
| Read an old PKCS#12 on OpenSSL 3.x | openssl pkcs12 -in old.pfx -out all.pem -noenc -legacy |
| Export for older software | openssl 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
| Task | Command |
|---|---|
| Open a TLS session | openssl s_client -connect example.com:443 -servername example.com |
| Show the full chain the server sends | openssl s_client -connect example.com:443 -showcerts |
| Expiry dates of the live certificate | echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates |
| Subject and issuer of the live certificate | echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer |
| Force a protocol version | openssl s_client -connect example.com:443 -tls1_2 |
| Check TLS 1.3 is offered | openssl s_client -connect example.com:443 -tls1_3 |
| SMTP with STARTTLS | openssl s_client -connect smtp.example.com:587 -starttls smtp |
| IMAP with STARTTLS | openssl 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
| Task | Command |
|---|---|
| Verify against a CA file | openssl verify -CAfile ca.crt server.crt |
| Verify with an intermediate | openssl 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 key | openssl rsa -in server.key -noout -modulus | openssl sha256 |
| Key and certificate match, any algorithm | openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl sha256 |
| … compare with the key | openssl pkey -in server.key -pubout -outform der | openssl sha256 |
| Does the CSR match the key | openssl 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
| Task | Command |
|---|---|
| SHA-256 of a file | openssl dgst -sha256 install.iso |
| SHA-512 of a file | openssl dgst -sha512 install.iso |
| 32 random bytes, base64 | openssl rand -base64 32 |
| 16 random bytes, hex | openssl rand -hex 16 |
| A SHA-512 crypt password hash | openssl passwd -6 'correct horse battery staple' |
| Encrypt a file | openssl enc -aes-256-cbc -pbkdf2 -salt -in secrets.txt -out secrets.enc |
| Decrypt it | openssl enc -d -aes-256-cbc -pbkdf2 -in secrets.enc -out secrets.txt |
| Base64 encode | openssl base64 -in file.bin -out file.b64 |
| Base64 decode | openssl 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.