Sheet ⁨04⁩ · ⁨Cheatsheets⁩Surveyed ⁨2026⁩
Cheatsheets

OpenSSL

A practical OpenSSL reference for TLS and certificate work. Generating keys and CSRs, self-signed certs, inspecting certificates, verifying chains, testing live handshakes with s_client, converting between PEM, DER, PKCS#12 and JKS, and checking that a key matches its certificate.

6 Categories7 Sections18 ExamplesPublished: 16 Oct 2026Updated: 17 Oct 2026
OpenSSLTLSSSLCertificatesPKICSRx509Debugging

Most OpenSSL use falls into two very different jobs. One is creating things: keys, CSRs, an internal CA. The other is answering “why does this TLS connection not work”, which is where the tool earns its reputation for being unfriendly, because the useful invocations are long and the error messages point at the wrong layer.

The single most common failure is worth internalising before the reference below, because it explains a symptom that otherwise makes no sense: the certificate works in your browser and fails in curl.

A server must send its leaf and any intermediates, but never the root. Browsers cache intermediates from other sites and hide a server that omits them, which is why curl, Java and Alpine containers disagree with your browser about the same certificate.

Two details from that diagram cause most wasted time. Hostname matching uses the Subject Alternative Name, not the Common Name, so a certificate with a perfect CN and no SAN simply fails. And s_client without -servername sends no SNI, so the server hands back its default certificate and you end up debugging a mismatch you created yourself.

On AWS, where the certificate lives changes what OpenSSL is even for.

With ACM in front of CloudFront or an ALB you never handle a private key, and OpenSSL becomes purely a debugging tool. It goes back to being a key-management tool the moment you terminate TLS yourself, behind an NLB passthrough or for service-to-service mTLS.

The reference below is grouped the way the work actually splits: keys, then CSRs and certificates, then inspecting, then the verification and live-handshake commands you reach for when something is broken.

Keys

Generating and inspecting private keys.

Generating private keys

RSA for compatibility, EC for anything new.

Modern key generation

Prefer EC (P-256) unless you have a specific compatibility reason. It gives equivalent security to RSA 3072 with far less CPU per handshake.

Code
Terminal window
# EC P-256: smaller, faster, and fine everywhere in 2026
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
# RSA 2048, the safe default when something old is involved
openssl genrsa -out key.pem 2048
# RSA 4096 if a policy demands it (slower handshakes)
openssl genrsa -out key.pem 4096
# genpkey is the modern front end and works for both
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out key.pem
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
  • Never commit a private key. `chmod 600` it and treat a leak as a full rotation.

Encrypted keys, and removing the passphrase

An encrypted key cannot be read by a service that starts unattended, which is why production keys are usually unencrypted on disk and protected by file permissions and a secret manager instead.

Code
Terminal window
# Generate with a passphrase
openssl genpkey -algorithm RSA -aes-256-cbc -out key.enc.pem
# Strip the passphrase (nginx and friends cannot prompt at boot)
openssl rsa -in key.enc.pem -out key.pem
# Add one to an existing key
openssl rsa -in key.pem -aes-256-cbc -out key.enc.pem

Inspecting a key

Reading the bit length confirms you generated what you meant to. It is a common surprise to find a 1024-bit key inherited from an old runbook.

Code
Terminal window
openssl pkey -in key.pem -noout -text # any key type
openssl rsa -in key.pem -noout -text # RSA specifically
openssl pkey -in key.pem -pubout # derive the public key
Execution
Terminal window
openssl pkey -in key.pem -noout -text | head -3
Output
Private-Key: (2048 bit, 2 primes)
modulus:
00:c4:1f:9a:...

CSRs and Certificates

Requesting certificates, and creating your own for internal use.

Certificate signing requests

What you send to a CA, and how to get the SAN right.

Key and CSR in one command, with SANs

`-nodes` means no passphrase on the key. `-addext subjectAltName` is the part that matters: without a SAN the certificate is rejected by every modern client regardless of the CN.

Code
Terminal window
openssl req -new -newkey rsa:2048 -nodes \
-keyout key.pem -out request.csr \
-subj "/C=JO/ST=Amman/L=Amman/O=Example Ltd/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com"
  • CN is legacy. Every hostname you need must appear in the SAN, including the bare domain.

A config file, for anything you will regenerate

Put the config in version control. Regenerating a CSR from a remembered command line is where SANs get dropped.

Code
Terminal window
cat > csr.cnf <<'CNF'
[req]
default_bits = 2048
prompt = no
distinguished_name = dn
req_extensions = ext
[dn]
C = JO
O = Example Ltd
CN = example.com
[ext]
subjectAltName = @alt
[alt]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
CNF
openssl req -new -config csr.cnf -newkey rsa:2048 -nodes \
-keyout key.pem -out request.csr

Verify a CSR before sending it

Always confirm the SAN is present before paying a CA. Finding out afterwards costs a reissue.

Code
Terminal window
openssl req -in request.csr -noout -text -verify
openssl req -in request.csr -noout -subject
Execution
Terminal window
openssl req -in request.csr -noout -text | grep -A2 "Alternative Name"
Output
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com

Self-signed and internal CAs

For local development and internal services.

A self-signed certificate in one line

Fine for local development. Clients will not trust it unless you add it to their trust store, which is the point of the next example.

Code
Terminal window
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout key.pem -out cert.pem \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

A small internal CA, then a cert signed by it

`openssl x509 -req` does NOT copy extensions from the CSR. Without `-extfile` the signed certificate has no SAN and will be rejected, even though the CSR had one.

Code
Terminal window
# 1. the CA (keep ca.key somewhere safe)
openssl req -x509 -newkey rsa:4096 -nodes -days 3650 \
-keyout ca.key -out ca.crt -subj "/CN=Example Internal CA"
# 2. a key + CSR for the service
openssl req -new -newkey rsa:2048 -nodes \
-keyout svc.key -out svc.csr -subj "/CN=svc.internal" \
-addext "subjectAltName=DNS:svc.internal"
# 3. sign it, copying the SAN across (this is the step people miss)
openssl x509 -req -in svc.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -days 365 -out svc.crt \
-extfile <(printf "subjectAltName=DNS:svc.internal")
  • Distribute `ca.crt` to clients; never distribute `ca.key`.

Inspecting Certificates

Reading what a certificate actually says.

Reading a certificate

Dates, names, issuer, extensions.

The fields you actually check

`-noout` suppresses the re-printed PEM, which is what makes these usable in scripts.

Code
Terminal window
openssl x509 -in cert.pem -noout -text # everything
openssl x509 -in cert.pem -noout -subject -issuer # who, signed by whom
openssl x509 -in cert.pem -noout -dates # validity window
openssl x509 -in cert.pem -noout -ext subjectAltName
openssl x509 -in cert.pem -noout -fingerprint -sha256
openssl x509 -in cert.pem -noout -serial
Execution
Terminal window
openssl x509 -in cert.pem -noout -dates -subject
Output
notBefore=Sep 1 00:00:00 2026 GMT
notAfter=Nov 30 23:59:59 2026 GMT
subject=CN = example.com

Is it expiring soon?

`-checkend` is the whole expiry-monitoring problem solved in one flag. Wire it into a cron check and stop finding out from customers.

Code
Terminal window
# exit 0 if the cert is still valid in 30 days, 1 if not
openssl x509 -in cert.pem -noout -checkend $((30*86400))
# readable form for a monitoring script
openssl x509 -in cert.pem -noout -enddate | cut -d= -f2
  • Check the whole chain, not just the leaf. An expired intermediate breaks the site just as thoroughly.

Does this key match this certificate?

Two identical hashes means the pair matches. This is the first thing to check when a server refuses to start with a key-mismatch error.

Code
Terminal window
# RSA: the moduli must be identical
openssl x509 -noout -modulus -in cert.pem | openssl sha256
openssl rsa -noout -modulus -in key.pem | openssl sha256
# EC keys: compare the public keys instead
openssl x509 -in cert.pem -noout -pubkey | openssl sha256
openssl pkey -in key.pem -pubout | openssl sha256

Verifying and Debugging

Chains, live handshakes, and why a client disagrees with your browser.

Chain verification

The missing-intermediate problem, and how to prove it.

Verify a chain offline

`-untrusted` is confusingly named: it means "here are intermediate certificates to build the path with", not "do not trust these".

Code
Terminal window
# against the system trust store
openssl verify cert.pem
# supplying the intermediate the server should be sending
openssl verify -untrusted intermediate.pem cert.pem
# against a private CA
openssl verify -CAfile ca.crt svc.crt
Execution
Terminal window
openssl verify -untrusted intermediate.pem cert.pem
Output
cert.pem: OK
  • "unable to get local issuer certificate" almost always means a missing intermediate, not an untrusted root.

What is the server actually serving?

Two or three certificates is normal (leaf plus intermediates). One means the server is not sending its intermediate, which works in browsers and fails in curl, Java and Alpine.

Code
Terminal window
# -servername sends SNI. Without it you may get the wrong cert entirely.
openssl s_client -connect example.com:443 -servername example.com
# count the certificates in the chain: one leaf only is a bug
openssl s_client -connect example.com:443 -servername example.com \
-showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"
# verify against a specific CA bundle
openssl s_client -connect example.com:443 -servername example.com \
-CAfile /etc/ssl/certs/ca-certificates.crt
Execution
Terminal window
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"
Output
2
  • Always pass `-servername`. Omitting SNI is the most common self-inflicted TLS debugging wound.
  • `</dev/null` stops s_client hanging waiting for input.

Protocol and cipher checks

A handshake failure on `-tls1_1` is the result you want: it proves the old protocol is disabled rather than merely unpreferred.

Code
Terminal window
# is TLS 1.2 still enabled? is 1.3 available?
openssl s_client -connect host:443 -tls1_2 </dev/null 2>&1 | grep -E "Protocol|Cipher"
openssl s_client -connect host:443 -tls1_3 </dev/null 2>&1 | grep -E "Protocol|Cipher"
# confirm an old protocol is actually refused
openssl s_client -connect host:443 -tls1_1 </dev/null 2>&1 | tail -3
# STARTTLS for mail, and other protocols
openssl s_client -starttls smtp -connect mail:587
openssl s_client -starttls postgres -connect db:5432

Converting Formats

PEM, DER, PKCS#12 and Java keystores.

Format conversions

The same certificate, different envelopes.

PEM, DER, and PKCS#12

PEM is text and safe to paste; DER is binary and used by Java and Windows tooling. PKCS#12 carries the key and the chain together in one password-protected file.

Code
Terminal window
# PEM (base64, "-----BEGIN") <-> DER (binary)
openssl x509 -in cert.pem -outform der -out cert.der
openssl x509 -in cert.der -inform der -out cert.pem
# bundle cert + key + chain into one .p12 (Windows, Java, browsers)
openssl pkcs12 -export -out bundle.p12 \
-inkey key.pem -in cert.pem -certfile intermediate.pem \
-name "example.com"
# and back out again
openssl pkcs12 -in bundle.p12 -nokeys -out cert.pem
openssl pkcs12 -in bundle.p12 -nocerts -nodes -out key.pem
  • Check which you have with `file cert.crt`, or just look for `-----BEGIN`.

Java keystores

OpenSSL cannot write JKS directly. Go through PKCS#12; `keytool` handles the last step.

Code
Terminal window
# the supported route: build a PKCS#12, then convert
openssl pkcs12 -export -out bundle.p12 -inkey key.pem -in cert.pem
keytool -importkeystore -srckeystore bundle.p12 -srcstoretype PKCS12 \
-destkeystore keystore.jks -deststoretype JKS
# inspect what a keystore holds
keytool -list -v -keystore keystore.jks

Everyday Utilities

The parts of OpenSSL that have nothing to do with certificates.

Hashes, random, and base64

Handy when you have OpenSSL and not much else.

Random values and digests

`openssl rand -hex 32` is a reliable way to generate a secret on a box with no other tooling.

Code
Terminal window
openssl rand -hex 32 # 32 random bytes as hex
openssl rand -base64 24 # a decent password
openssl dgst -sha256 file.tar.gz # checksum
openssl base64 -in f -out f.b64 # encode
openssl base64 -d -in f.b64 # decode
  • For password hashing use a real KDF (argon2, bcrypt, scrypt), not `dgst`.

Quick symmetric encryption

Fine for moving a file past an untrusted channel. Always include `-pbkdf2` and `-salt`; the old defaults were weak.

Code
Terminal window
openssl enc -aes-256-cbc -pbkdf2 -salt -in f -out f.enc
openssl enc -d -aes-256-cbc -pbkdf2 -in f.enc -out f

Was this useful?

You might also enjoy

More posts on similar topics

Linux Networking

Every Linux box speaks the network through a small set of tools, and knowing them turns "the network is broken" into a specific, fixable answer. This cheatsheet covers the modern stack: ip for inter

#Linux networking#Ip command#Tcpdump+5 tags
read more

Netcat

This netcat cheatsheet covers TCP/UDP connections, file transfers, port scanning, banner grabbing, and advanced network operations.

#Netcat#Nc#Network+4 tags
read more

Netstat

This netstat cheatsheet covers six categories, with worked examples and troubleshooting steps in each.

#Netstat#Network#Connections+3 tags
read more

Linux Processes

Most of what you need day to day is inspecting what is running, sending it the right signal, and reading why it stopped. The reference below covers those in order, but two ideas are worth internalisin

#Linux#Processes#Ps+6 tags
read more

TypeScript

A fast reference for the TypeScript you write every day: primitives and unions, interfaces versus type aliases, generics and constraints, the built-in utility types (Partial, Pick, Omit, Record, Retur

#TypeScript#TypeScript 7#Types+5 tags
read more

Python Virtual Environments Cheatsheet

A fast reference for keeping Python projects isolated and reproducible. It covers creating and activating environments with venv, installing packages with pip, locking dependencies with requirements f

#Python#Virtualenv#Pip+5 tags
read more

6 related posts