Retrieving certificates from System Roots keychain

Hi there, I'm continuing to build up the API on keychain, I'm trying to implement the ability to create an own certificate chain for validation purposes, similar to ssl. To this extent I need to retrieve the certificates from the System's stores but I can't seem to find a way to do this in code? Creating a query with kSecMatchTrustedOnly only returns certificates which are seemingly manually marked as trusted or otherwise just skips over the System roots keychain. As far as I understand using kSecUseKeychain doesn't work either, since (besides SecKeychain being deprecated) it only works with SecItemAdd.

Answered by DTS Engineer in 816365022
This is MacOS

OK.

ideally I'd like to be able to support both Intel and ARM versions, though I assume that they should be the same?

Correct.

Historically macOS stored all trusted anchors in the System keychain. That’s not been the case for a while. The default trusted anchors now come from a separate trust store, shown in Keychain Access as System Roots. That is actually saved in a keychain [1], but that’s now considered an implementation detail [2]. The keychain is not in the default search list:

% security list-keychains
    "/Users/quinn/Library/Keychains/login.keychain-db"
    "/Library/Keychains/System.keychain"

That’s why SecItemCopyMatching isn’t finding these items.

Back in your original post you wrote:

I'm trying to implement the ability to create an own certificate chain for validation purposes, similar to ssl.

The canonical way to do this is with a trust object (SecTrust). You give it the leaf, zero on more intermediates, and zero or more additional trusted anchors, and it’ll try to build a chain of trust. This will consult the system trust store, so you don’t need direct access to it.

If you want to do your own trust evaluation, a trust object is the best path forward because it does all the standard stuff that users expect to just work. It handles fetching missing intermediates via AIA extension, custom trust settings, revocation, Certificate Transparency, and so on. Replicating all of this yourself is a lot of work, and your implementation will inevitably diverge from the built-in one.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] On macOS. On iOS this is different, and that’s one of the reasons why it’s important to treat this as an implementation detail.

[2] If you’re curious about the implementation, do this:

% security dump-keychain /System/Library/Keychains/SystemRootCertificates.keychain

What platform is this on?

This matters because iOS and its child platforms manage trusted anchors very differently than macOS.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

This is MacOS, ideally I'd like to be able to support both Intel and ARM versions, though I assume that they should be the same?

This is MacOS

OK.

ideally I'd like to be able to support both Intel and ARM versions, though I assume that they should be the same?

Correct.

Historically macOS stored all trusted anchors in the System keychain. That’s not been the case for a while. The default trusted anchors now come from a separate trust store, shown in Keychain Access as System Roots. That is actually saved in a keychain [1], but that’s now considered an implementation detail [2]. The keychain is not in the default search list:

% security list-keychains
    "/Users/quinn/Library/Keychains/login.keychain-db"
    "/Library/Keychains/System.keychain"

That’s why SecItemCopyMatching isn’t finding these items.

Back in your original post you wrote:

I'm trying to implement the ability to create an own certificate chain for validation purposes, similar to ssl.

The canonical way to do this is with a trust object (SecTrust). You give it the leaf, zero on more intermediates, and zero or more additional trusted anchors, and it’ll try to build a chain of trust. This will consult the system trust store, so you don’t need direct access to it.

If you want to do your own trust evaluation, a trust object is the best path forward because it does all the standard stuff that users expect to just work. It handles fetching missing intermediates via AIA extension, custom trust settings, revocation, Certificate Transparency, and so on. Replicating all of this yourself is a lot of work, and your implementation will inevitably diverge from the built-in one.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] On macOS. On iOS this is different, and that’s one of the reasons why it’s important to treat this as an implementation detail.

[2] If you’re curious about the implementation, do this:

% security dump-keychain /System/Library/Keychains/SystemRootCertificates.keychain

Alright thank you very much, I'll try to hack my way through and keep this open for now just in case.

Oh, hey, doesn't SecTrustCopyAnchorCertificates provide the functionality I described? It doesn't seem to be deprecated?

No. It gives you back the certificates you applied via SecTrustSetAnchorCertificates(_:_:), which doesn’t tell you anything you don’t already know.

The way to get the anchor is to evaluate trust and, if it’s successful, call SecTrustCopyCertificateChain(_:) and get the last item.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

If you want to do your own trust evaluation, a trust object is the best path forward because it does all the standard stuff that users expect to just work.

Unfortunately for what we need this approach doesn't suffice, as we don't get the information about the way certificates in the chain get verified, whether if it's through an OCSP response or with a CRL exclusion, which is necessary for next steps after we verify the certificate. We already have the necessary functionality internally that we rely on, and I'm simply porting it over to MacOS.

As for SecTrustSetAnchorCertificates the documentation mentions that it uses the certificates set by the function if it was called, otherwise it "uses a default set provided by the system". And, in fact, if I run a simple program that only calls the CopyAnchor it does what I expect and returns the array of all root certificates.

One caveat for me is that this seems to depend on global state and if a caller or callee at some point set their own anchor certificates for use with this API, we'll just get those in return. There is a mention that I can pass nil to the second argument of SetAnchor to restore the default set of anchor certificates, but it also takes in a SecIdentity object which for my purposes I do not have and cannot create, at least not within the context of where I'm writing the code.

I could generate a key for myself and create a self-signed certificate for the purpose of resetting the anchors and then disposing of the dummy certificate afterwards, but now I feel like I'm building a battering ram to open a fence.

I've just found out that the source code for Security is available and reading it, it occurs to me that the online documentation for SecTrustCopyAnchorCertificates has been erroneously copy-pasted from SecTrustCopyCustomAnchorCertificates. In fact, it seems SecTrustCopyAnchorCertificates must not depend on any global state besides the certificates stored in system's roots, meaning that it is the function I was looking for.

it occurs to me that the online documentation for SecTrustCopyAnchorCertificates has been erroneously copy-pasted from SecTrustCopyCustomAnchorCertificates.

Oh hey, I was also mixing those up!

The reason why I always forget about SecTrustCopyAnchorCertificates is that it’s macOS only. You won’t be able to deploy this code on other Apple platforms.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Retrieving certificates from System Roots keychain
 
 
Q