<!-- https://pkizerotohero.com/guides/add-root-ca-macos/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Trust a private root CA on macOS with Keychain Access, the security command or an MDM profile, verify it, and fix the apps that ignore the keychain.

Source: https://pkizerotohero.com/guides/add-root-ca-macos/ · Last reviewed: 2026-10-20

How-to guide · Trust a CA

# How to trust a private root CA on macOS

**In one sentence:**

Your Mac, or every managed Mac, trusts your organisation's private root CA in the System keychain, and you know which apps need their own setting.

Task

[Trust a CA](https://pkizerotohero.com/guides/#task-trust)

Platforms

- macOS

Who it is for

- [IT ops](https://pkizerotohero.com/start/#itops)
- [Developer](https://pkizerotohero.com/start/#developer)

Level

Practitioner · 5 steps

## Before you start

- The [root certificate](https://pkizerotohero.com/handbook/glossary/#root-certificate) of your [private CA](https://pkizerotohero.com/handbook/glossary/#private-ca) as a `.cer`, `.crt` or `.pem` file (DER or PEM). Only the public certificate, never a `.p12` or a private key.
- Its SHA-256 [fingerprint](https://pkizerotohero.com/handbook/glossary/#fingerprint), from a channel you trust (your PKI team’s HTTPS intranet page, a signed ticket), not from the message that carried the file.
- An administrator account on the Mac. macOS asks for an administrator’s password to change trust; `sudo` alone is not enough.
- For a fleet: a device management (MDM) service.

Why it matters: a trusted root can vouch for **any** name in Safari, Chrome and every app that asks macOS to check certificates. Add only roots your organisation controls.

## Steps

### 1. Check the file before you trust it

Compute the fingerprint and compare it with the published value. Hashing the DER form gives the certificate’s fingerprint, and works with the LibreSSL `openssl` that ships with macOS:

```bash
openssl x509 -in corp-root.crt -outform DER | shasum -a 256
```

```text
bc02a4f5dc68b3fdcdc478030e308cb59d6da0845aa0b4333660788e96c22e01  -
```

Then check that it really is a root CA: subject and issuer are the same, and the CA flag is set. With OpenSSL 3 (for example from Homebrew) the output for our throwaway test root is:

```bash
openssl x509 -in corp-root.crt -noout -subject -issuer -enddate -ext basicConstraints
```

```text
subject=C=BE, O=Example Corp, CN=Example Corp Root CA
issuer=C=BE, O=Example Corp, CN=Example Corp Root CA
notAfter=Oct 17 01:43:50 2036 GMT
X509v3 Basic Constraints: critical
    CA:TRUE
```

The decoder below shows the same fields, in your browser. Paste certificates only, never a private key.

*Interactive tool: Certificate decoder (runs in the browser at [/tools/decoder/](https://pkizerotohero.com/tools/decoder/))*

### 2. Add it with Keychain Access

This follows [Apple’s Keychain Access guide](https://support.apple.com/guide/keychain-access/add-certificates-to-a-keychain-kyca2431/mac):

1. Open **Keychain Access** (search for it with Spotlight).
2. Select the **System** keychain, so the root applies to every account on the Mac. The **login** keychain covers only you.
3. Drag the certificate file onto the Keychain Access window, and type an administrator’s name and password.
4. Double-click the certificate, click the arrow next to **Trust**, and set **When using this certificate** to **Always Trust** ([changing trust settings](https://support.apple.com/guide/keychain-access/change-the-trust-settings-of-a-certificate-kyca11871/mac)). Close the window and enter the password again.

Adding the certificate is not enough on its own: until its trust setting says **Always Trust**, macOS treats it as an ordinary, untrusted certificate.

### 3. Or add it from the Terminal

The `security` command adds the certificate and its trust setting in one step. `-d` writes the admin trust settings (for all users), `-r trustRoot` marks it as a trusted root, and `-k` names the keychain:

```bash
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain corp-root.crt
```

Since macOS Big Sur, this still opens a dialog for an administrator’s password. Apple’s [Big Sur release notes](https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes) say “Running as the root user alone is no longer sufficient to modify certificate trust”. So the command cannot run unattended from a script; use MDM for that (step 4).

For your own account only, leave out `sudo` and `-d` and name your login keychain:

```bash
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db corp-root.crt
```

### 4. Or roll it out with MDM

On managed Macs, put the root in a **Certificates** payload of a configuration profile. Apple’s [Certificates payload settings](https://support.apple.com/guide/deployment/certificates-payload-settings-dep91d2eb26/web) say a certificate gets full trust automatically when it is “Automatically installed from a supported device management service”, or by a payload in an MDM enrollment profile. Apple also advises: “avoid having users manually install certificates”.

The same profile works for iPhone and iPad. A root that a user installs by hand there also needs **Settings** > **General** > **About** > **Certificate Trust Settings** ([Apple](https://support.apple.com/en-us/102390)).

### 5. Point the apps that ignore the keychain

| Program | Uses the macOS trust settings? | What to do |
| --- | --- | --- |
| Safari, Mail, apps using Apple’s networking | Yes | Nothing |
| Chrome, Edge | Yes: roots in the System and login keychains marked **Always Trust** ([Chrome Root Store FAQ](https://chromium.googlesource.com/chromium/src/+/main/net/data/ssl/chrome_root_store/faq.md)) | Restart the browser |
| Firefox | Yes, by default since Firefox 120 ([Mozilla](https://wiki.mozilla.org/CA/AddRootToFirefox)) | Restart Firefox |
| Node.js, npm | No | `node --use-system-ca` reads the keychain ([Node.js](https://nodejs.org/api/cli.html#--use-system-ca)), or `NODE_EXTRA_CA_CERTS`: see [Python, Node.js and npm](https://pkizerotohero.com/guides/trust-ca-python-node/) |
| pip 24.2 and later | Yes, in addition to its own bundle | Nothing |
| Python `requests`, python.org’s Python | No, they use the certifi bundle | See [Python, Node.js and npm](https://pkizerotohero.com/guides/trust-ca-python-node/) |
| Java | No, it uses `cacerts` | See [Java trust stores](https://pkizerotohero.com/guides/add-root-ca-java/) |
| Homebrew’s OpenSSL and the tools built on it | Only from a snapshot | After adding the root to the System keychain, run `brew postinstall ca-certificates` to rebuild Homebrew’s bundle |
| Docker containers | No, they are Linux | See [Linux](https://pkizerotohero.com/guides/add-root-ca-linux/) |

## Check it worked

List the admin trust settings. Your root should appear with its name:

```bash
security dump-trust-settings -d
```

Confirm which certificate is in the System keychain, by fingerprint:

```bash
security find-certificate -a -c "Example Corp Root CA" -Z /Library/Keychains/System.keychain | grep SHA-256
```

Then let macOS’s own trust evaluation check a real internal server. Given a URL, `security verify-cert` connects and checks the server’s chain against the SSL server policy ([security(1)](https://github.com/apple-oss-distributions/Security/blob/main/SecurityTool/macOS/security.1)):

```bash
security verify-cert https://intranet.example.com/
```

A successful verification means the chain reaches your root. Finally, open the site in Safari and in Chrome.

## If something goes wrong

- **Safari or Chrome still show [`NET::ERR_CERT_AUTHORITY_INVALID`](https://pkizerotohero.com/errors/chrome-err-cert-authority-invalid/).** The certificate is in a keychain but not set to **Always Trust**, it went to the login keychain of another user, or the server’s chain ends at a different root. Compare the issuer in the browser’s certificate viewer with your file.
- **Firefox says [`SEC_ERROR_UNKNOWN_ISSUER`](https://pkizerotohero.com/errors/firefox-sec-error-unknown-issuer/).** Check that `security.enterprise_roots.enabled` is `true` in `about:config`, or that your Firefox policy does not turn it off.
- **The browser works, but the command line fails** with [`self-signed certificate in certificate chain`](https://pkizerotohero.com/errors/curl-self-signed-certificate-in-certificate-chain/) or [`CERTIFICATE_VERIFY_FAILED`](https://pkizerotohero.com/errors/python-certificate-verify-failed/). That tool reads its own bundle: see step 5.
- **The command line works, but some clients report “unable to get local issuer certificate”.** The server leaves out its intermediate. Fix the server: [build the full chain file](https://pkizerotohero.com/guides/build-fullchain/) and see [verify error 21](https://pkizerotohero.com/errors/openssl-verify-error-21/).
- **`add-trusted-cert` fails in a script or over SSH.** That is the Big Sur change in step 3: it needs an administrator to authenticate in a dialog. Use an MDM profile.

Warning

Remove roots you no longer need

A retired or test root keeps vouching for any name until you delete it. Remove the trust setting with `sudo security remove-trusted-cert -d corp-root.crt`, then delete the certificate in Keychain Access, or remove it from the MDM profile. Prefer a private CA whose certificates carry [name constraints](https://pkizerotohero.com/handbook/glossary/#name-constraints), so that it can only vouch for your own domains.

## Why this works

macOS keeps certificates in keychains and decides trust separately, through trust settings in a user, admin or system domain. When an app asks macOS to check a server, it builds a [certificate chain](https://pkizerotohero.com/handbook/glossary/#certificate-chain) and accepts it only if it ends at a certificate whose trust setting says **Always Trust**. Being self-signed does not make your root trustworthy; the trust setting does ([Who do we trust?](https://pkizerotohero.com/handbook/trust-stores/)). How the chain is checked is in [Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/), and how to design a private CA worth trusting is in [A CA of your own](https://pkizerotohero.com/handbook/private-ca/).

## Go deeper

- [Keychain Access: add certificates](https://support.apple.com/guide/keychain-access/add-certificates-to-a-keychain-kyca2431/mac) and [change trust settings](https://support.apple.com/guide/keychain-access/change-the-trust-settings-of-a-certificate-kyca11871/mac): Apple’s step-by-step guides.
- [security(1) manual](https://github.com/apple-oss-distributions/Security/blob/main/SecurityTool/macOS/security.1): `add-trusted-cert`, `remove-trusted-cert`, `dump-trust-settings` and `verify-cert`, from Apple’s open-source Security project.
- [macOS Big Sur 11.0.1 release notes](https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes): why changing trust needs an administrator’s password.
- [Certificates payload settings](https://support.apple.com/guide/deployment/certificates-payload-settings-dep91d2eb26/web): deploying roots with MDM.
- [Chrome Root Store FAQ](https://chromium.googlesource.com/chromium/src/+/main/net/data/ssl/chrome_root_store/faq.md): which keychain settings Chrome honours.

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/guides/add-root-ca-macos/).

Last reviewed 20 October 2026Edition 0.9

## Keep going

### Learn why it works

- [Chapter 12: Who do we trust? CAs, trust stores and self-signed certificates](https://pkizerotohero.com/handbook/trust-stores/)
- [Chapter 22: A CA of your own: private two-tier PKI](https://pkizerotohero.com/handbook/private-ca/)
- [Chapter 13: Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/)

### Error pages

- [NET::ERR\_CERT\_AUTHORITY\_INVALID](https://pkizerotohero.com/errors/chrome-err-cert-authority-invalid/)
- [SEC\_ERROR\_UNKNOWN\_ISSUER](https://pkizerotohero.com/errors/firefox-sec-error-unknown-issuer/)
- [SSL certificate problem: self-signed certificate in certificate chain](https://pkizerotohero.com/errors/curl-self-signed-certificate-in-certificate-chain/)
- [\[SSL: CERTIFICATE\_VERIFY\_FAILED\] certificate verify failed: unable to get local issuer certificate](https://pkizerotohero.com/errors/python-certificate-verify-failed/)

### Tools

- [Certificate decoder](https://pkizerotohero.com/tools/decoder/): Paste a PEM certificate and read every field in plain words.
- [Chain checker](https://pkizerotohero.com/tools/chain/): Paste a chain and see which link is missing, expired or out of order.

### Topics

- [Chains and trust stores](https://pkizerotohero.com/topics/chains-and-trust/)
- [Private PKI](https://pkizerotohero.com/topics/private-pki/)

## More guides: Trust a CA

- [How to work behind a TLS-inspecting corporate proxy, safely](https://pkizerotohero.com/guides/corporate-proxy-certificates/)
- [How to trust a private root CA on Windows](https://pkizerotohero.com/guides/add-root-ca-windows/)
- [How to trust a private root CA on Linux](https://pkizerotohero.com/guides/add-root-ca-linux/)
- [How to add a CA to a Java trust store (cacerts)](https://pkizerotohero.com/guides/add-root-ca-java/)
- [How to make Python, Node.js and npm trust a private CA](https://pkizerotohero.com/guides/trust-ca-python-node/)
