SSL/TLS & OpenSSL Cheatsheet

Quick reference for OpenSSL, certificate operations, PKI setup, TLS debugging, format conversions, and cert-manager on Kubernetes.

Quick Reference

Certificate Inspection

# View full certificate
openssl x509 -in cert.pem -text -noout

# View specific fields
openssl x509 -in cert.pem -subject -noout
openssl x509 -in cert.pem -issuer -noout
openssl x509 -in cert.pem -dates -noout
openssl x509 -in cert.pem -fingerprint -sha256 -noout
openssl x509 -in cert.pem -modulus -noout | md5sum

# View SANs (Subject Alternative Names)
openssl x509 -in cert.pem -text -noout \
  | grep -A1 "Subject Alternative Name"

# Check expiry
openssl x509 -in cert.pem -noout -enddate
# Script: check if expires within 30 days
openssl x509 -checkend 2592000 -noout -in cert.pem
echo $?   # 0=valid, 1=expires soon

Generate Keys & CSR

# Generate RSA private key
openssl genrsa -out server.key 4096
openssl genrsa -aes256 -out server.key 4096   # With passphrase

# Generate ECDSA private key
openssl ecparam -name prime256v1 -genkey -noout -out ec.key
openssl ecparam -name secp384r1 -genkey -noout -out ec.key

# Generate CSR from existing key
openssl req -new -key server.key -out server.csr

# Generate key + CSR in one command
openssl req -newkey rsa:4096 -keyout server.key \
  -out server.csr -nodes \
  -subj "/C=VN/ST=Hanoi/L=Hanoi/O=MyCompany/CN=example.com"

# View CSR contents
openssl req -in server.csr -text -noout
openssl req -in server.csr -verify

# Self-signed certificate (for testing)
openssl req -x509 -newkey rsa:4096 -keyout key.pem \
  -out cert.pem -days 365 -nodes \
  -subj "/CN=localhost"

Certificate Operations

# Sign CSR with your CA
openssl x509 -req -in server.csr \
  -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out server.crt \
  -days 365 -sha256 \
  -extfile v3.ext        # Include extensions

# Convert PEM to DER
openssl x509 -in cert.pem -outform DER -out cert.der

# Convert DER to PEM
openssl x509 -in cert.der -inform DER -out cert.pem

# Convert PEM to PFX/PKCS12
openssl pkcs12 -export \
  -out certificate.pfx \
  -inkey server.key \
  -in server.crt \
  -certfile ca-chain.crt

# Extract cert from PFX
openssl pkcs12 -in certificate.pfx -nokeys -out cert.pem
openssl pkcs12 -in certificate.pfx -nocerts -nodes -out key.pem

# Bundle full chain
cat server.crt intermediate.crt root.crt > fullchain.pem

Verify & Test

# Verify cert is signed by CA
openssl verify -CAfile ca.crt server.crt
openssl verify -CAfile ca-chain.pem server.crt

# Verify cert matches private key (same modulus)
openssl x509 -noout -modulus -in server.crt | md5sum
openssl rsa -noout -modulus -in server.key | md5sum
# Both must produce same MD5 hash

# Test TLS connection
openssl s_client -connect example.com:443

# With SNI (Server Name Indication — required for vhosts)
openssl s_client -connect example.com:443 \
  -servername example.com

# Show full chain
openssl s_client -connect example.com:443 \
  -servername example.com -showcerts

# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

Remote Certificate Checks

# Check expiry of remote cert (one-liner)
echo | openssl s_client -connect example.com:443 \
  -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

# Get cert chain from server
openssl s_client -connect example.com:443 \
  -servername example.com \
  -showcerts 2>/dev/null | grep -E "BEGIN|END CERT"

# Download remote cert to file
echo | openssl s_client \
  -connect example.com:443 \
  -servername example.com 2>/dev/null \
  | openssl x509 > remote.crt

# Test cipher support
nmap --script ssl-enum-ciphers -p 443 example.com

# Check OCSP stapling
openssl s_client -connect example.com:443 \
  -servername example.com -status 2>/dev/null \
  | grep -A 10 "OCSP response:"

STARTTLS & Other Protocols

# SMTP STARTTLS
openssl s_client -connect smtp.gmail.com:587 \
  -starttls smtp

# IMAP STARTTLS
openssl s_client -connect mail.example.com:143 \
  -starttls imap

# LDAP STARTTLS
openssl s_client -connect ldap.example.com:389 \
  -starttls ldap

# Test specific cipher
openssl s_client -connect example.com:443 \
  -cipher ECDHE-RSA-AES256-GCM-SHA384

# Check if TLS 1.0/1.1 is disabled (should fail)
openssl s_client -connect example.com:443 -tls1
openssl s_client -connect example.com:443 -tls1_1

# curl TLS debug
curl -v --tls-max 1.2 https://example.com
curl -v --tlsv1.3 https://example.com

Complete PKI Setup: Root CA → Intermediate CA → Leaf Cert

Best Practice: Keep the Root CA offline (air-gapped). Use the Intermediate CA for day-to-day signing. If the Intermediate CA is compromised, revoke it and create a new one — the Root CA remains trusted.

Step 1 — Create Root CA

# Create directory structure
mkdir -p pki/{root-ca,intermediate-ca,certs}/{certs,crl,newcerts,private}
chmod 700 pki/root-ca/private pki/intermediate-ca/private
touch pki/root-ca/index.txt pki/intermediate-ca/index.txt
echo 1000 > pki/root-ca/serial
echo 1000 > pki/intermediate-ca/serial

# Generate Root CA key (4096-bit RSA, passphrase protected)
openssl genrsa -aes256 -out pki/root-ca/private/ca.key.pem 4096
chmod 400 pki/root-ca/private/ca.key.pem

# Create Root CA certificate
openssl req -config pki/openssl-root.cnf \
  -key pki/root-ca/private/ca.key.pem \
  -new -x509 -days 7300 -sha256 \
  -extensions v3_ca \
  -out pki/root-ca/certs/ca.cert.pem

# openssl-root.cnf [v3_ca] section:
# subjectKeyIdentifier = hash
# authorityKeyIdentifier = keyid:always,issuer
# basicConstraints = critical, CA:true
# keyUsage = critical, digitalSignature, cRLSign, keyCertSign

Step 2 — Create Intermediate CA

# Generate Intermediate CA key
openssl genrsa -aes256 \
  -out pki/intermediate-ca/private/intermediate.key.pem 4096
chmod 400 pki/intermediate-ca/private/intermediate.key.pem

# Create Intermediate CA CSR
openssl req -config pki/openssl-intermediate.cnf \
  -new -sha256 \
  -key pki/intermediate-ca/private/intermediate.key.pem \
  -out pki/intermediate-ca/csr/intermediate.csr.pem

# Sign with Root CA
openssl ca -config pki/openssl-root.cnf \
  -extensions v3_intermediate_ca \
  -days 3650 -notext -md sha256 \
  -in pki/intermediate-ca/csr/intermediate.csr.pem \
  -out pki/intermediate-ca/certs/intermediate.cert.pem

# Create certificate chain bundle
cat pki/intermediate-ca/certs/intermediate.cert.pem \
    pki/root-ca/certs/ca.cert.pem \
    > pki/intermediate-ca/certs/ca-chain.cert.pem

# [v3_intermediate_ca] section in openssl-root.cnf:
# subjectKeyIdentifier = hash
# authorityKeyIdentifier = keyid:always,issuer
# basicConstraints = critical, CA:true, pathlen:0
# keyUsage = critical, digitalSignature, cRLSign, keyCertSign

Step 3 — Issue Leaf (Server) Certificate

# Generate server key
openssl genrsa \
  -out pki/certs/example.com.key.pem 2048

# Create v3.ext for SAN (REQUIRED — CN alone is deprecated)
cat > /tmp/v3.ext <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
IP.1 = 192.168.1.100
EOF

# Create CSR
openssl req -new -sha256 \
  -key pki/certs/example.com.key.pem \
  -subj "/C=VN/ST=Hanoi/O=MyCompany/CN=example.com" \
  -out pki/certs/example.com.csr.pem

# Sign with Intermediate CA
openssl x509 -req \
  -in pki/certs/example.com.csr.pem \
  -CA pki/intermediate-ca/certs/intermediate.cert.pem \
  -CAkey pki/intermediate-ca/private/intermediate.key.pem \
  -CAcreateserial \
  -out pki/certs/example.com.cert.pem \
  -days 365 -sha256 \
  -extfile /tmp/v3.ext

# Verify the issued cert
openssl verify \
  -CAfile pki/intermediate-ca/certs/ca-chain.cert.pem \
  pki/certs/example.com.cert.pem

Subject Alternative Names (SAN)

Important: Modern browsers and clients ignore the Common Name (CN) field and require Subject Alternative Names. Always include SANs — a cert with only CN will fail validation in Chrome, Firefox, and most TLS clients since 2017.
# openssl.cnf with SAN section
[req]
default_bits       = 2048
prompt             = no
default_md         = sha256
req_extensions     = req_ext
distinguished_name = dn

[dn]
C  = VN
ST = Hanoi
L  = Hanoi
O  = MyCompany
OU = IT
CN = example.com

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1   = example.com
DNS.2   = www.example.com
DNS.3   = *.example.com       # Wildcard
IP.1    = 10.0.0.1
email.1 = [email protected]

# Generate with SAN config
openssl req -newkey rsa:2048 -nodes \
  -keyout server.key \
  -out server.csr \
  -config openssl.cnf

# Verify SANs in CSR
openssl req -text -noout -in server.csr | grep -A 5 "Subject Alternative"

Certificate Format Conversions

# Format summary:
# PEM  — Base64 encoded, -----BEGIN CERTIFICATE----- header, most common on Linux
# DER  — Binary format, used in Java and Windows environments
# PFX/PKCS12 — Binary, bundles cert + key + chain, used in Windows/IIS
# PKCS7/P7B  — Bundle of certs (no private key), used in Windows cert import
# JKS  — Java KeyStore, used in Java applications (Tomcat, etc.)

# PEM <-> DER
openssl x509 -in cert.pem -outform DER -out cert.der
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem

# PEM -> PFX/PKCS12
openssl pkcs12 -export \
  -out bundle.pfx \
  -inkey server.key \
  -in server.crt \
  -certfile ca-chain.crt \
  -name "My Certificate"

# PFX/PKCS12 -> PEM
openssl pkcs12 -in bundle.pfx -nodes -out all.pem          # All in one file
openssl pkcs12 -in bundle.pfx -nokeys -out cert.pem        # Cert only
openssl pkcs12 -in bundle.pfx -nocerts -nodes -out key.pem # Key only
openssl pkcs12 -in bundle.pfx -nokeys -cacerts -out chain.pem  # CA chain only

# PEM -> PKCS7/P7B
openssl crl2pkcs7 -nocrl \
  -certfile server.crt \
  -certfile ca-chain.crt \
  -out bundle.p7b

# PKCS7 -> PEM
openssl pkcs7 -in bundle.p7b -print_certs -out certs.pem

# JKS -> PFX (requires Java keytool)
keytool -importkeystore \
  -srckeystore keystore.jks \
  -destkeystore keystore.pfx \
  -deststoretype pkcs12

# PFX -> JKS
keytool -importkeystore \
  -srckeystore bundle.pfx -srcstoretype pkcs12 \
  -destkeystore keystore.jks -deststoretype jks

Cipher Suites

# List all available ciphers in OpenSSL
openssl ciphers -v 'ALL'

# List strong ciphers only (TLS 1.2+)
openssl ciphers -v 'ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL:!MD5:!DSS'

# List TLS 1.3 ciphersuites
openssl ciphers -v -tls1_3

# Test what ciphers a server supports
nmap --script ssl-enum-ciphers -p 443 example.com

# Nginx: restrict to strong ciphers only
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# ssl_prefer_server_ciphers off;

# Apache: restrict ciphers
# SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...

cert-manager on Kubernetes

# List all certificates
kubectl get certificates -A
kubectl get cert -A

# Describe a certificate (check conditions, last renewal)
kubectl describe certificate myapp-tls -n production

# Check certificate secret
kubectl get secret myapp-tls -n production -o yaml
kubectl get secret myapp-tls -n production \
  -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout

# Check CertificateRequests
kubectl get certificaterequests -A
kubectl describe certificaterequest -n production

# Trigger manual renewal
kubectl annotate certificate myapp-tls \
  cert-manager.io/issuer-name- \
  -n production

# Or use cmctl (cert-manager CLI)
cmctl renew myapp-tls -n production
cmctl status certificate myapp-tls -n production

# Check ClusterIssuers and Issuers
kubectl get clusterissuer
kubectl get issuer -A
kubectl describe clusterissuer letsencrypt-prod

# Example Certificate resource
# apiVersion: cert-manager.io/v1
# kind: Certificate
# metadata:
#   name: myapp-tls
#   namespace: production
# spec:
#   secretName: myapp-tls
#   issuerRef:
#     name: letsencrypt-prod
#     kind: ClusterIssuer
#   dnsNames:
#     - example.com
#     - www.example.com
#   duration: 2160h   # 90 days
#   renewBefore: 360h # Renew 15 days before expiry

Back to Documents