OpenSSL is the Swiss Army knife of SSL/TLS certificate management. Whether you need to generate a private key, inspect an expiring certificate, create a Certificate Authority for internal services, or debug a TLS handshake failure, the openssl CLI handles it all. This guide walks you through every essential certificate operation — from key generation to chain validation — with practical commands you can run immediately on any Linux server.

Prerequisites

  • OpenSSL installed (version 1.1.x or 3.x — check with openssl version)
  • Basic familiarity with the Linux command line
  • Root or sudo access if installing certificates system-wide
  • Understanding of what SSL/TLS certificates are (public/private key pairs, CAs, trust chains)

Generating Keys and Certificate Signing Requests

Every certificate lifecycle starts with a private key. OpenSSL supports RSA, ECDSA, and Ed25519. RSA 4096-bit and ECDSA P-256 are the most common choices for production.

Generate an RSA private key:

openssl genrsa -out key.pem 4096

Generate an ECDSA key (faster, smaller, equally secure):

openssl ecparam -genkey -name prime256v1 | openssl ec -out ec-key.pem

Create a Certificate Signing Request (CSR):

openssl req -new -key key.pem -out request.csr

You will be prompted for the Distinguished Name (DN) fields. The most critical field is Common Name (CN) — this must match your domain name. For modern TLS, Subject Alternative Names (SANs) are required. Use a config file to set them:

cat > san.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = dn
[dn]
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
EOF

openssl req -new -key key.pem -out request.csr -config san.conf

Inspect the CSR before submitting it:

openssl req -text -noout -in request.csr

Creating Self-Signed Certificates

Self-signed certificates are ideal for internal services, development environments, and private APIs where a public CA is unnecessary.

Create a self-signed cert in one command:

openssl req -x509 -newkey rsa:4096 \
  -keyout key.pem -out cert.pem \
  -sha256 -days 365 -nodes \
  -subj "/CN=example.com/O=MyOrg/C=US"

The -nodes flag skips passphrase encryption on the private key (needed for automated server restarts). Remove it for keys stored offline.

Self-signed with SANs (required for modern browsers):

openssl req -x509 -newkey rsa:4096 \
  -keyout key.pem -out cert.pem \
  -sha256 -days 365 -nodes \
  -subj "/CN=example.com" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.com"

Building an Internal Certificate Authority

An internal CA lets you issue trusted certificates for all services within your organization without relying on a public CA. This is the standard approach for Kubernetes clusters, internal APIs, and VPN infrastructure.

Create the CA key and self-signed root certificate:

openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 3650 \
  -key ca-key.pem -out ca-cert.pem \
  -subj "/CN=My Internal CA/O=MyOrg/C=US"

Sign a server CSR with your CA:

openssl x509 -req -days 365 \
  -in request.csr \
  -CA ca-cert.pem -CAkey ca-key.pem \
  -CAcreateserial -out server-cert.pem \
  -extfile san.conf -extensions v3_req

The -CAcreateserial flag creates a serial number file (ca-cert.srl) to track issued certificates. Keep the CA key offline and encrypted with a passphrase.

Inspecting and Validating Certificates

Inspecting certificates is the first step in diagnosing any TLS problem. OpenSSL provides detailed output on every field.

Read a local certificate:

openssl x509 -text -noout -in cert.pem

Check expiration date only:

openssl x509 -enddate -noout -in cert.pem

Inspect a remote server’s certificate:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -text -noout

Check expiration of a remote cert:

echo | openssl s_client -connect example.com:443 2>/dev/null \
  | openssl x509 -enddate -noout

Verify the certificate matches the private key (modulus hashes must be identical):

openssl rsa  -noout -modulus -in key.pem  | md5sum
openssl x509 -noout -modulus -in cert.pem | md5sum

Verify the certificate chain:

openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt cert.pem

Comparison

FeatureOpenSSL CLIcfssleasy-rsacertbot
Self-signed certsYesYesYesNo
Internal CAYesYesYesNo
Public CA integrationNoNoNoYes (Let’s Encrypt)
SAN supportYes (config file)YesYesYes
PKCS#12 exportYesPartialNoYes
Learning curveMediumLowLowVery low
ScriptableYesYesPartialYes

OpenSSL’s main advantage is that it is available on every Linux system and requires no additional installation. Use cfssl or easy-rsa when you need a friendlier interface for managing an internal CA at scale.

Praxisbeispiel — Real-World Scenario

You have a production Nginx server that is throwing SSL handshake errors. Users report “NET::ERR_CERT_COMMON_NAME_INVALID” in Chrome. Here is the diagnosis workflow:

# Step 1: check what cert the server is actually serving
echo | openssl s_client -connect myserver.com:443 -servername myserver.com 2>/dev/null \
  | openssl x509 -text -noout | grep -A3 "Subject Alternative Name"

# Step 2: if no SANs are listed, the cert is missing them
# Regenerate with SANs using the san.conf approach above

# Step 3: verify key/cert match after regeneration
openssl rsa  -noout -modulus -in /etc/nginx/ssl/key.pem  | md5sum
openssl x509 -noout -modulus -in /etc/nginx/ssl/cert.pem | md5sum

# Step 4: test the full handshake
openssl s_client -connect myserver.com:443 -servername myserver.com
# Look for: Verify return code: 0 (ok)

Gotchas and Edge Cases

SANs are mandatory since 2017. Chrome 58 dropped support for CN-only certificates. Always include subjectAltName even for single-domain certs.

Key file permissions matter. Your private key must be readable only by the process that needs it: chmod 600 key.pem. Nginx and Apache will refuse to start if the key is world-readable.

Passphrase-protected keys break automated restarts. Use -nodes when generating keys for web servers. Store passphrase-protected keys only for CA root keys kept offline.

Certificate chain order matters. Nginx and Apache expect the server certificate first, followed by intermediate certificates, in the same PEM file. Reverse order causes handshake failures with some clients.

Time skew breaks certificate validation. If the server clock is off by more than a few minutes, openssl verify will fail with “certificate not yet valid” even if the cert is fine. Check with date and sync with ntpd or chronyd.

PKCS#12 bundles for Windows/Java. Some platforms require the PKCS#12 format instead of PEM. Export with:

openssl pkcs12 -export \
  -out bundle.p12 \
  -inkey key.pem \
  -in cert.pem \
  -certfile ca-cert.pem

Troubleshooting

“verify error:num=20:unable to get local issuer certificate” — The CA certificate is not in your trust store. Pass it explicitly: openssl verify -CAfile ca-cert.pem cert.pem.

“SSL routines:tls_process_server_certificate:certificate verify failed” — The server certificate is expired, self-signed without trust, or missing SANs. Use openssl s_client to inspect exactly what is being served.

“private key does not match the certificate public key” — The modulus hashes differ. You regenerated the key without regenerating the cert, or mixed up files. Always keep key/cert/CSR together in the same directory.

“no shared cipher” — The client and server cannot agree on a cipher suite. Check the server’s SSL configuration; it may be restricting to old ciphers.

“certificate has expired” — Check openssl x509 -enddate -noout -in cert.pem. Renew with the same CSR (-new with the existing key) or use Let’s Encrypt for automatic renewal.

Summary

  • Generate RSA 4096 or ECDSA P-256 private keys with openssl genrsa or openssl ecparam
  • Always include Subject Alternative Names — CN-only certs are rejected by modern browsers
  • Use a config file with [alt_names] section to add SANs to CSRs and self-signed certs
  • Inspect any certificate with openssl x509 -text -noout -in cert.pem
  • Verify key/cert match by comparing modulus MD5 hashes
  • Build an internal CA for Kubernetes, VPN, and microservice TLS without external dependencies
  • Export to PKCS#12 format for Java keystores and Windows certificate stores
  • Keep CA private keys encrypted, offline, and backed up separately