Overview
Commands below were run against OpenSSL 3.0; -noenc is the 3.0 name for -nodes, and LibreSSL (macOS) may differ. For SSH keys use ssh-keygen (ssh-config). Never commit private keys; see github-secrets.
Keys
Use ECDSA P-256 or RSA 2048 or larger for TLS.
| Command | Creates |
|---|---|
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.key | ECDSA P-256 key. |
openssl genpkey -algorithm ed25519 -out ed.key | Ed25519 key (not every TLS stack accepts it). |
openssl genrsa -out key.pem 3072 | RSA key. |
openssl pkey -in key.pem -pubout -out pub.pem | Public key from any private key type. |
Certificates
Include a subject alternative name in every certificate you generate.
| Command | Creates |
|---|---|
openssl req -new -key key.pem -out csr.pem | CSR to send to a CA. |
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc -days 365 -keyout k.pem -out c.pem | Key plus self-signed certificate. |
Browsers ignore the Common Name and need a subject alternative name, so add one for local development:
openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -noenc \
-keyout localhost.key -out localhost.crt -subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"Inspect
Inspect with -noout -text before installing any certificate.
| Command | Shows |
|---|---|
openssl x509 -in c.pem -noout -text | Full certificate. |
openssl x509 -in c.pem -noout -dates -subject -issuer | Validity, subject, issuer. |
openssl x509 -in c.pem -noout -ext subjectAltName | SAN list. |
openssl x509 -in c.pem -noout -fingerprint -sha256 | SHA-256 fingerprint. |
openssl req -in csr.pem -noout -text | CSR contents. |
openssl verify -CAfile ca.pem c.pem | Chain check against a CA file. |
openssl crl2pkcs7 -nocrl -certfile chain.pem | openssl pkcs7 -print_certs -noout | Subject and issuer of every certificate in a bundle. |
Confirm a key matches a certificate: the two digests must be equal.
openssl x509 -noout -pubkey -in c.pem | openssl sha256
openssl pkey -pubout -in key.pem | openssl sha256Live TLS
Test with s_client before blaming the application.
| Command | Tests |
|---|---|
openssl s_client -connect host:443 | Handshake, chain, and chosen cipher. SNI is sent from the hostname by default. |
openssl s_client -connect host:443 -servername name | Override the SNI name (needed for IPs). |
openssl s_client -connect host:443 -tls1_3 | Force TLS 1.3 (-cipher sets TLS 1.2 suites, -ciphersuites sets 1.3). |
openssl s_client -starttls smtp -connect host:587 | STARTTLS; also imap, pop3, ftp. |
echo | openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -dates | Expiry of a live certificate. |
Convert
Convert formats with x509 and pkcs12; never edit PEM by hand.
| Conversion | Command |
|---|---|
| PEM to DER | openssl x509 -in c.pem -outform DER -out c.der |
| DER to PEM | openssl x509 -in c.der -inform DER -out c.pem |
| PEM to PKCS#12 | openssl pkcs12 -export -out b.p12 -inkey key.pem -in c.pem -certfile chain.pem |
| PKCS#12 to PEM | openssl pkcs12 -in b.p12 -out all.pem -noenc (add -nokeys for certificates only) |
| Full chain file | cat c.pem chain.pem > fullchain.pem |
Gotchas
Avoid these; each makes browsers reject a certificate or a server fail to start.
| Gotcha | Fix |
|---|---|
| A certificate without a SAN is rejected by browsers, even on localhost. | Always pass -addext "subjectAltName=...". |
req -x509 and x509 -req default to 30 days. | Set -days explicitly. |
s_client keeps waiting on stdin after the handshake. | Pipe echo into it, or redirect < /dev/null. |
| A PKCS#12 made by OpenSSL 3 may not import into old Java or Windows tools. | Re-export with -legacy. |
| A key and certificate mismatch stops nginx or Apache from starting. | Compare the public-key digests above. |