LinuxLXXI · TLS and PKIOpenSSL
OpenSSL for sysadmins - the practical toolkit
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
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
Q1. Which command creates a CSR?
Q2. Self-signed certificates are trusted by browsers by default.
Q3. Which of the following are valid openssl operations? Select all that apply.
Q4. Which field do modern TLS clients use to match a certificate to a hostname?
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.