Skip to content

OpenSSL Practical Reference

openssl is the Swiss Army knife of cryptography on the command line. This note is a dense practical reference for the tasks you’ll actually run: inspecting certificates, testing TLS, generating keys, creating a local CA, and verifying file integrity.


Terminal window
# Full certificate dump (all fields)
openssl x509 -in server.crt -text -noout
# Quick summary: subject, issuer, validity dates
openssl x509 -in server.crt -noout -subject -issuer -dates
# Extract just the serial number
openssl x509 -in server.crt -noout -serial
# Show certificate fingerprint (SHA-256)
openssl x509 -in server.crt -noout -fingerprint -sha256
# Verify certificate against a CA cert
openssl verify -CAfile ca.crt server.crt
# Verify against a chain (intermediate + root)
openssl verify -CAfile chain.pem server.crt
# Extract public key from certificate
openssl x509 -in server.crt -noout -pubkey > server-pubkey.pem
Terminal window
# Connect and show the presented certificate
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -text -noout
# Show just subject and issuer of live cert
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# See the full certificate chain presented
openssl s_client -connect example.com:443 -showcerts </dev/null 2>&1 \
| grep -E "^(subject=|issuer=|-----)"
# Check a non-HTTPS TLS service (SMTP STARTTLS example)
openssl s_client -connect mail.example.com:587 -starttls smtp </dev/null 2>/dev/null \
| openssl x509 -noout -subject -dates
# LDAPS (LDAP over TLS) - port 636
openssl s_client -connect ldap.example.com:636 </dev/null 2>/dev/null \
| openssl x509 -noout -subject -dates
# Test with specific TLS version
openssl s_client -connect example.com:443 -tls1_2 # force TLSv1.2
openssl s_client -connect example.com:443 -tls1_3 # force TLSv1.3
Terminal window
# List which ciphers the server accepts (nmap is more comprehensive)
nmap --script ssl-enum-ciphers -p 443 example.com
# Quick cipher test with openssl
# Test if specific cipher is accepted
openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' </dev/null
# Check if old/weak protocols are accepted (should fail if server is hardened)
openssl s_client -connect example.com:443 -ssl3 # SSLv3 - should fail
openssl s_client -connect example.com:443 -tls1 # TLS 1.0 - should fail
openssl s_client -connect example.com:443 -tls1_1 # TLS 1.1 - should fail
# Check OCSP stapling (certificate revocation)
openssl s_client -connect example.com:443 -status </dev/null 2>&1 | grep "OCSP"
# Look for "OCSP Response Status: successful" and "Cert Status: good"
# Full SSL/TLS assessment (use testssl.sh for comprehensive analysis)
bash testssl.sh example.com

Generating Keys and Certificate Signing Requests (CSRs)

Section titled “Generating Keys and Certificate Signing Requests (CSRs)”
Terminal window
# Generate an RSA 4096-bit private key (encrypted with AES-256)
openssl genrsa -aes256 -out server.key 4096
# Remove passphrase (for web servers that need to start without prompt)
openssl rsa -in server.key -out server.key.nopass
# Generate EC private key (P-256 curve - compact, fast)
openssl ecparam -name prime256v1 -genkey -noout -out ec-server.key
# Generate EC key on P-384 (stronger)
openssl ecparam -name secp384r1 -genkey -noout -out ec384-server.key
# Ed25519 key (modern; not supported everywhere yet)
openssl genpkey -algorithm Ed25519 -out ed25519.key
# List available EC curves
openssl ecparam -list_curves | grep -E "prime|secp384|X25519"

Generate a Certificate Signing Request (CSR)

Section titled “Generate a Certificate Signing Request (CSR)”
Terminal window
# Interactive CSR (prompts for CN, OU, O, etc.)
openssl req -new -key server.key -out server.csr
# Non-interactive CSR with all fields specified
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=California/L=San Francisco/O=ExampleCorp/OU=IT/CN=www.example.com"
# CSR with Subject Alternative Names (SANs) - required by modern browsers
cat > san.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = California
O = ExampleCorp
CN = www.example.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com
DNS.3 = api.example.com
IP.1 = 203.0.113.5
EOF
openssl req -new -key server.key -out server.csr -config san.cnf
# Verify the CSR contents
openssl req -in server.csr -text -noout

A local CA is used for internal services, development HTTPS, mutual TLS (mTLS), and testing.

Terminal window
# 1. Generate the CA private key and self-signed root certificate
openssl genrsa -aes256 -out ca.key 4096
openssl req -new -x509 -key ca.key -sha256 -days 3650 -out ca.crt \
-subj "/C=US/O=ExampleCorp Internal/CN=ExampleCorp Root CA"
# Inspect the CA certificate
openssl x509 -in ca.crt -text -noout | grep -E "Issuer|Subject|Not"
# 2. Generate server key and CSR
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr \
-subj "/C=US/O=ExampleCorp/CN=internal-api.example.com"
# 3. Sign the server certificate with the CA
# Extension file for server cert
cat > server-ext.cnf <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = internal-api.example.com
DNS.2 = localhost
IP.1 = 127.0.0.1
IP.2 = 10.0.0.50
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 \
-extfile server-ext.cnf
# 4. Verify the chain
openssl verify -CAfile ca.crt server.crt # should output: server.crt: OK
# 5. Bundle into full chain (for web servers)
cat server.crt ca.crt > fullchain.crt
# 6. Install the CA cert so your system trusts it (Ubuntu/Debian)
sudo cp ca.crt /usr/local/share/ca-certificates/examplecorp-root-ca.crt
sudo update-ca-certificates
# 7. Install CA cert in Firefox/Chrome (on Linux)
# Firefox: Settings → Privacy → Certificates → Import
# Chrome on Linux uses the OS cert store (after update-ca-certificates above)
Terminal window
# Client cert - identifies the client to the server
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr \
-subj "/C=US/O=ExampleCorp/[email protected]"
cat > client-ext.cnf <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature
extendedKeyUsage = clientAuth
EOF
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt -days 365 -sha256 \
-extfile client-ext.cnf
# Test mTLS connection with client cert
openssl s_client -connect api.example.com:443 \
-cert client.crt -key client.key \
-CAfile ca.crt

Terminal window
# Encrypt a file with AES-256-CBC (prompts for password)
openssl enc -aes-256-cbc -pbkdf2 -salt -in plaintext.txt -out encrypted.bin
# Decrypt
openssl enc -d -aes-256-cbc -pbkdf2 -in encrypted.bin -out decrypted.txt
# Encrypt with a specific key and IV (for scripting)
KEY=$(openssl rand -hex 32) # 256-bit key
IV=$(openssl rand -hex 16) # 128-bit IV
openssl enc -aes-256-cbc -K "$KEY" -iv "$IV" -in file.txt -out file.enc
echo "KEY=$KEY IV=$IV" # store these securely!

Asymmetric Encryption (RSA - for small data / key wrapping)

Section titled “Asymmetric Encryption (RSA - for small data / key wrapping)”
Terminal window
# Extract public key from an RSA private key
openssl rsa -in private.key -pubout -out public.pem
# Encrypt with recipient's public key (they decrypt with private key)
openssl rsautl -encrypt -inkey public.pem -pubin -in secret.txt -out secret.enc
# Decrypt with private key
openssl rsautl -decrypt -inkey private.key -in secret.enc -out secret.txt
# OAEP padding (more secure - use instead of default PKCS#1 v1.5)
openssl rsautl -encrypt -oaep -inkey public.pem -pubin -in secret.txt -out secret.enc
openssl rsautl -decrypt -oaep -inkey private.key -in secret.enc

Terminal window
# Compute file hash
openssl dgst -sha256 file.tar.gz
openssl dgst -sha512 file.tar.gz
openssl dgst -md5 file.tar.gz # MD5 - weak; only for legacy compatibility
# Verify against a known hash
echo "abc3d5... file.tar.gz" | sha256sum -c -
# Compute HMAC (keyed hash - proves both integrity and authenticity)
openssl dgst -sha256 -hmac "secretkey" file.tar.gz
# Generate random bytes
openssl rand -hex 32 # 32 random bytes as hex (e.g., for a secret key)
openssl rand -base64 24 # 24 random bytes as base64 (e.g., for a token)
openssl rand 16 > random.bin # raw binary
# Sign a file (proves the file came from the key holder)
openssl dgst -sha256 -sign private.key -out file.sig file.tar.gz
# Verify a signature
openssl dgst -sha256 -verify public.pem -signature file.sig file.tar.gz
# Output: Verified OK (or "Verification Failure")

Terminal window
# Decode a JWT payload (base64url → JSON)
echo "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIn0.xxx" \
| cut -d'.' -f2 \
| base64 -d 2>/dev/null | python3 -m json.tool
# Convert PEM to DER (binary format)
openssl x509 -in server.crt -outform DER -out server.der
# Convert DER back to PEM
openssl x509 -in server.der -inform DER -outform PEM -out server.pem
# Convert PEM private key to PKCS#8 format
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private.key -out private-pkcs8.key
# Create PKCS#12 (.pfx) bundle (cert + key - used by Windows and Java)
openssl pkcs12 -export -out bundle.pfx -inkey server.key -in server.crt -certfile ca.crt
# Extract from PKCS#12
openssl pkcs12 -in bundle.pfx -nocerts -nodes -out extracted.key # private key
openssl pkcs12 -in bundle.pfx -nokeys -out extracted.crt # certificates
# Decode a base64-encoded certificate
echo "LS0t..." | base64 -d | openssl x509 -text -noout

Terminal window
# Get the OCSP responder URL from a certificate
openssl x509 -in server.crt -noout -text | grep -A2 "OCSP"
# Manual OCSP check
# 1. Extract issuer cert from chain
openssl s_client -connect example.com:443 -showcerts </dev/null 2>&1 > chain.pem
# Extract individual certs from chain.pem (cert 0=server, cert 1=intermediate)
# 2. Get OCSP URI
OCSP_URI=$(openssl x509 -in server.crt -noout -text | grep "OCSP" | awk -F'URI:' '{print $2}')
# 3. Check revocation status
openssl ocsp -issuer issuer.crt -cert server.crt -url "$OCSP_URI" -resp_text
# Check revocation via CRL (Certificate Revocation List)
# Get CRL distribution point from cert
openssl x509 -in server.crt -noout -text | grep "CRL"
# Download and inspect CRL
curl -s "https://crl.example.com/issuer.crl" | openssl crl -inform DER -text -noout

Terminal window
# Generate a self-signed cert (dev/testing only)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -nodes \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
# Check cert expiry (exit 0 if valid, 1 if expired or within N days)
openssl x509 -in server.crt -checkend 0 # expired?
openssl x509 -in server.crt -checkend 2592000 # expires within 30 days? (2592000s)
# Check expiry of live server cert
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates
# Monitor multiple certs for expiry (shell loop)
for host in api.example.com www.example.com mail.example.com; do
echo -n "$host: "
echo | openssl s_client -connect "$host":443 -servername "$host" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null || echo "FAILED"
done