Open-source cryptographic toolkit for TLS/SSL, certificate management, and general-purpose encryption operations.
Addresses below are RFC 5737 documentation ranges or placeholders - swap in your own.
Table of Contents#
- Overview
- Installation
- Key Generation
- Certificate Signing Requests
- Self-Signed Certificates
- Certificate Inspection
- Certificate Bundling and Conversion
- Certificate Formats
- Certificate Authorities
- OCSP Stapling
- Let's Encrypt with Certbot
- x509 Client Authentication with CRL
- Troubleshooting
- See Also
- Sources
1. Overview#
OpenSSL is an open-source toolkit implementing the SSL and TLS protocols along with a full-strength general-purpose cryptography library. It provides:
- Private key and certificate signing request (CSR) generation
- Certificate signing, including self-signed certificates
- Data encryption and decryption
- Message digest (hash) computation
- S/MIME signing and encryption
- OCSP, CRL, and certificate chain validation
- TLS client and server testing via
s_clientands_server
OpenSSL ships with most Linux distributions and is the foundation for TLS in Apache, Nginx, and many other server applications.
Hash algorithm note: SHA-1 is fully deprecated for certificate signatures. All modern CAs and browsers reject SHA-1 signed certificates. Always use SHA-256 or stronger (-sha256, -sha384, -sha512).
2. Installation#
Debian/Ubuntu#
sudo apt-get update && sudo apt-get install opensslRHEL/Fedora#
sudo dnf install opensslArch Linux#
sudo pacman -S opensslVerify Installation#
openssl version -a3. Key Generation#
RSA Keys#
Generate a 4096-bit RSA private key:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out server.keyNote: RSA 2048-bit keys are considered weak for long-term use. Use 4096-bit RSA for maximum compatibility, or prefer ECDSA/Ed25519 for better performance.
ECDSA Keys#
Generate an ECDSA key using the P-256 curve (broadly compatible):
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out server-ecdsa.keyFor higher security, use P-384:
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out server-ecdsa384.keyEd25519 Keys#
Generate an Ed25519 key (simplest, no curve selection needed):
openssl genpkey -algorithm ED25519 -out server-ed25519.keyNote: ECDSA and Ed25519 offer equivalent or better security with shorter keys and faster operations than RSA. Ed25519 has growing but not universal client support. ECDSA P-256 has the broadest compatibility among elliptic curve algorithms.
Protecting Private Keys#
Encrypt a private key with AES-256:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -aes256 -out server-encrypted.keyRemove passphrase from a protected key:
openssl rsa -in server-encrypted.key -out server.key4. Certificate Signing Requests#
CSR Configuration File#
Create a req.conf file to specify CSR details:
[req]
default_bits = 4096
default_md = sha256
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = AT
ST = Vienna
L = Vienna
O = Example Organisation AG
CN = www.example.com
[v3_req]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com
IP.1 = 192.0.2.10Generate CSR from Existing Key#
openssl req -new -key server.key -out server.csr -config req.confGenerate Key and CSR Together#
openssl req -new -newkey rsa:4096 -nodes -keyout server.key -out server.csr -config req.confVerify CSR Contents#
openssl req -in server.csr -text -noout -verifyCSR Submission#
After generating the CSR, either:
- Submit to a CA: Send
server.csrto a Certificate Authority (e.g., Let's Encrypt, DigiCert, Sectigo). The CA verifies the information and issues a signed certificate. - Self-sign: Use the CSR to generate a self-signed certificate (see next section).
5. Self-Signed Certificates#
Quick Self-Signed Certificate#
Generate a key and self-signed certificate in one command:
openssl req -new -x509 -sha256 -newkey rsa:4096 -nodes \
-keyout server.key -out server.crt -days 365 -config req.confSelf-Sign from Existing CSR#
openssl x509 -req -sha256 -in server.csr -signkey server.key \
-out server.crt -days 365 -extfile req.conf -extensions v3_reqSelf-Signed with SAN (Subject Alternative Name)#
Modern browsers require SAN entries. Without them, certificate validation fails even if CN matches:
openssl req -new -x509 -sha256 -newkey rsa:4096 -nodes \
-keyout server.key -out server.crt -days 365 \
-subj "/C=AT/ST=Vienna/L=Vienna/O=Example/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com"6. Certificate Inspection#
Inspect a Local Certificate#
openssl x509 -in certificate.crt -text -nooutInspect a Remote Server Certificate (with SNI)#
echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null \
| openssl x509 -text -nooutCheck Certificate Expiration#
openssl x509 -in certificate.crt -noout -enddateCheck Remote Certificate Expiration#
echo | openssl s_client -servername <hostname> -connect <hostname>:443 2>/dev/null \
| openssl x509 -noout -datesInspect a Private Key#
openssl rsa -in private.key -text -nooutFor EC keys:
openssl ec -in private-ec.key -text -nooutInspect a CSR#
openssl req -in request.csr -text -nooutVerify Certificate Chain#
openssl verify -CAfile ca-bundle.crt certificate.crtVerify Key Matches Certificate#
Compare the modulus of the key and certificate (they must match):
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5Test TLS Connection#
openssl s_client -connect <hostname>:443 -servername <hostname> -tls1_37. Certificate Bundling and Conversion#
P12/PFX File#
Create a PKCS#12 bundle containing private key, certificate, and CA chain:
openssl pkcs12 -export -out server.p12 \
-inkey server.key -in server.crt \
-certfile ca-bundle.crt -name "example_com"Extract certificate from P12:
openssl pkcs12 -in server.p12 -nokeys -out certificate.pemExtract private key from P12:
openssl pkcs12 -in server.p12 -nocerts -nodes -out private.keyJKS File (Java KeyStore)#
Convert P12 to JKS:
keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 \
-destkeystore server.jks -deststoretype JKSPEM Concatenation#
Create a combined PEM file (certificate + key):
cat server.crt server.key > server.pemCreate a full chain PEM (certificate + intermediate + root):
cat server.crt intermediate.crt root.crt > fullchain.pemDER to PEM Conversion#
openssl x509 -inform DER -in certificate.der -out certificate.pemPEM to DER Conversion#
openssl x509 -outform DER -in certificate.pem -out certificate.der8. Certificate Formats#
| Format | Encoding | Extension | Description |
|---|---|---|---|
| PEM | Base64 | .pem, .crt, .cer, .key | Most common on Linux; can contain multiple certificates and keys |
| DER | Binary | .der, .cer | Single certificate; common in Java and Windows |
| PFX/P12 | Binary | .pfx, .p12 | Bundles certificate + key + chain in one encrypted file |
| JKS | Binary | .jks | Java-specific keystore format (being replaced by PKCS12) |
| PKCS#7 | Base64 | .p7b, .p7c | Certificate chain without private key; used by Windows and Java |
9. Certificate Authorities#
Public CAs#
- Let's Encrypt - Free, automated DV certificates (90-day validity)
- DigiCert - Commercial OV/EV certificates
- Sectigo (Comodo) - Commercial DV/OV/EV certificates
- GlobalSign - Commercial certificates with broad trust
Certificate Validation Levels#
| Level | Abbreviation | Validation | Use Case |
|---|---|---|---|
| Domain Validation | DV | Domain ownership only | Basic encryption |
| Organization Validation | OV | Domain + organization identity | Business websites |
| Extended Validation | EV | Domain + thorough organization audit | Financial, e-commerce |
Self-Signed Certificates#
Self-signed certificates are signed by their own private key without involving a CA. They are not trusted by browsers or clients by default and are primarily used for:
- Development and testing environments
- Internal services with custom trust stores
- Client certificate authentication
10. OCSP Stapling#
OCSP (Online Certificate Status Protocol) stapling allows the server to fetch and cache the OCSP response from the CA and present it to clients during the TLS handshake. This improves performance and privacy compared to clients querying the CA directly.
Nginx OCSP Stapling Configuration#
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/private/server.key;
# Enable OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# Trusted CA certificate for OCSP verification
ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
# DNS resolver for OCSP responder lookups
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
}Apache OCSP Stapling Configuration#
SSLUseStapling On
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors offManually Test OCSP#
Query the OCSP responder for a certificate:
# Extract the OCSP URI from the certificate
openssl x509 -in server.crt -noout -ocsp_uri
# Query the OCSP responder
openssl ocsp -issuer intermediate.crt -cert server.crt \
-url http://ocsp.example.com -resp_textTest OCSP Stapling on a Live Server#
echo | openssl s_client -servername <hostname> -connect <hostname>:443 -status 2>/dev/null \
| grep -A 17 "OCSP response"11. Let's Encrypt with Certbot#
Let's Encrypt provides free, automated DV certificates valid for 90 days. Certificates require renewal before expiration.
Install Certbot#
# Debian/Ubuntu
sudo apt install certbot python3-certbot-nginx
# RHEL/Fedora
sudo dnf install certbot python3-certbot-nginx
# Arch Linux
sudo pacman -S certbot certbot-nginxGenerate a Certificate with Nginx Plugin#
Certbot configures Nginx automatically:
sudo certbot --nginx -d example.com -d www.example.comGenerate a Certificate Manually (Standalone)#
Certbot runs its own temporary web server (port 80 must be free):
sudo certbot certonly --standalone -d example.comGenerate a Certificate with Webroot#
Use an existing web server's document root:
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.comGenerate a Wildcard Certificate (DNS Challenge)#
Wildcard certificates require DNS-01 validation:
sudo certbot certonly --manual --preferred-challenges=dns \
--server https://acme-v02.api.letsencrypt.org/directory \
--agree-tos -d example.com -d '*.example.com'Certificate File Locations#
After issuance, certificates are stored under /etc/letsencrypt/live/<domain>/:
| File | Contents |
|---|---|
privkey.pem | Private key |
cert.pem | Server certificate only |
chain.pem | Intermediate CA certificate(s) |
fullchain.pem | Server certificate + intermediates (use this in server config) |
Enable Automatic Renewal#
Certbot installs a systemd timer (certbot.timer) that runs renewal checks twice daily:
# Verify the timer is active
sudo systemctl status certbot.timer
# Enable if not already active
sudo systemctl enable --now certbot.timerTest renewal without making changes:
sudo certbot renew --dry-runRenewal Hooks#
Add post-renewal hooks to reload services:
# Create a deploy hook script
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploycat <<'HOOK' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash
systemctl reload nginx
systemctl reload postfix
HOOK
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.shAlternatively, specify hooks per certificate in /etc/letsencrypt/renewal/<domain>.conf:
[renewalparams]
post_hook = systemctl reload nginx12. x509 Client Authentication with CRL#
12.1 Creating a Root CA and Issuing Client Certificates#
For production use, an intermediate CA is recommended:
Root CA -[signs]-> Intermediate CA -[signs]-> Client CertificateThe following example uses a direct Root CA for simplicity.
Create the Root CA#
# Generate Root CA key and self-signed certificate (10-year validity)
openssl req -newkey rsa:4096 -nodes -keyform PEM \
-keyout example-ca.key -x509 -days 3650 -outform PEM \
-out example-ca.crt -subj "/C=AT/ST=Vienna/O=Example/CN=Example Root CA"Generate and Sign a Client Certificate#
# Generate client private key
openssl genrsa -out example-client.key 4096
# Generate client CSR
openssl req -new -key example-client.key -out example-client.csr \
-subj "/C=AT/ST=Vienna/O=Example/CN=Client User"
# Sign client CSR with Root CA (2-year validity, random serial)
openssl x509 -req -in example-client.csr \
-CA example-ca.crt -CAkey example-ca.key \
-set_serial "0x$(openssl rand -hex 8)" \
-days 730 -sha256 -outform PEM -out example-client.crt
# Bundle client certificate and key into P12 for browser import
openssl pkcs12 -export -inkey example-client.key \
-in example-client.crt -out example-client.p12
# Copy CA certificate for server configuration
sudo cp example-ca.crt /etc/pki/tls/example-ca.crt12.2 CRL Configuration#
Create a directory structure and configuration for CRL management:
mkdir -p demoCA/{newcerts,private}
touch demoCA/index.txt
echo "01" > demoCA/crlnumber
cp example-ca.crt demoCA/cacert.pem
cp example-ca.key demoCA/private/cakey.pemCreate crl.conf:
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ./demoCA
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem
default_md = sha256
crlnumber = $dir/crlnumber
default_crl_days = 30
default_days = 365
preserve = no
policy = policy_any
[ policy_any ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional12.3 Revoking Certificates#
# Revoke a specific client certificate
openssl ca -config crl.conf -revoke demoCA/newcerts/01.pem
# Generate an updated CRL
openssl ca -config crl.conf -gencrl -out crl.pem
# Copy CRL to server directory
sudo cp crl.pem /etc/pki/CA/crl/crl.pem12.4 Nginx Client Certificate Configuration#
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.key;
# Client certificate verification
ssl_client_certificate /etc/pki/tls/example-ca.crt;
ssl_crl /etc/pki/CA/crl/crl.pem;
ssl_verify_client on;
ssl_verify_depth 2;
location /secure {
# Only clients with valid, non-revoked certificates can access
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_pass http://backend;
}
location / {
# Public access, no client cert required
ssl_verify_client optional;
proxy_pass http://backend;
}
}After updating the CRL, reload Nginx:
sudo systemctl reload nginxTroubleshooting#
| Issue | Cause | Solution |
|---|---|---|
unable to load certificate | Wrong file format or corrupted file | Verify format with openssl x509 -in cert.crt -text -noout; convert DER to PEM if needed |
key values mismatch | Certificate and key do not match | Compare modulus: openssl x509 -noout -modulus -in cert.crt | md5sum vs openssl rsa -noout -modulus -in key.key | md5sum |
| Browser shows "Not Secure" for self-signed cert | Certificate not in client trust store | Import the CA certificate into the browser or OS trust store |
certificate has expired | Certificate past its validity date | Renew the certificate; check certbot.timer is active for Let's Encrypt |
verify error:num=20:unable to get local issuer certificate | Missing intermediate certificate | Use fullchain.pem instead of cert.pem in server configuration |
| OCSP stapling not working | Missing ssl_trusted_certificate or resolver | Add ssl_trusted_certificate pointing to CA bundle; add resolver directive |
sslv3 alert handshake failure | Protocol or cipher mismatch | Check server supports TLS 1.2+; verify cipher suite compatibility |
| CSR SAN missing | Extensions not applied during CSR generation | Pass -config req.conf and ensure req_extensions is set |
| Certbot renewal fails | Port 80 blocked or DNS misconfigured | Check certbot renew --dry-run; verify firewall and DNS records |
| SHA-1 certificate rejected | SHA-1 fully deprecated since 2017 | Regenerate certificate with -sha256 or stronger |