Skip to main content
RunBook Academy

LinuxLXXI · TLS and PKIOpenSSL

OpenSSL for sysadmins - the practical toolkit

Intermediate⏱ ~14 minopenssl

What you'll learn

  • Generate keys and CSRs with openssl
  • Sign certificates and verify signatures
  • Check certificate expiry
  • Convert between certificate formats

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

OpenSSL is the standard tool for TLS / X.509 operations. This lesson covers the common sysadmin tasks: keys, CSRs, signing, verification, and format conversion.

Generate a key

# RSA 2048-bit
openssl genrsa -out server.key 2048

# RSA 4096-bit (more secure, slower)
openssl genrsa -out server.key 4096

# ECDSA (faster, smaller keys)
openssl ecparam -name prime256v1 -genkey -noout -out server.key

Protect the private key:

chmod 600 server.key

The Common Name is not the hostname

Read this before the first -subj you type.

The Common Name in the subject is not how clients match a certificate to a hostname. It has not been since RFC 2818 deprecated the practice in 2000. RFC 9525 finished the job and removed the fallback entirely. Chrome has required a subjectAltName since version 58; Go rejects CN-only certificates outright since 1.15, which means Prometheus, Consul, Vault, Docker and most Kubernetes components reject them too.

A certificate identifies a host through the subjectAltName (SAN) extension. The CN is decoration.

The trap is that OpenSSL itself still accepts the CN fallback, so openssl verify and openssl s_client report OK on a certificate that Go and Chrome refuse. Your own validation step passes and the cutover still fails. Check the extension directly:

openssl x509 -in server.crt -noout -ext subjectAltName
# No extensions in certificate   <- this certificate is broken

Every recipe below therefore sets a SAN, and every recipe that produces a server certificate also sets basicConstraints, keyUsage and extendedKeyUsage.

Create a CSR (Certificate Signing Request)

openssl req -new -key server.key -out server.csr \
    -subj "/C=US/ST=CA/L=SF/O=MyOrg/CN=server.example.com" \
    -addext "subjectAltName=DNS:server.example.com,DNS:www.example.com"

The CSR contains the public key, the subject, and the extensions you are requesting. Send it to a CA (public or private) to be signed. List every name the service will be reached by; a name that is not in the SAN will not validate. Use IP:10.0.0.5 for an address rather than DNS:.

Check what you built before sending it:

openssl req -in server.csr -noout -text | \
    sed -n '/Requested Extensions/,/Signature Algorithm/p'

Self-sign a certificate (private CA or test)

openssl x509 -req -in server.csr -signkey server.key -sha256 \
    -copy_extensions copyall -out server.crt -days 365

The certificate is valid for 365 days.

-copy_extensions copyall is the important flag. openssl x509 -req adds no v3 extensions of its own and discards the ones in the CSR unless you ask for them. Drop the flag and you get the CN-only certificate described above.

Sign with a CA (proper CA workflow)

When signing someone else’s CSR, do not copy its extensions blindly. A CSR is an untrusted request: copyall would let the requester grant itself CA:TRUE or any SAN it likes. The CA decides what it issues, so supply the extensions from a file you control.

cat > server-ext.cnf <<'EOF'
subjectAltName = DNS:server.example.com, DNS:www.example.com
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
EOF

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out server.crt -days 365 -sha256 \
    -extfile server-ext.cnf

The CA signs the certificate. The result is in server.crt.

Audit the CSR first, then write the names you are willing to certify into server-ext.cnf. If you do use -copy_extensions copyall for a CSR you generated yourself, read its requested extensions first.

Check a certificate

# Show full details
openssl x509 -in server.crt -text -noout

# Check expiry only
openssl x509 -enddate -noout -in server.crt

# Verify against CA, and check the hostname really matches
openssl verify -CAfile ca.crt -verify_hostname server.example.com \
    -purpose sslserver server.crt

# Confirm the SAN exists - plain verify accepts a CN-only cert
openssl x509 -in server.crt -noout -ext subjectAltName

# Check a remote server certificate
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -subject

Convert certificate formats

# PEM to DER
openssl x509 -in server.crt -outform der -out server.der

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

# PEM to PKCS12 (with key)
openssl pkcs12 -export -in server.crt -inkey server.key \
    -out server.p12

# PKCS12 to PEM
openssl pkcs12 -in server.p12 -nodes -out server.pem

# PEM bundle (cert + chain)
cat server.crt ca.crt > server-bundle.crt

Generate a self-signed certificate for testing

openssl req -x509 -newkey rsa:2048 -nodes \
    -keyout server.key -out server.crt -days 365 -sha256 \
    -subj "/CN=server.example.com" \
    -addext "subjectAltName=DNS:server.example.com,DNS:www.example.com" \
    -addext "basicConstraints=critical,CA:FALSE" \
    -addext "keyUsage=critical,digitalSignature,keyEncipherment" \
    -addext "extendedKeyUsage=serverAuth"

openssl req -x509 does honour -addext, so a one-shot certificate needs no extension file.

Self-signed for testing or internal use only. Browsers and clients do not trust self-signed certificates by default. They will not trust one without a SAN under any circumstances, so a test certificate that omits it fails for the wrong reason and wastes an afternoon.

Knowledge check

Knowledge check · 5 questions

  1. Q1. Which command creates a CSR?

  2. Q2. Self-signed certificates are trusted by browsers by default.

  3. Q3. Which of the following are valid openssl operations? Select all that apply.

  4. Q4. Which field do modern TLS clients use to match a certificate to a hostname?

  5. Q5. A certificate signed with openssl x509 -req ends up with no SAN unless you pass -copy_extensions or supply the extensions yourself with -extfile.

Passing score: 75%. Answers are checked in this browser.