Being your own Certificate Authority

2020-11-15 4-minute read

There are many blogs and tutorials with nice shortcuts providing the necessary openssl commands to create and sign x509 certficates.

However, there is precious few instructions for how to easily create your own certificate authority.

You probably never want to do this in a production environment, but in a development environment it will make your life signficantly easier.

Create the certificate authority

Prepare your CA directories

Pick a directory to store your keys and certificates in.

Then make a subdirectory for the certficiate authority and some required directories:

mkdir ca
mkdir ca/private
touch index.txt

Create the key and certificate

Then, make your certificate authority key and certificate:

openssl genrsa -out ca/private/cakey.pem 2048
openssl req -x509 -new -nodes -key ca/private/cakey.pem -sha256 -days 1024 -out ca/cacert.pem

Some tips:

  • You will be prompted to enter some information about your certificate authoirty. Provide the minimum information - i.e., only overwrite the defaults. So, provide a value for Country, State or Province, and Organization Name and leave the rest blank.
  • You probably want to leave the password blank if this is a development/testing environment.

Want to review what you created?

openssl x509 -text -noout -in ca/cacert.pem 

Prepare your openssl.cnf file

With Debian Trixie, the default openssl configuration file seems to be /usr/lib/ssl/openssl.cnf. Prior versions used /etc/ssl/openssl.cnf.

In this document we will explicitly use /etc/ssl/openssl.cnf to get the Debiand defaults.

But, there are two bits missing from the Debian configuration that have to be added.

  1. At the very top add: subjectAltName = ''.
  2. In the [user_cert] section, add: subjectAltName=$ENV::subjectAltName

Create a your first key and ceritificate signing request

First, pick your domain names (aka “common” names). For example, example.org and www.example.org.

Set those values in an environment variable. If you just have one:

export subjectAltName=DNS:example.org

If you have more then one:

export subjectAltName=DNS:example.org,DNS:www.example.org

If you have a wild card domain:

export subjectAltName='DNS:*.example.org'

Next, create a key and a certificate signing request:

openssl req -config /etc/ssl/openssl.cnf -new -nodes -addext "$subjectAltName" -out new.csr -keyout new.key

Again, you will be prompted for some values (country, state, etc) - be sure to choose the same values you used with your certficiate authority! I honestly don’t understand why this is necessary (when I set different values I get an error on the signing request step below). Maybe someone can add a comment to this post explaining why these values have to match?

Also, you must provide a common name for your certificate - you can choose the same name as the altSubjectNames value you set above (but just one domain).

Want to review what you created?

openssl req -config /etc/ssl/openssl.cnf -in new.csr -text -noout 

Sign it!

At last the momenet we have been waiting for:

openssl ca -config /etc/ssl/openssl.cnf -keyfile ca/private/cakey.pem -cert ca/cacert.pem -out new.crt -outdir . -rand_serial -infiles new.csr

Now you have a new.crt and new.csr that you can install via your web browser, mail server, etc specification.

Smoke Test

This command will confirm that the certificate is trusted by your certificate authority.

openssl verify -config /etc/ssl/openssl.cnf -no-CApath -CAfile ca/cacert.pem new.crt 

But wait, there’s still a question of trust

You probably want to tell your computer or browser that you want to trust your certificate signing authority.

Command line tools

Most tools in linux by default will trust all the certificates in /etc/ssl/certs/ca-certificates.crt. (If that file doesn’t exist, try installing the ca-certificates package). If you want to add your certificate to that file:

cp cacert.pem /usr/local/share/ca-certificates/cacert.crt
sudo dpkg-reconfigure ca-certificates

Want to know what’s funny? Ok, not really funny. If the certificate name ends with .pem the command above won’t work. Seriously.

Once your certificate is installed with your web server you can now test to make sure it’s all working with:

gnutls-cli --print-cert $domainName

Want a second opinion?

curl https://$domainName
wget https://$domainName -O-

Both will report errors if the certificate can’t be verified by a system certificate.

If you really want to narrow down the cause of error (maybe reconfiguring ca-certificates didn’t work)?

curl --cacert /path/to/your/cacert.pem --capath /tmp

Those arguments tell curl to use your certificate authority file and not to load any other certificate authority files (well, unless you have some installed in the temp directory).

Web browsers

Firefox and Chrome have their own store of trusted certificates - you’ll have to import your cacert.pem file into each browser that you want to trust your key.

Renewing

In the first step, you created a certificate signing authority key with an expiration of 1,024 days.

With luck, you’ll still be using it after 3 years which means you’ll need to renew it.

Start by changing into the ca directory.

Then, create a new certificate signing request:

openssl x509 -x509toreq -in ca/cacert.pem -signkey ca/private/cakey.pem -out new-server.csr

That command creates the file new-server.csr - a certificate signing request.

Now, simply sign it:

openssl x509 -req -days 1024 -in new-server.csr -signkey private/cakey.pem -out new-cacert.pem

This command generates your brand new cacert.pem file, but with the new name new-cacert.pem.

Now, you simply use the new file to replace your old cacert.pem file.