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 copy trusts whatever the CSR asked for, including basicConstraints=CA:TRUE. For a CA you actually operate, use copyall never, 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 -CAca -config
Config fileNot requiredRequired
Serial trackingca.srl via -CAcreateserialserial file, auto increment
Issued-cert databaseNoneindex.txt
Revocation and CRLsImpossible-revoke, -gencrl
Subject policy enforcementNonepolicy section
Extension source-copy_extensions or -extfile-extensions section
Good forOne-off, dev, scriptsA 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.