How mkcert Creates and Manages Local CA Certificates: A Deep Dive into the Source Code
mkcert creates a local Certificate Authority by generating an RSA-3072 key pair and self-signed X.509 certificate stored in a platform-specific directory (CAROOT), then reuses this CA to sign leaf certificates while providing commands to install or uninstall the root certificate from system trust stores.
The FiloSottile/mkcert tool simplifies local HTTPS development by acting as its own certificate authority. Understanding how mkcert creates and manages local CA certificates reveals why it requires zero configuration while producing browser-trusted certificates. This analysis examines the actual implementation in the Go source code, tracing the lifecycle from initial CA generation to system-wide trust integration.
The CAROOT Directory: Where mkcert Stores Your Local CA
Before creating any certificates, mkcert determines where to persist the root CA files. This location, known as CAROOT, varies by operating system and is resolved by the getCAROOT() function in main.go.
Platform-Specific Default Paths
- Linux and BSD:
~/.local/share/mkcert - macOS:
~/Library/Application Support/mkcert - Windows:
%LocalAppData%\mkcert
You can override this location by setting the CAROOT environment variable. The getCAROOT() implementation (lines 40-64 in main.go) handles path expansion and ensures the directory exists before any file operations occur.
CA Creation and Loading Logic
The (*mkcert).loadCA() method in cert.go orchestrates whether to load an existing CA or create a new one. This method checks for the existence of rootCA.pem in the CAROOT directory using pathExists(filepath.Join(m.CAROOT, rootName)).
Loading an Existing CA
If the root certificate file exists, loadCA() (lines 81-108 in cert.go) parses the PEM-encoded certificate into m.caCert and the private key into m.caKey. This ensures that subsequent certificate generations maintain the same trust chain, preventing browser warnings about changing CAs.
Generating a New CA
When no existing CA is found, newCA() (lines 110-148 in cert.go) executes the following steps:
- Key Generation: Calls
generateKey(true)to create an RSA-3072 bit private key - Certificate Template: Constructs an X.509 template with a 10-year validity period, sets the
IsCAflag to true, and includes the current username and hostname in the Subject field - Self-Signing: Uses
x509.CreateCertificate(rand.Reader, tpl, tpl, pub, priv)to produce the final certificate - File Persistence: Writes the private key to
rootCA-key.pemwith mode0400(read-only for owner) and the certificate torootCA.pemwith mode0644
The filenames are defined as constants in main.go (lines 52-54):
rootName = "rootCA.pem"
rootKeyName = "rootCA-key.pem"
Root Certificate Storage and File Structure
Once generated, the CA persists in two files under CAROOT:
rootCA.pem: The public certificate (mode 0644)rootCA-key.pem: The private key material (mode 0400)
mkcert uses ioutil.WriteFile to atomically write these files. The deterministic naming convention ensures that every invocation of the tool can locate the same CA, creating a consistent trust anchor across all generated leaf certificates.
Installing the Local CA into System Trust Stores
The (*mkcert).install() method triggers platform-specific installation routines that copy the root certificate into various trust stores. This process enables browsers and system tools to trust certificates signed by your local CA without security warnings.
Linux and macOS Integration
On Unix-like systems, installPlatform() (in truststore_linux.go, lines 51-74) executes:
sudo tee /etc/pki/ca-trust/source/anchors/mkcert_development_CA_<serial>.pem
update-ca-trust extract
The caUniqueName() function (lines 66-68 in cert.go) generates the unique filename from the CA's serial number, preventing collisions if multiple mkcert instances exist.
Windows, NSS, and Java Support
Separate files handle specific trust stores:
truststore_darwin.go: macOS Keychain integration usingsecuritycommandstruststore_windows.go: Windows certificate store via CryptoAPItruststore_nss.go: Firefox and Chrome NSS database modificationtruststore_java.go: Javacacertskeystore updates
Each implementation calls the appropriate system commands to import rootCA.pem with the unique name generated by caUniqueName().
Generating Leaf Certificates Signed by the Local CA
With the CA loaded into memory (m.caCert and m.caKey), the makeCert(hosts []string) function (in cert.go) creates end-entity certificates:
- Generates a new key (RSA-2048 or ECDSA-P256 depending on preferences)
- Builds a certificate template containing the requested hostnames/IPs as Subject Alternative Names (SANs)
- Signs the certificate using
x509.CreateCertificate(..., m.caCert, m.caKey) - Outputs PEM files (or PKCS#12 with the
-pkcs12flag)
For Certificate Signing Request (CSR) workflows, makeCertFromCSR() (lines 209-280 in cert.go) reads a CSR file, copies its subject and extensions into a new template, and signs it with the local CA.
Uninstalling and Cleaning Up the CA
The (*mkcert).uninstall() method reverses the installation process. It calls uninstallPlatform(), uninstallNSS(), and uninstallJava() to remove the certificate from each trust store. For example, uninstallPlatform() in truststore_linux.go (lines 77-99) removes the PEM file from the system anchors directory and refreshes the certificate cache.
This operation removes trust but preserves the CA files in CAROOT, allowing you to reinstall later without regenerating the root certificate.
Summary
- mkcert stores your local CA in a platform-specific CAROOT directory (
~/.local/share/mkcerton Linux), determined bygetCAROOT()inmain.go - The CA is created once via
newCA()incert.gousing RSA-3072 keys and a 10-year self-signed certificate, then reused vialoadCA() - Two files comprise the CA:
rootCA.pem(public certificate, mode 0644) androotCA-key.pem(private key, mode 0400) - System trust integration happens through platform-specific files like
truststore_linux.go, which copy the certificate to system directories using unique names derived from the CA serial number - Leaf certificates are generated by
makeCert()and signed with the loaded CA, ensuring they validate against the installed root
Frequently Asked Questions
How does mkcert ensure the local CA is only created once?
mkcert checks for existing files before generation. The loadCA() function in cert.go verifies rootCA.pem exists using pathExists(). If found, it loads the existing certificate and key into memory; if not, it triggers newCA() to generate fresh credentials. This prevents accidental CA regeneration that would invalidate previously issued certificates.
What cryptographic standards does mkcert use for the root CA?
According to the source code in cert.go, mkcert generates an RSA-3072 bit key for the root CA and sets the certificate validity to 10 years. The certificate includes the IsCA basic constraint set to true and derives its subject name from the current system user and hostname, creating a unique but identifiable local authority.
Can I move my mkcert CA to another machine?
Yes. The entire CA consists of the two files in your CAROOT directory (rootCA.pem and rootCA-key.pem). Copy these files to the equivalent directory on another machine, or set the CAROOT environment variable to a shared location. mkcert will load the same CA on the new machine, allowing seamless certificate portability across development environments.
Does mkcert support removing the CA from system trust stores?
Yes. Running mkcert -uninstall triggers (*mkcert).uninstall(), which calls platform-specific removal functions like uninstallPlatform() in truststore_linux.go. These functions delete the CA certificate from system directories, NSS databases, and Java keystores, effectively untrusting the root without deleting the underlying files from CAROOT.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →