Apr 10, 2018

How to generate self-signed certificates with SAN (Subject Alternative Name)

I am a developer, and every web app or web page I develop, I run it locally with HTTPS support. So all my web developments have HTTP support from the beginning. Be it a node app, a python app, a ruby app, a php app, anything, it has to have HTTPS support.

To do so, I generate certificates for each app, and sometimes wildcard certificates for a group of them, but I do not want my browsers to complain for every new certificate, so I am my own Certificate Authority, I trust myself, so I instructed Safari, Firefox and Chrome to also trust me, and I sign every certificate I generate, so the browsers happily tell me that I am visiting a secure site.

This is Firefox trusting my certificate I signed:

Firefox trusts my Self-Sign certificate.

If you want to do the same:

  1. Generate the key which you will use to sign your certificates (and its certificate).
  2. Instruct your browser and OS to trust any certificates signed with that key.
  3. Generate all the certificates you want!

Generate the key which you will use to sign your certificates (and its certificate)

Open your terminal and run the following two commands:

# This gives you the key you will use to sign your certificates.
openssl genrsa -out rootCA-key.pem 2048
# This gives you the certificate that you will install on your browser and OS.
openssl req -x509 -new -nodes -key rootCA-key.pem -sha256 -days 1024 -out rootCA-crt.pem

Keep your rootCA-key.pem file very very secret, or someone else might sign things for you, and you do not want that.

Instruct your browser and OS to trust any certificates signed with that key

This changes for each browser and OS. In general, Firefox needs to have your Root Certificate (rootCA-crt.pem) added, while Chrome and Safari will trust the certificates you install to your OS certificate store (Keychain Access for macOS).

Remember, the file you need to add is rootCA-crt.pem.

Generate all the certificates you want!

This is the step you will repeat for each certificate you want to generate.

Open your terminal, and run the following command.

# for convenience save the domain name to use to a variable
export CERT_DOMAIN="*.domain.com"
# Create the private key for this certificate.
openssl genrsa -out device-key.pem 2048
# Generate the certificate signing request.
openssl req -new -key device-key.pem -out device-crt.pem -subj /CN="$CERT_DOMAIN"
# Sign the CSR with your CA root key.
openssl x509 -req -in device-crt.pem -CA rootCA-crt.pem -CAkey rootCA-key.pem -CAcreateserial -out device-crt.pem -days 500 -sha256 \
    -extensions SAN \
    -extfile <(cat /private/etc/ssl/openssl.cnf \
        <(printf '[SAN]\nsubjectAltName=DNS:%s' "$CERT_DOMAIN"))

I assume you run it on the directory where you have the rootCA-crt.pem and rootCA-key.pem files. If you run it from somewhere else, change the paths to those files.

The files you need are device-crt.pem and device-key.pem, and now your browsers will trust your certificates.


Chrome is enforcing the use of SAN (Subject Alternative Name) on all the certificates, and does not take into account the CN (Common Name) anymore, so if you are getting this error (or similar):

Attackers might be trying to steal your information from DOMAIN (for example, passwords, messages, or credit cards).

It probably means you certificate does not have the domain in the SAN field. Generate a new certificate with the commands form this page, and you should be good to go.