Being your own Certificate Authority

2020-11-15 5-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

Create the key and certificate

Pick a directory to store things in. Then, make your certificate authority key and certificate:

openssl genrsa -out cakey.pem 2048
openssl req -x509 -new -nodes -key cakey.pem -sha256 -days 1024 -out 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 cacert.pem 

Prepare your directory

You can create your own /etc/ssl/openssl.cnf file and really customize things. But I find it safer to use your distribution’s default file so you can benefit from changes to it every time you upgrade.

If you do take the default file, you may have the dir option coded to demoCA (in Debian at least, maybe it’s the upstream default too).

So, to avoid changing any configuration files, let’s just use this value. Which means… you’ll need to create that directory. The setting is relative - so you can create this directory in the same directory you have your keys.

mkdir demoCA

Lastly, you have to have a file that keeps track of your certificates. If it doesn’t exist, you get an error:

touch demoCA/index.txt

That’s it! Your certificate authority is ready to go.

Create a 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

Next, create a key and a certificate signing request:

openssl req -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 -in new.csr -text -noout 

Sign it!

At last the momenet we have been waiting for. I recently discovered that my stock debian (bookworm) /etc/ssl/openssl.cnf file does not contain the right bits for setting the subject alternate name (subAltName) and, without that, Chromium fails to approve of the certs.

I fixed it by adding the following to the top of my /etc/ssl/openssl.cnf file:

subjectAltName          = ''

Then, I found the section with the heading “[ usr_cert ]” and added:

subjectAltName=$ENV::subjectAltName

Assuming you still have your variable set above, you should be able to run:

openssl ca -keyfile cakey.pem -cert cacert.pem -out new.crt -outdir . -rand_serial -infiles new.csr

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

Sanity check it

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

openssl verify -no-CApath -CAfile 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 demoCA directory.

Then, create a new certificate signing request:

openssl x509 -x509toreq -in cacert.pem -signkey 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 new-cacert.pem.

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