December 19, 2019

Setting Up a Home Root CA

Tired of getting those SSL error pages when accessing your services? Sick of having to click three times to get past warning pages? Have I got a guide for you!

This guide is based on the excellent guide by robpol86.com. Much of the content has been copied however it shall diverge with my own edits.

This guide is going to involve pulling some template files and scripts from my repository Certificate Authority Jumpstart. I highly recommend cloning it onto your Raspberry Pi before we get started.

TomAFrench/Certificate-Authority-Jumpstart
Contribute to TomAFrench/Certificate-Authority-Jumpstart development by creating an account on GitHub.

This guide will go over setting up an offline root certificate authority for your home network. It is based on what I've learned from jamielinux.com with a few differences:

  1. We will not be creating an intermediate pair here. Since my intentions are just setting up SSL certs on a handful of internal web interfaces and maybe even WPA2 Enterprise one day, I didn't think it was worth setting this up. It might make revoking certs not as quick, but I don't see myself signing very many certs after my initial run.
  2. I'll include steps on how to bridge the air gap. For maximum paranoid-tier security we will not be plugging in any USB flash drives (or USB anything excluding keyboards) or network cables. WiFi adaptors are also obviously forbidden. For this we'll be using qrencode.
  3. I'll be assuming the Linux computer you're using has a GUI (desktop environment). This is to reduce the number of QR codes needed since you'll have more resolution with a GUI than with a frame buffer so you can fit more data in each QR code.
  4. For additional paranoid-tier security we'll generate a 8192-bit long RSA key for our root CA. 4096-bit keys are fine too but I'm crazy. We'll also be creating 4096-bit SSL keys instead of the usual 2048-bit. While this guide should work fine with any Linux computer I'll be focusing on Debian-based distributions.

The goal here is to setup an offline root CA. It will be online at first to get updates but right before generating the root pair we will remove any network connectivity from the host and never EVER connect it to any networks or USB devices. This will be an offline and air-gapped root CA.

Preparing the Host

This section will go over preparing a newly-installed Debian/Raspbian system. For machines without a real time clock (e.g. Raspberry Pis) we'll setup a script that runs during boot that prompts you for the current time.

  1. Perform a clean install of Debian (or install the latest Raspbian PIXEL image on the Raspberry Pi) and boot up the host. It's ok to have network access for now. For my Raspberry Pi I followed Raspbian Setup (Raspberry Pi)  (you don't need to install any of those packages in that link, just upgrade).
  2. Upgrade all of your packages since this will be the last time the system will have internet access: sudo apt update && sudo apt upgrade.
  3. sudo reboot in case a new kernel was installed.
  4. Finally install these required packages: sudo apt-get install qrencode acl

Boot Date Prompt on Raspberry Pi

Raspberry Pis don't have real time clocks so they don't keep track of the time when powered off. Usually they handle this by getting the current time from the internet after booting up. However since our root CA will never have internet access again we need to always set the current time every time it boots up.

Since time is very important for signing certificates we'll want to avoid forgetting this. You can install this systemd file to have it prompt you for the current time before the Raspberry Pi finishes booting up, guaranteeing you won't forget:

Copy the OpenSSL Config

Based on a few articles I've found while considering which domain to use at home, I thought I would mention it here even though it's more of a network-related topic rather than an SSL/Certificate topic. I highly encourage you to either purchase a dedicated domain name for your home network or at least use a dedicated subdomain on a domain you already own.

In the table below I'll use myhome.net as an example. ORG_NAME is just a name so in this case the value would be "MyHome.net". If you used home.mycooldomain.com then the ORG_NAME equivalent may be "Home.MyCoolDomain.com". It can actually be set to anything but this is what I've done for my home network.

The first step is to configure OpenSSL. You'll need to replace some values in the configuration file I'll be providing to you. Refer to the table below for what you'll be replacing.

To Replace Replace With Example
SUB_COUNTRY_NAME Two-letter ISO abbreviation for your country. US
SUB_STATE_NAME State or province where you live. No abbreviations. California
SUB_LOCALITY City where you are located. San Francisco
SUB_ORG_NAME Name of your organization. MyHome.net
SUB_UNIT_NAME Section of the organization. Home
SUB_EMAIL Your contact email. xx@yy.zz

Overwrite all of /etc/ssl/openssl.cnf with this file (it's still ok to have network access for this part). Be sure to replace SUB_ strings as otherwise you'll have to enter these values on each certificate request.

OpenSSL Directory Structure

Everything will live in /root/ca. It will also all be owned by root. Remember this computer is a dedicated CA so it won't be doing anything else at all except hosting your very important root certificate private key and the root certificate itself.

Run the command sudo bash prepare_root.sh to setup directories and permissions within /root/ca. The setfacl commands set file-system ACLs which enforce default maximum file permissions for new files/directories. A brief description for these directories:

Directory Description
/root/ca/certs Certificates are dumped here.
/root/ca/crl Certificate revocation lists.
/root/ca/csr Certificate signing request.
/root/ca/newcerts Not used in this guide.
/root/ca/private Private keys. VERY SENSITIVE.

Air Gap

This is the moment we've all been waiting for! Just one more file and then isolate the host from the world permanently.

Place this file into /usr/local/bin/airgap and chmod +x it. We'll use this script to convert files into QR codes after compressing and encrypting them.

Now remove all USB devices (sans keyboard) and network cables/connections. If this is on a Raspberry Pi either swap it out with a Model A (the one without WiFi or ethernet ports), or fill in the ethernet port with hot glue. Do the same with all but one USB ports. Or just be super duper sure never to plug in things when using this SD card.

Finally Generate the Pair

This is where we actually generate the root key and certificate. The root key is used to sign additional certificate pairs for specific devices/servers, and the root certificate is what you'll export to clients that should trust any of these additional certificates.

Warning: The root key ca.key.pem you'll be generating is the most sensitive file on this dedicated computer. Keep it as secure as possible. When openssl genrsa asks you for a password enter a unique and very secure password. Make sure setfacl worked and the permissions are: -r-------- 1 root root 1.8K Aug 15 12:21 private/ca.key.pem

The openssl req command will prompt you for some information. The defaults you've specified in openssl.cnf will be fine. However double check that the Common Name is the fully qualified domain name of this certificate authority.

sudo su -  # Become root.
cd /root/ca
export CN=$(hostname --fqdn)
export SAN=DNS:$CN
openssl genrsa -aes256 -out private/ca.key.pem 8192
openssl req -key private/ca.key.pem -new -x509 -days 1827 -extensions v3_ca -out certs/ca.cert.pem
openssl x509 -noout -text -in certs/ca.cert.pem |more  # Confirm everything looks good.

You're done generating your root certificate and private key. You're technically "done". However you'll probably want to do these two steps:

  1. Install the public root certificate on client computers so they can trust your servers instead of getting SSL errors.
  2. Creating an SSL certificate to install on your web servers (router admin pages, IPMI interfaces, etc.). For more info see: Issuing Server Certificates

For the former you'll want to export the certs/ca.cert.pem file and install it on client computers/devices. For example:

  • OS X: The "Keychain Access" app can install that file in the System keychain (not System Roots), an you'll need to manually set the trust to "Always Trust" (you may also have to restart web browsers or just reboot to get rid of SSL errors).
  • Fedora/CentOS/RHEL: Copy that file to /etc/pki/ca-trust/source/anchors/ and then run sudo update-ca-trust.
  • Android: In the Wifi settings under "Wifi Preferences" there is button labelled "Install certificates". Pressing this brings up a file explorer from which you can select ca.cert.pem, you must then give the CA "VPN and apps" credential use. (Note: apps by default do not have to accept user added certificate authorities so some apps may still produce errors e.g. Metamask Mobile)

Instructions for exporting this file is available in the Bridging the Air Gap section below.


Frequent Tasks

This section will contain additional sub sections with instructions on how to complete some tasks you may repeat for different use cases.

Issuing Server Certificates

This section covers issuing SSL certificates for web servers such as router admin pages. We will generate an SSL certificate and its private key. You'll need to install both files on the web server. Keep in mind the private key is very sensitive and is used to sign SSL sessions to keep it secure as you transfer it to the web server!

When asked for a Common Name you'll need to enter the web server's FQDN. So instead of accessing your router admin page using http://192.168.0.1 you'll instead be using https://router.myhome.net for example. Common Name here
will be router.myhome.net.

On the root CA host place sign_cert.sh in /root/ca and run these commands. Substitute router.myhome.net with whatever FQDN your target web server will use.

export CN=server.myhome.net
export SAN=DNS:$CN
bash sign_cert.sh

If you want to issue a certificate with multiple Subject Alternative Names (e.g. one cert for server.myhome.net and sub.server.myhome.net) you can set them in the SAN environment variable. Below is an example for a certificate valid for the main domain as well as all (single-level) wildcard sub-domains:

   export CN=server.myhome.net
   export SAN=DNS:$CN,DNS:*.$CN

Verify that the Issuer is the root CA and the Subject is the certificate itself. You will need to install both certs/router.myhome.net.cert.pem and private/router.myhome.net.key.pem on the web server. Read Bridging the Air Gap for instructions on how to do this securely.

Bridging the Air Gap

We can use the airgap script we downloaded earlier to encode files into one or more QR codes to be scanned by your phone and reassembled on another computer. This is a one-way data transfer so your root CA host remains secure and air gapped.

airgap will print a password at the end of its run. Use this one-time password to decrypt the files on the receiving computer.

For example if you want to just export the certs/ca.cert.pem file you'll do something like this (you can also specify multiple files for airgap to encode at once):

pi@raspberrypi:~ $ sudo su -
root@raspberrypi:~# cd /root/ca
root@raspberrypi:~/ca# airgap certs/ca.cert.pem
Compressing, encrypting, and encoding 1 file(s)...
certs/ca.cert.pem
Done
-rw-r--r-- 1 root root 7219 Feb 16 16:28 /tmp/qr-01.png
-rw-r--r-- 1 root root 6816 Feb 16 16:28 /tmp/qr-02.png
Password: 3SOD8voj8bT
root@raspberrypi:~/ca#

Then in the GUI open both of those files and scan them with your phone using a QR scanner app. If you're scanning QR codes with an Android phone using Barcode Scanner you can "Share via email" which gives you the option to share to Dropbox (for some dumb reason) which makes it easy to get encrypted data on your computer.

Reconstructing Data

Once you've scanned the QR codes and saved the large strings of data somewhere on a Linux or OS X computer run these commands to reassemble and decrypt data: