How do you programatically retrieve a list of SecKeychainRef / keychains?

I need to programmatically add an item to a keychain using SecItemAdd. I am told to use kSecUseKeychain to specify which keychain.

I also need to programmatically retrieve items from a keychain using SecItemCopyMatching, and I am told I need kSecMatchSearchList to do this.

Where I am stuck is I need to be nice to the end user and allow them to choose a keychain using a friendly name.

The document https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains mentions that SecKeychainRef's are needed, but not where they come from.

What calls do I need to use to enumerate the keychains on MacOS?

Most specifically, what calls do I need to use to programmatically retrieve the following list, containing "login", "iCloud", "System" and "System Roots".

I am aware that some functions are deprecated.

Replies

What calls do I need to use to enumerate the keychains on macOS?

The APIs in <Security/SecKeychain.h>, and specifically SecKeychainCopyDomainSearchList to get the list and SecKeychainCopyDomainDefault to get the default.

Share and Enjoy

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

So SecKeychainCopyDomainSearchList takes a SecPreferencesDomain, which is an enumeration of user, system, common and dynamic.

If I start with user, I get one entry back:

<SecKeychain 0x101604b70 [0x7ff84e723800]>

I only appear to be able to get the path of this SecKeychain with SecKeychainGetPath, which gives me what looks like a path on the filesystem like this:

/Users/minfrin/Library/Keychains/login.keychain-db

How do I get the name "login" from the above, do I have to parse the filename? That doesn't make sense for keychains not backed by a file, I feel like I a missing an API call or an attribute I should be requesting for the name, the docs say nothing on this.

The system enumeration gives me one keychain, with a path as follows:

/Library/Keychains/System.keychain

The common enumeration gives me the same as the system enumeration, but I can't see any explanation as to why:

/Library/Keychains/System.keychain

The dynamic enumeration gives me nothing (not tried it with any smartcards plugged in).

How does this map onto the Keychain Access application?

Neither "iCloud" nor "System Roots" appear anywhere as keychains, are these handled as special cases? Where do their names "iCloud" and "System Roots" come from, are they returned by an API or are they hardcoded?

What is the correct way to query iCloud as a keychain using SecItemCopyMatching?

I only appear to be able to get the path of this SecKeychain with SecKeychainGetPath

Correct.

How do I get the name login from the above, do I have to parse the filename?

There’s no API to get the user-visible name for a keychain. For a file-based keychain one typically shows the file name. I usually do this by getting the localizedNameKey property. I don’t think that’ll hide the extension by default, so you’ll want to drop the extension first.

That doesn't make sense for keychains not backed by a file

Correct. This whole mechanism assumes the file-based keychain. There’s no way to get a localised name for the data protection keychain. You would have to hard code that, which is tricky because you can’t well whether iCloud Keychain is enabled or not.

Note If you’re not familiar with these terms, see TN3137 On Mac keychain APIs and implementations.

What is the correct way to query iCloud as a keychain using SecItemCopyMatching?

TN3137 talks about this.

How does this map onto the Keychain Access application?

It doesn’t. Keychain Access is built in to macOS and, as such, it can do things that a third-party app can’t do.

What is your high-level goal here? If you’re trying to clone Keychain Access, that’s not going to work. Specifically, you might be able to create a reasonable clone of its file-based keychain functionality, but you will hit serious roadblocks when it comes to the data protection keychain.

Share and Enjoy

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

There’s no API to get the user-visible name for a keychain. For a file-based keychain one typically shows the file name. I usually do this by getting the localizedNameKey property. I don’t think that’ll hide the extension by default, so you’ll want to drop the extension first.

Will try this.

It doesn’t. Keychain Access is built in to macOS and, as such, it can do things that a third-party app can’t do.

Is it true to say that the iCloud keychain (the one visible in Keychain Access, not private groups defined by SecAccessControl) is hidden from all 3rd party apps?

The end goal is to add keychain support to Redwax Tool at https://redwax.eu/rt/, which reads certificates and keys from all the places, matches them all up in ways you define, and then writes them back to all the places. It seems that supporting anything other than file based keychains is not possible.

Is it true to say that the iCloud keychain … is hidden from all 3rd party apps?

No.

iCloud Keychain is a user-level term for the data protection keychain. The data protection keychain is available for third-party apps to use. However, their use of that keychain is mediated by entitlements.

It is true to say that “No third-party app can access all items in the data protection keychain.” Keychain Access is built in to macOS and thus has special capabilities.

Share and Enjoy

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