Sometimes I need to create certificates for testing purposes. For example, I setup an etcd cluster by hand recently. This happens rarely enough that I need to look up the openssl syntax every time.

This is my personal openssl cheat sheet with the commands that are most relevant to me. Examples use options that I need often (e. g. DNS and IP SANs in certificate signing requests). All commands were tested on Ubuntu 20.04.1 LTS with OpenSSL 1.1.1f  31 Mar 2020.

Create a CA

Generate private key:

# Generate private key, password protected (encrypted with DES), 4096 bits
openssl genrsa -des -out rootCA.key 4096

Generate self-signed CA certificate:

# Create CA cert, expires in 365 days, empty subj skips interactive DN prompts, you will be prompted for password of key if private key is DES encrypted
# sha256 is the default message digest algorithm since openssl 1.1
openssl req -x509 -new -key rootCA.key -sha256 -days 365 -out rootCA.crt -subj "/"

Create certificate signed with CA

Generate certificate signing request and private key. It's advisable to include the CN as SAN:

# openssl config file
cat <<- EOF > req.conf
[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[dn]
CN = test.nicktriller.com

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = test.nicktriller.com
DNS.2 = aaa.nicktriller.com
DNS.3 = bbb.nicktriller.com
IP.1 = 127.0.0.1
EOF

Generate private key and certificate signing request

# "nodes" = no DES (unencrypted, no password)
openssl req -nodes -new -config req.conf -keyout cert.key -out cert.csr

Create signed certificate:

# Generate signed certificate (you will be prompted for CA password)
openssl x509 -req -in cert.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out cert.crt -days 500 -sha256 -extensions req_ext -extfile req.conf

-extensions defines the config section that configures x509 extensions.
-days defines the number of days after which the certificate expires.
-CAcreateserial makes OpenSSL create a new file for serial tracking if it's missing. The default filename consists of the CA certificate file base name with ".srl" appended, e. g. "mycacert.srl".
-extfile defines the file containing the extensions.

Create certificates for client and server authentication

The openssl config file from the section "Create certificate signed with CA" can be augmented with serverAuth / clientAuth extensions:

...
[req_ext]
subjectAltName = @alt_names
extendedKeyUsage = clientAuth
...

Create certificate chain bundle

End entity first, root CA last, intermediate CAs in between if applicable:

cat "cert.crt" "rootCA.crt" > "chain.crt"

Import a CA certificate in Debian based systems

sudo mkdir /usr/local/share/ca-certificates/extra
sudo cp rootCA.crt /usr/local/share/ca-certificates/extra/
sudo update-ca-certificates

Inspecting CSRs, certificates and keys

The "-noout" flag suppresses printing the encoded input material to stdout. With the "-text" flag, openssl outputs the decoded input material.

Inspect CSR:

openssl req -noout -text -in cert.csr

Inspect certificate:

openssl x509 -noout -text -in cert.crt

Inspect key:

openssl rsa -noout -text -in cert.crt

Inspect all certificates in a chain bundle:

openssl crl2pkcs7 -nocrl -certfile chain.crt | openssl pkcs7 -print_certs -text -noout

Check if certificate and key match

Validate a certificate and key belong to each other. The "tee" ensures error messages are visible. Otherwise, the md5 digest would appear to match when both commands output the same error message. Hashing the modulus produces a shorter output that's easier to compare.

openssl x509 -noout -modulus -in cert.crt | tee /dev/tty | openssl md5
openssl rsa -noout -modulus -in cert.key | tee /dev/tty | openssl md5

If both message digests are the same, the modulus of key and cert match, therefore the certificate and key belong together.

Using secure client

s_client is a generic TLS client and is highly useful for debugging and inspecting  certificates used by an endpoint.

openssl s_client -connect nicktriller.com:443

stdin stays open when the above command is executed. Whatever is entered will be transfered to the target by s_client. For example, you could send an HTTP request by hand like this (don't miss the trailing newline):

GET /blog/ HTTP/1.1
HOST: www.nicktriller.com

Alternative to OpenSSL

Cloudflare's cfssl is a fine alternative to the openssl cli tool for the creation and signing of certificates.

Further reading

A detailed OpenSSL guide that also describes creation of intermediate CAs can be found here: https://jamielinux.com/docs/openssl-certificate-authority/