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
.
Retrieving certificates from System Roots 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"