Personal Root CA
March 5, 2023•1,129 words
I decided to learn a bit more about PKI, SSL certificates, CAs, x509, etc. I've had Let's Encrypt certs on all of my servers for a good while, done self-signed certs for some personal authentication stuff, had a vague knowledge of trust chains, but was weak on the details and methods.
Towards fixing that, I've set up my own Root CA, with determination to do it some form of 'properly'. That is, to do everything decently securely, use intermediate authorities with a well-defined structure and naming convention, use the x509 extensions, design standard procedures for certificate renewal, and figure out some tooling to make upkeep tolerable.
An end goal is to move away from using the Let's Encrypt certificates for every non-public service, like email access and file storage. This will also let me more consistently secure things on my local network that cannot get an LE cert. Zero-trust and all that. Hopefully I can also get rid of passwords for a few things too, going to cert/key authentication.
So, what do?
Security
First off for security, I first made a password-secured VM (VMware Fusion), with file encryption (OS X FileVault) enabled internally too, as well as keychain passwords. I started it up, generated the root CA, created the first level of intermediate CAs and extracted them from the VM, and then shut it down. There it shall sleep until renewal (or revocation) time.
Outside of securing the root, I'm making sure to be careful with directory permissions for all permanent private key storage areas. For transfers, I'm careful to only transmit privkeys inside PKCS #12 files with good passwords, and to not leave the .p12 files lying about afterwards anyway. Another part of the security is the CA structure itself.
Structure, Naming Convention
The structure is perhaps overdesigned for my tiny network. I created a parent 'organization', the Relay Authority (nod to Vernor Vinge), to act as basically the overall root authority, parent to sub-organizations for each domain/network. Each sub-org has its own intermediate CA, the Provisional Authority. This CA is solely used to provide another layer of CAs, 'entity certificates': these are either Cynosure certificates provided to servers (VM, VPS, or baremetal), and Ipseity certs provided to an individual person/identity. This final level of CA is then used to make leaf certificates for specific use cases: individual daemons or services get Recept certs, e.g. an Email Recept for a given server, and each person can generate Safeguard certs for a given email or other account, e.g. Safeguard certs for each email address (Blame! for some of this naming also goes to Tsutomu Nihei).
While certainly overkill, I think it's still a nice way of organizing things, allowing flexibility for making contextual certificates. It also adds to security in two ways:
It means security in the greater Net can only be compromised so far. If a VPS were breached (or even if a provider had a bad actor employee), at most only that Cynosure branch would need to be resecured. I may play with this idea in the future (i.e., pretend a breach occurred) in order to get practice with CRLs and related tools.
It also means the leaf certificates have a very narrow scope. While this does mean I'll be doing a fair amount of renewals over time, it also means I can be less-worried about setups where I might need to put private keys in dubious situations, because any risk is confined to that use case, and the parent ipseity is not in danger.
Extensions
As far as the x509 extensions I think I was a bit lax. I used them, but I didn't generally set the usage extensions as critical because of a nervousness about possible future uses. I may lock things down a bit more when renewing certs, after I've used the system for a while and have a feel for how I use it.
I was more careful about using the Basic Constraints, just to get a feel for them. While it's only theoretical, it could be useful in the case where I wanted to add someone else to the trust system, like if I gave someone else an account for one of my services.
And of course the Subject Alternative Name extension is important just because so many of my servers have multiple hostnames.
Procedures
After reading a few things about various certificate setups, I decided to rotate the Root certificate every 8 years, while making it valid for 16, and to reuse private keys once. This will allow staggered cross-signing to maintain validity where necessary. Each layer down then has half the validity term and rotation times, leaving leaf certificates with yearly rotation. Annoying? Maybe a bit. Secure? Yep. It'll also force me to keep all of this resonably fresh in my memory.
Tooling
For Linux/VPS handling, while I learned a fair bit about the openssl
command itself and its config files, it seemed fairly cumbersome to use long-term. After looking around and trying a few things I decided on Cloudflare's cfssl toolset to make things a bit easier.
cfssl
uses short, easily parsed json (or I think yaml?) files to define CSRs (esp. helpful for creating and recording Recept and Safeguard requests), and has a one-shot command to go from that input file to a final certificate, pem-form csr, and privkey. With a script to parse its json output into files (using the jq tool) it's quite simple to generate a new leaf certificate.
For local stuff—which currently means all intermediate certificate generation and most Safeguard certs—I'm currently using OS X keychain access and certificate assistant to generate and store certs. It lets me easily use the personal certs, and password-protect various sets of CAs. I may switch some of this process over to cfssl
in the future, though.
So in the end...
Currently finishing up replacing old self-signed certs with new leaf certs, and propagating the Relay root cert and parts of the chain where required. Later I may look into automating things more, as well as CRL tools, OCSP, etc. Various bits and procedures will probably be refined as I use the system.
A big thing I'm going to keep an eye on is key type and length. I currently use 4096-bit RSA for the Root, and 2048-bit RSA everywhere else. I've so much sus-talk about ECC (re: curve selection) that I'm hesitant to use it, but I have already switched some other things over to ed25519 in some places because it seems to be more trusted. Currently cfssl
only supports RSA or ECC, but when the relevant PR goes through it's likely I'll start changing over to that when key renewal time comes 'round.