Update-MozillaAuthRoot.ps1 manages Windows' Third-party Root Certification Authorities store (Cert:\LocalMachine\AuthRoot) from curl's PEM conversion of Mozilla's CA store. This is a security-sensitive operation: certificates in this store can establish trust for TLS, code signing, Wi-Fi/VPN, proxy inspection, and internal services.
The curl cacert bundle that is used by default is a conversion of Mozilla's store, not Mozilla's original trust database. In particular, it does not retain browser-specific constraints such as name constraints. See curl's CA Extract documentation.
- Run it only from an elevated Windows PowerShell session.
- For higher assurance, obtain the SHA-256 for the exact
cacert.pemrevision from a trusted, independent channel and pass it as-ExpectedSha256. A checksum fetched from the same HTTPS source is mainly an accidental-corruption check, not an independent trust boundary. - Start with
-WhatIf; it downloads and validates the bundle, then removes its temporary file without creating a backup or changing the certificate store. - Prefer the default additive mode.
-Replaceremoves every AuthRoot certificate not in the bundle, including roots installed by your organisation or management tooling. - Keep the generated backup somewhere durable. Test the recovery procedure in a non-production environment before depending on it.
First inspect the proposed change. The current curl CA Extract page publishes the current bundle's SHA-256.
.\Update-MozillaAuthRoot.ps1 -WhatIfImport missing Mozilla roots while retaining existing AuthRoot certificates. This downloads the bundle over HTTPS and performs structural validation:
.\Update-MozillaAuthRoot.ps1For higher assurance, pin an independently obtained digest for the default curl cacert bundle (https://curl.se/ca/cacert.pem.sha256):
.\Update-MozillaAuthRoot.ps1 -ExpectedSha256 '<trusted-64-character-SHA-256>'To use a file you downloaded and reviewed yourself—for example, cacert.pem beside the script—pass -BundlePath. The script does not download or delete that file:
.\Update-MozillaAuthRoot.ps1 -BundlePath .\cacert.pem -ExpectedSha256 '<trusted-64-character-SHA-256>'Only when you explicitly intend to make AuthRoot match the bundle, replace certificates not in it:
.\Update-MozillaAuthRoot.ps1 -BundlePath .\cacert.pem -Replace -ConfirmThe script downloads the PEM over HTTPS (or uses -BundlePath), optionally verifies its SHA-256, rejects malformed/non-CA/duplicate entries and unexpectedly small bundles, creates a PKCS#7 backup before a real change, imports all missing Mozilla roots, verifies they are present, and only then removes non-Mozilla roots. Therefore an import failure retains the existing roots rather than clearing the store first.
-ExpectedSha256 is optional; when it is omitted, the script warns before proceeding. -WhatIf is intentionally a dry run. Real changes have a high-impact, single confirmation prompt; use -Confirm:$false only in an appropriately controlled automation context. The backup defaults to thirdpartyrootsbackup-<timestamp>.p7b in the current directory.
The script requires administrator rights and is compatible with Windows PowerShell 5.1 and PowerShell 7. It was tested on Windows with Windows PowerShell 5.1.26100.8737 and PowerShell 7.6.3.
The test runs covered PEM parsing, -WhatIf cleanup and non-mutation, SHA-256 and minimum-count rejection, additive import, -Replace, PKCS#7 backup recovery, and HTTPS validation after replacement.
The backup is a PKCS#7 certificate collection. To add its certificates back without removing currently installed certificates, use .NET's certificate-store API. This is reliable in both interactive and non-interactive sessions:
$certificates = New-Object Security.Cryptography.X509Certificates.X509Certificate2Collection
$certificates.Import('.\thirdpartyrootsbackup-<timestamp>.p7b')
$store = New-Object Security.Cryptography.X509Certificates.X509Store('AuthRoot', 'LocalMachine')
$store.Open([Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
try {
$store.AddRange($certificates)
}
finally {
$store.Close()
}To restore the backed-up store exactly, first export anything you need from the current AuthRoot store, then remove its contents and import the backup. This is destructive; use it only for recovery and test it before production use.
Get-ChildItem Cert:\LocalMachine\AuthRoot | Remove-Item
$certificates = New-Object Security.Cryptography.X509Certificates.X509Certificate2Collection
$certificates.Import('.\thirdpartyrootsbackup-<timestamp>.p7b')
$store = New-Object Security.Cryptography.X509Certificates.X509Store('AuthRoot', 'LocalMachine')
$store.Open([Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
try {
$store.AddRange($certificates)
}
finally {
$store.Close()
}Before production use, test on a representative non-production machine:
- Run
-WhatIfand confirm the planned add/remove counts are expected. - Run the default additive mode and confirm the backup can be imported into a test store.
- If
-Replaceis required, verify that organisation-specific roots are intentionally absent from the bundle and that internal TLS, VPN, Wi-Fi, proxy, and code-signing workflows still work. - Simulate a bad SHA-256 or incomplete download and confirm that the script stops before backing up or changing AuthRoot.
-SourceUrloverrides the HTTPS bundle URL. It is ignored when-BundlePathis used.-ExpectedSha256is an optional trusted, 64-character SHA-256 of the PEM. It is recommended when the digest comes from an independent trusted channel.-BundlePathuses an existing local PEM file instead of downloading one. The script never deletes this file.-MinimumCertificateCountdefaults to 100 and rejects suspiciously incomplete bundles.-BackupPathchooses the PKCS#7 backup location.-DownloadPathchooses the temporary PEM location; it is removed on every exit path where possible.-Replaceremoves AuthRoot certificates absent from the validated bundle, after all needed imports succeed.-Verboseshows download and hash details.-WhatIfvalidates and displays the planned backup/import/removal actions without retaining files or changing the certificate store.