Certificate Basics
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:
- HTTPS / TLS encryption
- Verifying host identity
- Trusted communication between services
- Some authentication workflows (mutual TLS, Kerberos-adjacent)
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
- Cert expired — check
notAfterdate - Hostname mismatch — the CN or SAN in the cert does not match the hostname you are connecting to
- Missing intermediate chain — the server only presents the end-entity cert, not the full chain to a trusted root
- Wrong key paired with cert — verify with the modulus MD5 check above
- Wrong permissions on key file — private keys should be
chmod 600and readable only by root or the service user - Old trust store — client does not trust the CA that issued the cert
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
- Certificate chain — should list end-entity cert → intermediate CA → root CA. If only one cert is shown, the server is missing the intermediate chain.
- Subject and Issuer — confirm the cert belongs to the right domain and was issued by the expected CA.
- CN/SAN match — look for
Subject: CN=hostnameand check thatX509v3 Subject Alternative Nameincludes the hostname you are connecting to. - Validity dates —
Not BeforeandNot Aftershow the cert window. Compare against today's date. - Verify return code — the last line:
Verify return code: 0 (ok)means the cert is trusted. Any other code means something is wrong.
# 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.