# How to Integrate mkcert CA with Windows Certificate Store: A Complete Technical Guide

> Integrate mkcert CA with Windows Certificate Store seamlessly. This guide explains how mkcert automatically adds its root CA to Windows ROOT store using native CryptoAPI calls. No external tools needed.

- Repository: [Filippo Valsorda/mkcert](https://github.com/FiloSottile/mkcert)
- Tags: how-to-guide
- Published: 2026-03-05

---

**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`](https://github.com/FiloSottile/mkcert/blob/main/cert.go), the `loadCA()` function reads `rootCA.pem` and extracts the raw DER-encoded bytes that Windows requires.

```go
// 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`](https://github.com/FiloSottile/mkcert/blob/main/main.go) checks if the system trust store is enabled. It then calls `installPlatform()`, which triggers the Windows-specific implementation.

```go
// 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`](https://github.com/FiloSottile/mkcert/blob/main/truststore_windows.go). The `openWindowsRootStore()` function loads `crypt32.dll` and invokes `CertOpenSystemStoreW` to obtain a handle to the "ROOT" store.

```go
// 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.

```go
// 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`.

```go
// 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.

```go
// 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.

```bash

# 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.go`](https://github.com/FiloSottile/mkcert/blob/main/truststore_windows.go) uses `syscall` to call `crypt32.dll` directly, avoiding PowerShell or `certutil`.
- **Atomic replacement**: The `CERT_STORE_ADD_REPLACE_EXISTING` flag ensures that running `mkcert -install` multiple times safely overwrites stale CA certificates.
- **Precise uninstallation**: The `deleteCertsWithSerial` function in [`truststore_windows.go`](https://github.com/FiloSottile/mkcert/blob/main/truststore_windows.go) enumerates 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`](https://github.com/FiloSottile/mkcert/blob/main/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`](https://github.com/FiloSottile/mkcert/blob/main/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.