How to Integrate mkcert CA with Windows Certificate Store: A Complete Technical Guide
Running mkcert -install on Windows automatically adds the local root CA to the system-wide "ROOT" certificate store using native CryptoAPI calls via crypt32.dll, requiring no external tools like PowerShell or certutil.
Integrating a local Certificate Authority (CA) into the Windows Certificate Store is essential for seamless HTTPS development on localhost. The mkcert tool by FiloSottile automates this integration by directly interfacing with the Windows CryptoAPI. This article examines the exact implementation details, source file paths, and system calls used to install and remove the mkcert CA from the Windows ROOT store.
How mkcert Installs the CA on Windows
The installation process follows a six-step pipeline that moves from CA loading to native Windows store manipulation. Each step corresponds to specific functions in the mkcert source tree.
Step 1: Loading the CA Certificate (cert.go)
Before interacting with the Windows store, mkcert loads the PEM-encoded root CA from the CAROOT directory. In cert.go, the loadCA() function reads rootCA.pem and extracts the raw DER-encoded bytes that Windows requires.
// cert.go:81-94
func (m *mkcert) loadCA() {
// Reads rootCA.pem and decodes to DER bytes
// Stores in m.caCert (x509.Certificate) and m.caCertPEM
}
Step 2: Platform Detection and Dispatch (main.go)
When you execute mkcert -install, the install() function in main.go checks if the system trust store is enabled. It then calls installPlatform(), which triggers the Windows-specific implementation.
// main.go:67-77
func (m *mkcert) install() {
if m.systemTrust {
m.installPlatform()
}
// ... user trust store handling
}
Step 3: Opening the Windows ROOT Store (truststore_windows.go)
The Windows-specific logic resides entirely in truststore_windows.go. The openWindowsRootStore() function loads crypt32.dll and invokes CertOpenSystemStoreW to obtain a handle to the "ROOT" store.
// truststore_windows.go:71-80
func openWindowsRootStore() (windowsRootStore, error) {
rootStr, err := syscall.UTF16PtrFromString("ROOT")
if err != nil { return 0, err }
store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(rootStr)))
if store == 0 {
return 0, fmt.Errorf("failed to open windows root store: %v", err)
}
return windowsRootStore(store), nil
}
Step 4: Adding the Certificate with Replace Logic
Once the store is open, addCert() adds the DER-encoded CA using CertAddEncodedCertificateToStore. The implementation uses CERT_STORE_ADD_REPLACE_EXISTING (value 3) to ensure any stale version of the CA is overwritten.
// truststore_windows.go:91-104
func (w windowsRootStore) addCert(cert []byte) error {
// CERT_STORE_ADD_REPLACE_EXISTING = 3
ret, _, err := procCertAddEncodedCertificateToStore.Call(
uintptr(w),
uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING),
uintptr(unsafe.Pointer(&cert[0])),
uintptr(len(cert)),
3, // CERT_STORE_ADD_REPLACE_EXISTING
0,
)
if ret == 0 {
return fmt.Errorf("failed adding cert: %v", err)
}
return nil
}
Step 5: Cleanup and Store Closure
After installation, close() releases the store handle via CertCloseStore.
// truststore_windows.go:83-88
func (w windowsRootStore) close() error {
ret, _, err := procCertCloseStore.Call(uintptr(w), 0)
if ret == 0 {
return fmt.Errorf("failed to close store: %v", err)
}
return nil
}
How mkcert Removes the CA from Windows
Uninstallation requires precise identification to avoid deleting legitimate root certificates. The mkcert -uninstall command triggers uninstallPlatform(), which calls deleteCertsWithSerial().
Enumerating and Matching by Serial Number
The deletion logic enumerates all certificates in the ROOT store using CertEnumCertificatesInStore, parses each with x509.ParseCertificate, and compares the serial number against the mkcert CA. Only matching certificates are removed.
// truststore_windows.go:107-136
func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {
var cert *syscall.CertContext
deleted := false
for {
certPtr, _, err := procCertEnumCertificatesInStore.Call(uintptr(w), uintptr(unsafe.Pointer(cert)))
if cert = (*syscall.CertContext)(unsafe.Pointer(certPtr)); cert == nil {
if errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 { // CRYPT_E_NOT_FOUND
break
}
return deleted, fmt.Errorf("enumeration error: %v", err)
}
// Parse certificate and compare serial numbers...
// Delete if match found
}
return deleted, nil
}
Command Line Usage
While the Go implementation handles the heavy lifting, you interact with it through simple CLI commands.
# Install the mkcert root CA into the Windows ROOT store
mkcert -install
# Verify installation (opens Certificate Manager GUI)
certmgr.msc
# Navigate to: Trusted Root Certification Authorities -> Certificates
# Uninstall and clean up the CA
mkcert -uninstall
Note: The -install command requires Administrator privileges because it modifies the system-wide ROOT store.
Summary
Integrating mkcert with the Windows Certificate Store relies on direct Win32 CryptoAPI calls rather than external utilities. Key implementation details include:
- Zero external dependencies: The implementation in
truststore_windows.gousessyscallto callcrypt32.dlldirectly, avoiding PowerShell orcertutil. - Atomic replacement: The
CERT_STORE_ADD_REPLACE_EXISTINGflag ensures that runningmkcert -installmultiple times safely overwrites stale CA certificates. - Precise uninstallation: The
deleteCertsWithSerialfunction intruststore_windows.goenumerates the ROOT store and removes only certificates matching the mkcert CA's unique serial number. - System-wide trust: Installation targets the Windows "ROOT" store via
CertOpenSystemStoreW, making certificates trusted by all applications including Edge, Chrome, and system services.
Frequently Asked Questions
Does mkcert require administrator privileges to install on Windows?
Yes, installing the mkcert CA into the Windows Certificate Store requires elevated privileges. The CertOpenSystemStoreW API call targets the system-wide "ROOT" store, which is protected by Windows. You must run mkcert -install from an Administrator command prompt or PowerShell session.
How can I verify that mkcert installed correctly in the Windows Certificate Store?
You can verify installation by running certmgr.msc and navigating to Trusted Root Certification Authorities > Certificates, where you should see an entry for "mkcert [hostname]". Alternatively, running mkcert -install a second time will report that the CA is already installed. The certificate details will match the serial number generated in your CAROOT directory.
What happens if I regenerate my mkcert CA?
If you delete rootCA-key.pem and run mkcert -install again, the tool generates a new CA with a new serial number. The Windows implementation uses CERT_STORE_ADD_REPLACE_EXISTING in truststore_windows.go, which automatically overwrites the old certificate in the ROOT store. However, any certificates signed by the old CA will become untrusted, and you will need to regenerate leaf certificates for your local domains.
Does mkcert use PowerShell or certutil for Windows installation?
No, mkcert does not shell out to PowerShell, certutil, or any external tools. The implementation in truststore_windows.go directly loads crypt32.dll using Go's syscall package and invokes Win32 APIs including CertOpenSystemStoreW, CertAddEncodedCertificateToStore, and CertEnumCertificatesInStore. This approach ensures consistent behavior across Windows versions without requiring external dependencies.
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 →