QCecuring - Enterprise Security Solutions

OpenSSL Complete Guide: Commands, Configuration & Troubleshooting

SSL/TLS 10 May, 2026 · 08 Mins read

Master OpenSSL with this comprehensive guide covering certificate generation, CSR creation, chain verification, TLS debugging, format conversion, and production hardening. Every command you'll ever need.


OpenSSL is the most widely deployed cryptography toolkit in the world. It powers TLS connections for web servers, generates certificates for enterprise PKI, debugs handshake failures at 3 AM, and converts between every certificate format ever invented. If you work with certificates, keys, or encrypted connections, you use OpenSSL.

This guide covers everything from basic commands to advanced production operations. It’s written for the practitioner who needs to get things done — not the academic who wants to understand the math.

Current versions: OpenSSL 4.0.0 (released April 2026), 3.6.x (current stable), 3.0.x LTS (EOL September 2026). OpenSSL 1.1.1 reached end-of-life in September 2023 — if you’re still on it, migrate now.


Check Your OpenSSL Version

Before anything else, know what you’re working with:

# Check installed version
openssl version

# Detailed version with build options
openssl version -a

# Show the default configuration directory
openssl version -d
# Output: OPENSSLDIR: "/etc/ssl" (Linux) or "/usr/local/etc/openssl@3" (macOS Homebrew)

Generate Private Keys

RSA Keys

# Generate 4096-bit RSA private key (recommended for external-facing)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:4096

# Generate 2048-bit RSA key (minimum acceptable)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048

# Legacy command (still works, but genpkey is preferred)
openssl genrsa -out server.key 4096

# Generate RSA key with passphrase protection
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:4096 -aes256

ECDSA Keys (Faster TLS Handshakes)

# Generate ECDSA P-256 key (equivalent to RSA 3072, much faster)
openssl genpkey -algorithm EC -out server-ec.key -pkeyopt ec_paramgen_curve:P-256

# Generate ECDSA P-384 key (stronger, slightly slower)
openssl genpkey -algorithm EC -out server-ec.key -pkeyopt ec_paramgen_curve:P-384

# Legacy command
openssl ecparam -genkey -name prime256v1 -out server-ec.key

Ed25519 Keys (Modern, Fast)

# Generate Ed25519 key (best for SSH, internal services)
openssl genpkey -algorithm ED25519 -out server-ed25519.key

Which to choose:

  • RSA 4096 — external-facing services, maximum compatibility
  • ECDSA P-256 — internal services where performance matters (3x faster handshake than RSA 4096)
  • Ed25519 — SSH keys, internal signing (not yet widely supported for TLS certificates from public CAs)

Generate Certificate Signing Requests (CSR)

Basic CSR

# Generate CSR from existing private key
openssl req -new -key server.key -out server.csr \
  -subj "/C=US/ST=California/L=San Francisco/O=Acme Corp/OU=Engineering/CN=api.acme.com"

CSR with Subject Alternative Names (SANs)

SANs are mandatory — CN-only certificates are deprecated by all major browsers and CAs.

# Generate key + CSR with SANs in one command
openssl req -new -newkey rsa:4096 -nodes -keyout server.key -out server.csr \
  -subj "/C=US/ST=California/O=Acme Corp/CN=api.acme.com" \
  -addext "subjectAltName=DNS:api.acme.com,DNS:api-v2.acme.com,DNS:*.internal.acme.com,IP:10.0.1.50"

For OpenSSL versions that don’t support -addext, use a config file:

# Create openssl-san.cnf
cat > openssl-san.cnf << 'EOF'
[req]
default_bits = 4096
prompt = no
distinguished_name = dn
req_extensions = v3_req

[dn]
C = US
ST = California
L = San Francisco
O = Acme Corp
CN = api.acme.com

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = api.acme.com
DNS.2 = api-v2.acme.com
DNS.3 = *.internal.acme.com
IP.1 = 10.0.1.50
EOF

# Generate CSR using config
openssl req -new -key server.key -out server.csr -config openssl-san.cnf

Verify CSR Contents

# View CSR details (verify SANs are included)
openssl req -in server.csr -noout -text

# Verify CSR signature is valid
openssl req -in server.csr -verify -noout

Self-Signed Certificates (Development & Testing)

Quick Self-Signed Certificate

# Generate key + self-signed cert in one command (valid 365 days)
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout dev.key -out dev.crt -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,DNS:*.local.dev,IP:127.0.0.1"

Build a Private CA (Root + Intermediate)

For development environments or internal PKI:

# 1. Generate Root CA key (keep this OFFLINE in production)
openssl genpkey -algorithm RSA -out root-ca.key -pkeyopt rsa_keygen_bits:4096

# 2. Create self-signed Root CA certificate (valid 10 years)
openssl req -x509 -new -key root-ca.key -out root-ca.crt -days 3650 \
  -subj "/C=US/O=Acme Corp/CN=Acme Root CA" \
  -addext "basicConstraints=critical,CA:TRUE" \
  -addext "keyUsage=critical,keyCertSign,cRLSign"

# 3. Generate Intermediate CA key
openssl genpkey -algorithm RSA -out intermediate-ca.key -pkeyopt rsa_keygen_bits:4096

# 4. Create Intermediate CA CSR
openssl req -new -key intermediate-ca.key -out intermediate-ca.csr \
  -subj "/C=US/O=Acme Corp/CN=Acme Intermediate CA"

# 5. Sign Intermediate CA with Root CA (valid 5 years)
openssl x509 -req -in intermediate-ca.csr -CA root-ca.crt -CAkey root-ca.key \
  -CAcreateserial -out intermediate-ca.crt -days 1825 \
  -extfile <(printf "basicConstraints=critical,CA:TRUE,pathlen:0\nkeyUsage=critical,keyCertSign,cRLSign")

# 6. Sign a server certificate with Intermediate CA
openssl x509 -req -in server.csr -CA intermediate-ca.crt -CAkey intermediate-ca.key \
  -CAcreateserial -out server.crt -days 365 \
  -extfile <(printf "subjectAltName=DNS:api.acme.com,DNS:*.api.acme.com\nextendedKeyUsage=serverAuth")

Warning: Never use self-signed CAs in production without proper key ceremony procedures. For production internal PKI, see our guide on PKI hierarchy design.


Debug TLS Connections

The single most useful OpenSSL command for troubleshooting:

Connect and Show Certificate

# Connect to a server and display the full certificate chain
openssl s_client -connect api.acme.com:443 -servername api.acme.com -showcerts

# Show only the server certificate details
echo | openssl s_client -connect api.acme.com:443 -servername api.acme.com 2>/dev/null | \
  openssl x509 -noout -text

# Check certificate expiry dates
echo | openssl s_client -connect api.acme.com:443 -servername api.acme.com 2>/dev/null | \
  openssl x509 -noout -dates

# Check SANs (Subject Alternative Names)
echo | openssl s_client -connect api.acme.com:443 -servername api.acme.com 2>/dev/null | \
  openssl x509 -noout -ext subjectAltName

Critical: Always use -servername for SNI. Without it, you may get the wrong certificate on shared hosting or CDNs.

Test Specific TLS Versions

# Test TLS 1.3 support
openssl s_client -connect api.acme.com:443 -tls1_3

# Test TLS 1.2 support
openssl s_client -connect api.acme.com:443 -tls1_2

# Test with specific cipher suite
openssl s_client -connect api.acme.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384'

# List available TLS 1.3 cipher suites
openssl ciphers -v -tls1_3

Check OCSP Stapling

# Verify OCSP stapling is working
openssl s_client -connect api.acme.com:443 -servername api.acme.com -status 2>/dev/null | \
  grep -A 5 "OCSP Response"

Verify Certificate Chains

# Verify a certificate against a CA bundle
openssl verify -CAfile ca-bundle.crt server.crt

# Verify with intermediate certificate
openssl verify -CAfile root-ca.crt -untrusted intermediate-ca.crt server.crt

# Verify against system trust store
openssl verify server.crt

# Check if private key matches certificate
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5
# If both MD5 hashes match → key and cert are a pair

# Check if CSR matches private key
openssl req -noout -modulus -in server.csr | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5

Convert Between Certificate Formats

Flowchart showing top-down process flow

PEM ↔ DER

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

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

PEM ↔ PKCS#12 (PFX)

# Create PFX bundle (key + cert + chain)
openssl pkcs12 -export -out bundle.pfx \
  -inkey server.key -in server.crt -certfile ca-chain.crt \
  -passout pass:YourPassword

# Extract everything from PFX
openssl pkcs12 -in bundle.pfx -out everything.pem -nodes -passin pass:YourPassword

# Extract only the private key
openssl pkcs12 -in bundle.pfx -nocerts -out key.pem -nodes

# Extract only the certificates
openssl pkcs12 -in bundle.pfx -nokeys -out certs.pem

# Extract only the client certificate (no CA certs)
openssl pkcs12 -in bundle.pfx -nokeys -clcerts -out client.pem

Key Format Conversions

# Convert PKCS#8 key to traditional RSA format
openssl rsa -in pkcs8.key -out traditional.key

# Convert traditional RSA to PKCS#8
openssl pkcs8 -topk8 -in traditional.key -out pkcs8.key -nocrypt

# Remove passphrase from private key
openssl rsa -in encrypted.key -out decrypted.key

# Add passphrase to private key
openssl rsa -in decrypted.key -out encrypted.key -aes256

Certificate Inspection

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

# View specific fields
openssl x509 -in cert.pem -noout -subject
openssl x509 -in cert.pem -noout -issuer
openssl x509 -in cert.pem -noout -dates
openssl x509 -in cert.pem -noout -serial
openssl x509 -in cert.pem -noout -fingerprint -sha256
openssl x509 -in cert.pem -noout -ext subjectAltName
openssl x509 -in cert.pem -noout -ext keyUsage
openssl x509 -in cert.pem -noout -ext extendedKeyUsage

# Check if certificate expires within 30 days
openssl x509 -in cert.pem -noout -checkend 2592000
# Exit code 0 = still valid, Exit code 1 = expires within 30 days

# View certificate purpose (what it can be used for)
openssl x509 -in cert.pem -noout -purpose

Cipher Suite Management

# List all available cipher suites
openssl ciphers -v 'ALL'

# List only TLS 1.3 suites
openssl ciphers -v -tls1_3

# List only HIGH security suites
openssl ciphers -v 'HIGH:!aNULL:!MD5'

# Test if a specific cipher is supported
openssl s_client -connect api.acme.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384'

# Recommended cipher string for 2026
# TLS 1.3 (automatic, no configuration needed — always uses AEAD)
# TLS 1.2 (if you must support it):
# ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384

Production Hardening Checklist

CheckCommandExpected Result
Key length ≥ 2048openssl rsa -in key.pem -text -noout | head -1”Private-Key: (4096 bit)“
No SHA-1 signaturesopenssl x509 -in cert.pem -noout -text | grep "Signature Algorithm"sha256WithRSA or better
Valid chainopenssl verify -CAfile chain.pem cert.pem”cert.pem: OK”
SANs presentopenssl x509 -in cert.pem -noout -ext subjectAltNameDNS names listed
Not expiredopenssl x509 -in cert.pem -noout -checkend 2592000Exit code 0
OCSP staplingopenssl s_client -connect host:443 -status”OCSP Response Status: successful”
No weak ciphersopenssl s_client -connect host:443 -cipher 'RC4:DES:3DES'Handshake fails (good)
TLS 1.3 supportedopenssl s_client -connect host:443 -tls1_3”Protocol: TLSv1.3”

Certificate Expiry Monitoring Script

#!/bin/bash
# check-cert-expiry.sh — Alert if any cert expires within N days
# Usage: ./check-cert-expiry.sh 30

THRESHOLD_DAYS=${1:-30}
THRESHOLD_SECS=$((THRESHOLD_DAYS * 86400))

DOMAINS=(
  "api.acme.com"
  "app.acme.com"
  "auth.acme.com"
  "mail.acme.com"
)

echo "Checking certificate expiry (threshold: ${THRESHOLD_DAYS} days)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

EXIT_CODE=0

for domain in "${DOMAINS[@]}"; do
  expiry=$(echo | openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | \
    openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)

  if [ -z "$expiry" ]; then
    echo "❌ $domain — CANNOT CONNECT"
    EXIT_CODE=1
    continue
  fi

  expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$expiry" +%s 2>/dev/null)
  now_epoch=$(date +%s)
  remaining=$((expiry_epoch - now_epoch))
  days_left=$((remaining / 86400))

  if [ $remaining -lt 0 ]; then
    echo "🔴 $domain — EXPIRED ($expiry)"
    EXIT_CODE=1
  elif [ $remaining -lt $THRESHOLD_SECS ]; then
    echo "🟡 $domain — EXPIRES IN $days_left DAYS ($expiry)"
    EXIT_CODE=1
  else
    echo "✅ $domain — OK ($days_left days remaining)"
  fi
done

exit $EXIT_CODE

OpenSSL 4.0 / 3.x vs 1.1.1: Key Differences

FeatureOpenSSL 1.1.1 (EOL)OpenSSL 3.x / 4.0
Provider architectureEngine APIProvider API (modular crypto)
FIPS moduleSeparate buildLoadable FIPS provider
Deprecated algorithmsAll availableMD5, DES, RC4 disabled by default
Default security levelLevel 1Level 2 (112-bit minimum)
API changesLow-level APIs availableMany low-level APIs removed
PerformanceBaselineImproved (especially ECDSA)
PQC supportNoneExperimental (OpenSSL 4.0)

If you’re still on 1.1.1, your system is running unpatched software with known vulnerabilities. Upgrade immediately.

# Check if you're on an EOL version
openssl version
# If output shows "1.1.1" — you need to upgrade

Common Errors and Fixes

”unable to get local issuer certificate"

# Cause: Missing intermediate CA in the chain
# Diagnosis:
openssl s_client -connect api.acme.com:443 -servername api.acme.com

# Fix: Concatenate cert + intermediate(s) in order
cat server.crt intermediate.crt > fullchain.crt
# Configure web server to use fullchain.crt

"certificate verify failed"

# Cause: CA not in trust store, or chain incomplete
# Fix 1: Specify CA bundle explicitly
openssl s_client -connect api.acme.com:443 -CAfile /path/to/ca-bundle.crt

# Fix 2: Update system CA certificates
# Ubuntu/Debian:
sudo update-ca-certificates
# RHEL/CentOS:
sudo update-ca-trust

"key values mismatch"

# Cause: Private key doesn't match the certificate
# Diagnosis: Compare modulus hashes
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
# If they don't match → wrong key for this cert

"PEM_read_bio_X509: no start line”

# Cause: File is DER format but OpenSSL expects PEM, or file is corrupted
# Fix: Convert DER to PEM
openssl x509 -in cert.der -inform der -out cert.pem -outform pem

# Or check if file has Windows line endings
file cert.pem
# Fix: dos2unix cert.pem

FAQ

Q: What’s the difference between openssl genrsa and openssl genpkey?

genpkey is the modern, unified command that works for all algorithm types (RSA, EC, Ed25519). genrsa is the legacy RSA-only command. Use genpkey for new work — it’s more consistent and supports newer algorithms.

Q: Should I use RSA or ECDSA for my certificates?

For public-facing web servers in 2026: ECDSA P-256 gives you equivalent security to RSA 3072 with 3x faster handshakes. Use RSA 4096 only if you need compatibility with very old clients. See our RSA vs ECC comparison for details.

Q: How do I check if my private key has a passphrase?

openssl rsa -in key.pem -check -noout
# If it prompts for a passphrase → it's encrypted
# If it says "RSA key ok" immediately → no passphrase

Q: Is OpenSSL 3.x backward compatible with 1.1.1?

For CLI usage: mostly yes. Your certificate generation and inspection commands work the same. The main differences: deprecated algorithms (MD5, RC4, DES) are disabled by default, the engine API is replaced by providers, and some low-level C APIs were removed. For command-line operations, your scripts should work unchanged.

Q: How do I test if a server supports a specific TLS version?

openssl s_client -connect host:443 -tls1_3    # Test TLS 1.3
openssl s_client -connect host:443 -tls1_2    # Test TLS 1.2
# If successful → "Protocol: TLSv1.3" in output
# If not supported → handshake failure

Q: What key size should I use in 2026?

  • RSA: 3072-bit minimum, 4096-bit recommended for long-lived certs
  • ECDSA: P-256 (equivalent to RSA 3072) or P-384 for higher security
  • Ed25519: Ideal for SSH keys and internal services
  • Post-quantum: Start testing ML-KEM + ML-DSA hybrid modes for future readiness

Related Reading:

Free Certificate Decoder

Paste any PEM certificate and instantly see expiry, SANs, issuer, key type, and chain status.

Decode a Certificate

Related Insights

CLM

How to Automate Certificate Renewal with ACME Protocol: A Practical Guide

ACME automates TLS certificate issuance and renewal without human intervention. Here's how to set it up with Certbot, acme.sh, and cert-manager — with real configs for Nginx, Apache, and Kubernetes.

By Ayush kumar rai

03 May, 2026 · 06 Mins read

CLMDevOpsPKI

Code signing

Code Signing and Software Supply Chain Security: A Complete Guide

Code signing proves software authenticity and integrity. Here's how to implement it across CI/CD pipelines, protect signing keys, and defend against supply chain attacks like SolarWinds and xz-utils.

By Ayush kumar rai

20 Mar, 2026 · 05 Mins read

Code signingDevopsSecurity

Pki

Kubernetes Certificate Management: cert-manager, Service Mesh, and Beyond

Kubernetes uses certificates at every layer — cluster infrastructure, ingress, and service-to-service. Here's how to manage them all with cert-manager, Istio, and proper monitoring to prevent outages.

By Mounith reddy

15 Mar, 2026 · 04 Mins read

PkiDevopsCloud

Ready to Secure Your Enterprise?

Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.

Stay ahead on cryptography & PKI

Get monthly insights on certificate management, post-quantum readiness, and enterprise security. No spam.

We respect your privacy. Unsubscribe anytime.