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#

  1. Overview
  2. Installation
  3. Key Generation
  4. Certificate Signing Requests
  5. Self-Signed Certificates
  6. Certificate Inspection
  7. Certificate Bundling and Conversion
  8. Certificate Formats
  9. Certificate Authorities
  10. OCSP Stapling
  11. Let's Encrypt with Certbot
  12. x509 Client Authentication with CRL
  13. Troubleshooting
  14. See Also
  15. 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_client and s_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 openssl

RHEL/Fedora#

sudo dnf install openssl

Arch Linux#

sudo pacman -S openssl

Verify Installation#

openssl version -a

3. Key Generation#

RSA Keys#

Generate a 4096-bit RSA private key:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out server.key

Note: 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.key

For higher security, use P-384:

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out server-ecdsa384.key

Ed25519 Keys#

Generate an Ed25519 key (simplest, no curve selection needed):

openssl genpkey -algorithm ED25519 -out server-ed25519.key

Note: 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.key

Remove passphrase from a protected key:

openssl rsa -in server-encrypted.key -out server.key

4. 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.10

Generate CSR from Existing Key#

openssl req -new -key server.key -out server.csr -config req.conf

Generate Key and CSR Together#

openssl req -new -newkey rsa:4096 -nodes -keyout server.key -out server.csr -config req.conf

Verify CSR Contents#

openssl req -in server.csr -text -noout -verify

CSR Submission#

After generating the CSR, either:

  • Submit to a CA: Send server.csr to 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.conf

Self-Sign from Existing CSR#

openssl x509 -req -sha256 -in server.csr -signkey server.key \
  -out server.crt -days 365 -extfile req.conf -extensions v3_req

Self-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 -noout

Inspect 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 -noout

Check Certificate Expiration#

openssl x509 -in certificate.crt -noout -enddate

Check Remote Certificate Expiration#

echo | openssl s_client -servername <hostname> -connect <hostname>:443 2>/dev/null \
  | openssl x509 -noout -dates

Inspect a Private Key#

openssl rsa -in private.key -text -noout

For EC keys:

openssl ec -in private-ec.key -text -noout

Inspect a CSR#

openssl req -in request.csr -text -noout

Verify Certificate Chain#

openssl verify -CAfile ca-bundle.crt certificate.crt

Verify 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 md5

Test TLS Connection#

openssl s_client -connect <hostname>:443 -servername <hostname> -tls1_3

7. 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.pem

Extract private key from P12:

openssl pkcs12 -in server.p12 -nocerts -nodes -out private.key

JKS File (Java KeyStore)#

Convert P12 to JKS:

keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 \
  -destkeystore server.jks -deststoretype JKS

PEM Concatenation#

Create a combined PEM file (certificate + key):

cat server.crt server.key > server.pem

Create a full chain PEM (certificate + intermediate + root):

cat server.crt intermediate.crt root.crt > fullchain.pem

DER to PEM Conversion#

openssl x509 -inform DER -in certificate.der -out certificate.pem

PEM to DER Conversion#

openssl x509 -outform DER -in certificate.pem -out certificate.der

8. Certificate Formats#

FormatEncodingExtensionDescription
PEMBase64.pem, .crt, .cer, .keyMost common on Linux; can contain multiple certificates and keys
DERBinary.der, .cerSingle certificate; common in Java and Windows
PFX/P12Binary.pfx, .p12Bundles certificate + key + chain in one encrypted file
JKSBinary.jksJava-specific keystore format (being replaced by PKCS12)
PKCS#7Base64.p7b, .p7cCertificate 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#

LevelAbbreviationValidationUse Case
Domain ValidationDVDomain ownership onlyBasic encryption
Organization ValidationOVDomain + organization identityBusiness websites
Extended ValidationEVDomain + thorough organization auditFinancial, 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 off

Manually 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_text

Test 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-nginx

Generate a Certificate with Nginx Plugin#

Certbot configures Nginx automatically:

sudo certbot --nginx -d example.com -d www.example.com

Generate a Certificate Manually (Standalone)#

Certbot runs its own temporary web server (port 80 must be free):

sudo certbot certonly --standalone -d example.com

Generate 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.com

Generate 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>/:

FileContents
privkey.pemPrivate key
cert.pemServer certificate only
chain.pemIntermediate CA certificate(s)
fullchain.pemServer 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.timer

Test renewal without making changes:

sudo certbot renew --dry-run

Renewal Hooks#

Add post-renewal hooks to reload services:

# Create a deploy hook script
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
cat <<'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.sh

Alternatively, specify hooks per certificate in /etc/letsencrypt/renewal/<domain>.conf:

[renewalparams]
post_hook = systemctl reload nginx

12. 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 Certificate

The 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.crt

12.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.pem

Create 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 = optional

12.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.pem

12.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 nginx

Troubleshooting#

IssueCauseSolution
unable to load certificateWrong file format or corrupted fileVerify format with openssl x509 -in cert.crt -text -noout; convert DER to PEM if needed
key values mismatchCertificate and key do not matchCompare 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 certCertificate not in client trust storeImport the CA certificate into the browser or OS trust store
certificate has expiredCertificate past its validity dateRenew the certificate; check certbot.timer is active for Let's Encrypt
verify error:num=20:unable to get local issuer certificateMissing intermediate certificateUse fullchain.pem instead of cert.pem in server configuration
OCSP stapling not workingMissing ssl_trusted_certificate or resolverAdd ssl_trusted_certificate pointing to CA bundle; add resolver directive
sslv3 alert handshake failureProtocol or cipher mismatchCheck server supports TLS 1.2+; verify cipher suite compatibility
CSR SAN missingExtensions not applied during CSR generationPass -config req.conf and ensure req_extensions is set
Certbot renewal failsPort 80 blocked or DNS misconfiguredCheck certbot renew --dry-run; verify firewall and DNS records
SHA-1 certificate rejectedSHA-1 fully deprecated since 2017Regenerate certificate with -sha256 or stronger

See Also#

Sources#