OpenSSL Cheatsheet
Self-Signed and Local CA
Use this OpenSSL reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Self-signed certificate in one command
Key plus certificate, no CSR file on disk. Good for a load balancer health check or a local dev server. Browsers will still warn, because nothing trusts the key.
openssl req -x509 -newkey rsa:2048 -noenc \ -keyout key.pem -out cert.pem -days 365 -sha256 \ -subj "/CN=localhost" \ -addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1"
EC version, and a longer life for an internal box:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -noenc -keyout ec.key -out ec.crt -days 3650 -sha256 \ -subj "/CN=box.internal" \ -addext "subjectAltName=DNS:box.internal"
Self-sign a certificate for a key you already have:
openssl req -x509 -new -key key.pem -out cert.pem -days 365 -sha256 \ -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"
Signing a CSR with x509 -CA
The quick path. x509 acts as a micro CA with no database and no config file.
openssl x509 -req -in req.csr \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -days 365 -sha256 -copy_extensions copy \ -out cert.pem
-copy_extensions copy is not the default. Leave it off and the SAN in
your CSR is dropped, so the certificate is useless to a browser. On OpenSSL
1.1.x, where the flag does not exist, pass extensions explicitly instead:
cat > ext.cnf <<'EOF' basicConstraints = CA:FALSE keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = DNS:example.com, DNS:www.example.com EOF openssl x509 -req -in req.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -days 365 -sha256 -extfile ext.cnf -out cert.pem
-CAcreateserial writes ca.srl the first time and increments it after,
which keeps serials unique. Skip it and the second signing fails.
-copy_extensions copytrusts whatever the CSR asked for, includingbasicConstraints=CA:TRUE. For a CA you actually operate, usecopyallnever, and override the dangerous extensions with-extfile.
Root CA key and certificate
A root is just a self-signed certificate with CA constraints set.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 \ -aes-256-cbc -out ca.key openssl req -x509 -new -key ca.key -sha256 -days 3650 \ -subj "/C=US/O=Acme Inc/CN=Acme Internal Root CA" \ -addext "basicConstraints=critical,CA:TRUE,pathlen:1" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ -out ca.crt
pathlen:1 allows exactly one intermediate below the root. pathlen:0 means
this CA may only issue leaf certificates.
Minimum openssl.cnf for the ca command
openssl ca is the real CA tool. It keeps a serial counter and an index of
everything it has issued, which is what makes revocation possible. It needs a
directory layout before it will run.
mkdir -p ca/newcerts touch ca/index.txt echo 1000 > ca/serial echo 1000 > ca/crlnumber
# ca.cnf [ca] default_ca = CA_default [CA_default] dir = ./ca database = $dir/index.txt new_certs_dir = $dir/newcerts serial = $dir/serial crlnumber = $dir/crlnumber certificate = ./ca.crt private_key = ./ca.key default_md = sha256 default_days = 365 default_crl_days= 30 policy = policy_loose copy_extensions = copy unique_subject = no [policy_loose] commonName = supplied countryName = optional stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional emailAddress = optional [server_cert] basicConstraints = CA:FALSE keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth [client_cert] basicConstraints = CA:FALSE keyUsage = critical, digitalSignature extendedKeyUsage = clientAuth
Sign with it:
openssl ca -config ca.cnf -extensions server_cert \
-in req.csr -out cert.pem -batch -notext-batch skips the two confirmation prompts. -notext keeps the human
readable dump out of the PEM file, which some parsers choke on.
ca vs x509 -CA
x509 -req -CA | ca -config | |
|---|---|---|
| Config file | Not required | Required |
| Serial tracking | ca.srl via -CAcreateserial | serial file, auto increment |
| Issued-cert database | None | index.txt |
| Revocation and CRLs | Impossible | -revoke, -gencrl |
| Subject policy enforcement | None | policy section |
| Extension source | -copy_extensions or -extfile | -extensions section |
| Good for | One-off, dev, scripts | A CA you run for months |
Intermediates, revocation, CRLs
An intermediate is a CSR signed by the root with CA:TRUE:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out int.key openssl req -new -key int.key -subj "/O=Acme Inc/CN=Acme Issuing CA" \ -out int.csr openssl x509 -req -in int.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -days 1825 -sha256 -out int.crt \ -extfile <(printf '%s\n' \ 'basicConstraints=critical,CA:TRUE,pathlen:0' \ 'keyUsage=critical,keyCertSign,cRLSign')
Revoke and publish a CRL:
openssl ca -config ca.cnf -revoke cert.pem -batch openssl ca -config ca.cnf -revoke cert.pem -crl_reason keyCompromise -batch openssl ca -config ca.cnf -gencrl -out ca.crl -crldays 30 openssl crl -in ca.crl -noout -text
Valid -crl_reason values: unspecified, keyCompromise, CACompromise,
affiliationChanged, superseded, cessationOfOperation, certificateHold,
removeFromCRL.
Build the chain file a server serves, leaf first and root omitted:
cat cert.pem int.crt > fullchain.pem
Gotchas
- Nothing trusts your CA until you install it. Add ca.crt to the system
store (/usr/local/share/ca-certificates/ then update-ca-certificates on
Debian, Keychain Access on macOS). Firefox and Java keep separate stores.
- openssl ca errors with unable to load CA private key when dir in
the config does not resolve from your current working directory. Every path
is relative to where you invoke the command, not to the config file.
- failed to update database, TXT_DB error number 2 means a certificate
with that exact subject was already issued. Set unique_subject = no.
- Chrome rejects self-signed leaf certificates valid longer than 398 days
in some configurations, and public CAs are capped at 398 days. Internal CAs
are not, but keep leaves short anyway.
- Certificates issued in the same second with the same subject and serial are
indistinguishable to clients. Let the serial file do its job.