DEV Community

Cover image for I Trust 158 Root Certificates. I Installed Three of Them
Mustafa ERBAY
Mustafa ERBAY

Posted on Originally published at mustafaerbay.com.tr

I Trust 158 Root Certificates. I Installed Three of Them

If someone asked me "how many certificate authorities do you trust?", my honest answer for years would have been "no idea, whatever Apple says." I see the padlock in the browser, I move on. I assume somebody back there has thought about it.

This morning I sat down and counted that assumption. Trust isn't a feeling, after all; it's a list sitting on my machine. And despite two decades in this line of work, I had never once opened that list.

What surprised me when I did wasn't how long it was. It was that the number turned out not to be a single number.

I asked the same question in four places and got four answers

The macOS root store is a keychain file on disk. Counting what's inside is one command:

security find-certificate -a -p \
  /System/Library/Keychains/SystemRootCertificates.keychain \
  | grep -c 'BEGIN CERTIFICATE'
# 158
Enter fullscreen mode Exit fullscreen mode
  1. A nice, clean number. Then I asked the trust settings the same question:
security dump-trust-settings -s 2>/dev/null | head -1
# Number of trusted certs = 157
Enter fullscreen mode Exit fullscreen mode
  1. One short. A certificate that lives in the store but doesn't make it into the "trusted" count. I put the third question to the names: deduplicating the labels of those 158 certificates left me with 155. And the fourth question was this — how many of the 158 did I put there? Answer: none. The ones I added aren't even in this file; they live in entirely separate places, and there are three of them.

So: same question, and the answers are 158, 157, 155 and 3. None of them is wrong. Each measures something different. Ever since the day I asked five different tools how much free disk space I had and found a 13.5 GiB spread, this no longer surprises me — but when the subject is trust, it does make you wince a little.

That extra certificate in the store isn't a bug

Tracking down the gap between 158 and 157 took some digging, but I liked the answer. I opened all 158 certificates in the store and compared the subject field against the issuer field. In 157 of them the two matched — self-signed. In exactly one, they didn't:

subject= CN=Developer ID Certification Authority, O=Apple Inc., C=US
issuer = CN=Apple Root CA, O=Apple Inc., C=US
notAfter= Feb  1 22:12:15 2027 GMT
Enter fullscreen mode Exit fullscreen mode

This is Apple's developer signing intermediate. It lives in the root store but it isn't a root; it has a parent, and that parent — Apple Root CA — sits in the same store. Its name never appears in the security dump-trust-settings -s output, because that command lists anchors, not residents of the store.

There's a subtle but important distinction here. RFC 5914 defines a trust anchor as "an authoritative entity represented by a public key and associated data"; the anchor is an input to path validation, not a link in the chain. An intermediate's trustworthiness doesn't come from itself, it comes from Apple Root CA. Sure enough, validation passes cleanly:

security verify-cert -c devid.pem -p basic
# ...certificate verification successful.
Enter fullscreen mode Exit fullscreen mode

That's why 157 is the more honest number: it counts the points this machine accepts without question. The intermediate is parked there for convenience.

The naming side has a similar trap. There are four separate certificates whose label is exactly GlobalSign; the distinguishing information isn't in the CN but tucked away in the OU field (Root CA - R3, Root CA - R6, ECC Root CA - R5, ECC Root CA - R4). And if you search for certificates with "GlobalSign" anywhere in the CN, you get nine. Four or nine? Both are correct; one is an exact match, the other a substring search. All 158 certificates have distinct SHA-256 fingerprints — what repeats isn't the certificate, it's the name shown to the human.

While comparing the two lists, by the way, I walked straight into a trap of my own making. Subtracting the trust list from the store list showed two "missing" names, and for a moment I was genuinely excited. The second one was real (the intermediate above); the first was entirely my own fault. NetLock Arany, a Hungarian root, has non-ASCII characters in its name, and the two commands print the same certificate two different ways — one as a hexadecimal blob, the other as proper UTF-8. Same certificate, two spellings, one phantom finding. When you're measuring, the most expensive mistakes always surface where the tools disagree with each other.

Counting certificates is not counting trust

After finishing with the system store I looked at the other keychains on the machine, and that's where I got the actual lesson.

/Library/Keychains/System.keychain holds six certificates. Four are familiar: Apple's system identity, its Kerberos KDC, the developer relations certificate, and the AdGuard root we'll get to below. The fifth is a customer's internal root. The sixth one's name tells me nothing at all:

subject= CN=Self-Signed-7C9F7AE3
notBefore= Dec  2 08:06:00 2020 GMT
notAfter = Nov 30 08:06:00 2030 GMT
X509v3 Basic Constraints:
    CA:FALSE
Enter fullscreen mode Exit fullscreen mode

December 2020. Nearly six years ago. It may predate this machine; it may have been carried over in a migration. I don't know what it is, and writing that I don't know beats inventing a guess.

But here's the part that matters: CA:FALSE. This isn't a root, it's a leaf certificate. Its name doesn't appear in any of the three trust domains — I checked all three, zero hits. So it sits on my machine and nothing trusts it. A harmless leftover.

In my own user keychain things get more instructive still. There are 20 certificates there, and 12 of them are marked CA:TRUE — structurally capable of signing certificates. But when I look at the trust settings, the user domain contains only two roots.

I have twelve CA certificates and I trust two of them. The other ten are merely being stored; somebody sent me an organization's root, I dropped it into the keychain, and never trusted it. That's the correct behaviour — a pleasant surprise, even.

What follows from this is simple, and I had been assuming the opposite: you cannot measure trust by counting certificates. The keychain is a drawer; the trust settings are a separate ledger. Something being in the drawer doesn't mean it's written in the ledger. The sentence "I have 184 certificates on my machine" tells you, on its own, nothing at all.

Of the 157 roots, zero carry a decision of mine

Apple documents that it splits this list into three categories: trusted roots, ones that always ask, and blocked ones. Its wording for the blocked category is unambiguous — certificates believed to be compromised, which will never be trusted.

Curious, I went looking for these categories on my own machine. Across all three trust domains (system, admin, user) I couldn't find a single record returning Deny or an always-ask result. What I found instead was more interesting: every one of the 157 anchors in the system domain looks like this.

Cert 5: ISRG Root X1
   Number of trust settings : 0
Enter fullscreen mode Exit fullscreen mode

Zero. No explicit setting — implicit, blanket trust. Not one of the 157 roots carries a decision recorded by me or by any administrator of this machine. That isn't a bad thing in itself; Apple's store is managed through audited, public processes and can be updated remotely. But the subject of the sentence "I trust these" isn't me. I trust a list I inherited.

So are there any records carrying an explicit decision? Three: two in the user domain, one in the admin domain. All three are mine.

But the genuinely uncomfortable part surfaced right here. The AdGuard root, the protagonist of this post, also has an explicit setting count of zero. I carry it exactly the way I carry Apple's 157 — without being asked anything. A root I added with my own hands sits in precisely the same silence as the list I inherited.

Diagram

The three roots I added myself

Dumping the admin and user domains turned up three distinct roots. I installed all three at some point, and I had forgotten all three.

The first is my own work: kopru-root-ca, the root of the köprü platform I use to manage customer servers. Self-signed, CA:TRUE, pathlen:0, generated on 9 July 2026, valid until 6 July 2036. I put this there deliberately, I know what it does, and I know where the key behind it lives. No complaints.

The second is a customer's internal root CA. I'm not naming it here — the name of an organization's internal infrastructure doesn't need to appear in my blog post. What makes it interesting is this: it's registered in both the admin and the user domain. Same root, two places. I don't remember adding it twice. Most likely it went in once by hand and once with a tool's help during some setup.

The third one actually stopped me: Adguard Personal CA.

I apparently promised an ad blocker until 2045

I installed AdGuard to cut out ads. A reasonable request. During setup it told me I needed to install a certificate, and I said "sure, it has to filter HTTPS" and approved it. I haven't thought about it once since closing that dialog.

Today I looked at that certificate:

subject= C=EN, O=AdGuard, CN=Adguard Personal CA
notBefore= Jan 19 22:27:46 2025 GMT
notAfter = Jan 14 22:27:46 2045 GMT
X509v3 Basic Constraints: critical
    CA:TRUE
Enter fullscreen mode Exit fullscreen mode

Twenty years. 2045. The first thing that struck me reading that line was that this laptop will long since have been scrapped by then. The certificate is written to outlive the computer it sits on. I added this root thinking "for now"; the document says "for life."

And this root is not dormant. AdGuard is running right now, with three processes open including a system extension:

/Applications/Adguard.app/Contents/MacOS/Adguard
com.adguard.mac.adguard.network-extension.systemextension
com.adguard.mac.adguard.helper
Enter fullscreen mode Exit fullscreen mode

A trusted root flagged CA:TRUE can sign a certificate for any domain. Including my bank's. I'm not claiming AdGuard is malicious — I have no evidence of that. What I'm saying is structural: if that key is ever leaked or abused, it carries the same authority on my machine as DigiCert. The difference is that DigiCert is subject to public audits, transparency logs and a revocation mechanism. The only person auditing AdGuard's root is me, and I didn't check it once in twenty months.

CISA's warning on this isn't new — it dates from 2017 and sits in their archive — but the mechanism hasn't changed. The gist is that products performing HTTPS inspection have to install a trusted certificate on the device in order to avoid showing the client warnings, and that many of these products don't properly verify the server's certificate chain. In other words, the layer in the middle may be telling you "this connection is secure" without looking as carefully as you would. From that point on, the padlock describes the intermediary, not the far end.

This is not a call to delete AdGuard. Filtering ads and trackers is a real benefit and I wanted that benefit. But making the trade knowingly isn't the same as approving it on a setup screen and forgetting. I had done the second.

And then there are the expiry dates

Once you start counting it's hard to stop. I pulled the end dates for all 158 certificates: none have expired as of today, and the furthest runs until 9 December 2054. But the nearest is 27 November 2026 — less than eight weeks away. Three roots will lapse before 2028; the fourth certificate expiring in that window is the Apple intermediate we already met, in February 2027.

Those dates aren't my problem; Apple handles them with updates. For the three roots I added myself, nobody is standing behind me. I'm the one who has to track what happens to those keys in 2036 and 2045, and I have hard data on my performance so far: twenty months, zero checks.

If you want to read your own list

These commands are all read-only; none of them changes anything:

# How many certificates are in the store
security find-certificate -a -p \
  /System/Library/Keychains/SystemRootCertificates.keychain \
  | grep -c 'BEGIN CERTIFICATE'

# The anchors the system trusts without question
security dump-trust-settings -s

# THE TWO COMMANDS THAT ACTUALLY MATTER: what was added by hand
security dump-trust-settings -d   # admin domain
security dump-trust-settings      # user domain
Enter fullscreen mode Exit fullscreen mode

The last two are the important ones. The first two show you Apple's work; the last two show you yours. For every name that comes up you should have an answer to three questions: why did I add this, is it still needed, and when does it expire? If you can't answer all three, that root hasn't earned its place on the list.

The distinction that helped me decide what to do with those names was this. If there's somebody other than you behind a root — an audited public CA, your organization's IT team, a process that lands in transparency logs — then there's a structure carrying the risk, and your job is just to know the list. But if you installed the root, you are its only caretaker, and that has three concrete consequences: you have to know where the key lives, you have to put its expiry in your calendar, and you have to delete the root the day you stop using the tool. The third is the most commonly skipped. Removing a tool often doesn't revoke the trust record; the application leaves, the root stays.

The second distinction concerns intermediary layers. Ad blockers, corporate filters, debugging proxies — they all use the same mechanism and they all raise the same question: is this layer actually validating the server's certificate on my behalf? That was the sharp edge of CISA's warning. If you don't know the answer, you need to walk back some of the meaning you've attached to that padlock.

For me, this round came out like this: the köprü root stays, its rationale is clear. I'll deduplicate the customer root's twin records across the two domains. AdGuard's root stays too — but now knowingly; its expiry is going into my calendar.

Trust is the sum of decisions you don't remember

What I noticed while writing this is that the number 157 never bothered me. Those aren't my decisions; they're a list Apple audits, can revoke, and updates. There's risk, but there's an owner.

The three bother me. Because I'm their owner and I was absent. Everyone who likes tinkering with infrastructure has a layer of sediment like this on their machine: a certificate, a proxy, a port opened for a test. Taking root of trust in server infrastructure seriously while not opening the root store on my own laptop for twenty months is a strange inconsistency. I'd made the same kind of slip while writing that identity doesn't come from network location: there I was insisting that "it came from Cloudflare" doesn't mean "it came for me," while here I handed out twenty years of authority because a setup screen asked for it.

Most security writing tells you to add something new. This morning's lesson was the reverse: reading the list once taught me more than installing another tool would have. Four commands, one morning, three forgotten roots.

Go and look at yours. There's probably somebody on your list too, and you probably invited them.

Official Sources

Top comments (0)