Certificate Basics

Page 08 — What certificates are, core openssl commands, and common issues.

What a certificate is

A certificate is a signed identity document. It says in effect: "this public key belongs to this host or service, and a trusted authority (CA) has verified this." Certificates are used for:

Core terms

private key
Secret key. Must be protected. Never share it. Used to sign or decrypt.
public key
Can be shared. Anyone can have it. Used to verify or encrypt.
CSR
Certificate Signing Request. You generate this when asking a CA to issue a certificate. Contains your public key and identity info.
certificate
A signed document binding a public key to an identity. Issued by a CA.
CA
Certificate Authority. The trusted entity that signs and issues certificates.
chain
The path of trust from your certificate back through intermediate CAs to a root CA.
SAN
Subject Alternative Name. Lists additional hostnames or IPs the certificate is valid for.
PEM
Text format, base64-encoded. Common on Linux. Extensions: .crt, .pem, .key.
DER
Binary format. Extensions: .der, .cer.
PFX / PKCS12
Bundle containing certificate, key, and chain in one file. Extension: .pfx, .p12.

Generate a private key

# RSA 2048-bit (widely compatible, larger key/signature)
openssl genrsa -out server.key 2048

# RSA 4096-bit (more secure but slower TLS handshakes)
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:4096

RSA 2048 is still the most widely compatible choice for most environments. For new deployments, prefer ECDSA P-256 or P-384 — smaller keys, faster handshakes, and equivalent or better security. See the ECDSA section below.

Generate a CSR

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

Uses the private key to create a CSR. You will be prompted for subject fields: country, state, organisation, common name (hostname), etc. The CSR is sent to a CA to get a signed certificate. Note: the interactive prompt does not add Subject Alternative Names (SANs). For production CSRs, use a config file — see the CSR with SANs section below.

Inspect a certificate

openssl x509 -in server.crt -text -noout

Shows the certificate contents in human-readable form: subject, issuer, validity dates, SANs, key usage, and other details.

Check expiry

openssl x509 -in server.crt -noout -dates

Shows only the notBefore and notAfter dates. Use this when checking whether a cert has expired.

Inspect a CSR

openssl req -in server.csr -text -noout

Verify key and cert match

The MD5 hashes of the modulus must be identical. If they differ, the key and cert are mismatched.

openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5

Convert formats

PEM to DER

openssl x509 -in cert.pem -outform der -out cert.der

PEM to PFX

openssl pkcs12 -export -out bundle.pfx -inkey server.key -in server.crt

Add -certfile chain.crt to include intermediate CA chain in the PFX bundle.

Why cert problems happen

Key permissions: If a service cannot start due to a TLS error, check the key file permissions first. Many services refuse to start with a world-readable private key.

Live inspection with openssl s_client

openssl s_client is the most useful everyday certificate tool. It connects to a live TLS endpoint and shows you exactly what the server is presenting — no browser needed.

Basic connection

# Always include -servername for SNI (server name indication)
# Without it, the server may return the wrong certificate on a multi-vhost server
openssl s_client -connect host:443 -servername host

# STARTTLS — for mail servers (SMTP, IMAP, POP3)
openssl s_client -connect mail.example.com:587 -starttls smtp
openssl s_client -connect mail.example.com:143 -starttls imap

On modern servers with multiple virtual hosts, omitting -servername may return the default (wrong) certificate. Always include it unless you are deliberately testing the default. -starttls smtp speaks the SMTP protocol up to STARTTLS then upgrades the connection — use this instead of connecting to port 465 directly (which is SSL from the start).

Show full certificate chain

openssl s_client -connect host:443 -showcerts

Without -showcerts you only see the end-entity certificate. With it, you see every certificate in the chain the server sends. Useful for diagnosing missing intermediate chain issues.

What to look for in the output

# Quick expiry check from the command line
openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -dates

Pipes the certificate directly into openssl x509 to print just the dates. Useful in scripts.

ECDSA keys

ECDSA (Elliptic Curve Digital Signature Algorithm) keys are smaller and faster than equivalent RSA keys. P-256 is the most widely supported curve.

# Generate an ECDSA P-256 private key
openssl ecparam -name prime256v1 -genkey -noout -out ec.key

# Or using the newer genpkey interface
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.key

# Generate a CSR from the ECDSA key
openssl req -new -key ec.key -out ec.csr

# Check the key type
openssl pkey -in ec.key -text -noout | head -5

P-256 (prime256v1) is supported by all modern browsers, Java, Python, and OpenSSL. P-384 is more conservative but slightly slower. Both are far superior to RSA 1024 and comparable to RSA 3072+ in security margin.

CSR with Subject Alternative Names

Modern certificates require SANs — the CN field alone is ignored by most clients. You cannot add SANs through the interactive openssl req prompt. Use a config file instead.

# Create a config file: san.cnf
[req]
default_bits       = 2048
prompt             = no
distinguished_name = dn
req_extensions     = req_ext

[dn]
C  = AU
ST = New South Wales
L  = Sydney
O  = Example Pty Ltd
CN = example.com

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
IP.1  = 192.168.1.10   # include IP SANs if needed
# Generate key and CSR in one step
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -config san.cnf

# Verify the CSR contains the SANs
openssl req -text -noout -in server.csr | grep -A5 "Subject Alternative"

Send server.csr to your CA. They will produce a signed server.crt containing the SANs. If using FreeIPA's CA, you can request the certificate via ipa cert-request or through certmonger.

Chain verification

# Verify a certificate against a CA bundle or fullchain
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt server.crt

# Verify using a specific intermediate CA file
openssl verify -CAfile intermediate-ca.crt server.crt

# Verify a fullchain file (the chain must be: leaf → intermediate → root)
cat server.crt intermediate-ca.crt > fullchain.pem
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt fullchain.pem

# Check what SANs are in a cert
openssl x509 -in server.crt -noout -ext subjectAltName

Services like Nginx need the full chain (leaf + intermediates) in one file. The private key stays separate. Use cat server.crt intermediate.crt > fullchain.pem — the order matters; leaf first, then intermediates up to (but not including) root.

Trust stores

Adding a custom or internal CA so that tools like curl, Python, and browsers trust it.

# RHEL / CentOS (uses update-ca-trust)
# Copy the CA cert (must be PEM format) to:
cp internal-ca.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust extract
# Verify: curl https://internal.example.com  (should not complain)

# Debian / Ubuntu (uses update-ca-certificates)
cp internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
update-ca-certificates
# Verify
curl https://internal.example.com

The cert file must have a .crt extension for update-ca-certificates to pick it up. On RHEL, the .crt extension is conventional but not required. After adding to the system trust store, curl, wget, git, and most system tools will trust the CA. Java uses its own keystore (keytool -importcert -keystore $JAVA_HOME/lib/security/cacerts) and must be updated separately.