Though on macOS you could see both login and iCloud keychains, former is macOS-style keychain while latter is iOS-style keychain. macOS-style keychain items can be freely accessed by apps as long as the user consents. iOS-style items follow access group policies strictly.
Every key in the iCloud keychain is under an access group. An app. calling SecItemCopyMatching
should've said access group as an entitlement for a key to turn up in the query results.
Once you're sure the searched key's access group is among one of the access groups listed under your app's entitlements, there're a couple of attributes you've to pass to SecItemCopyMatching
query dictionary (in addition to the mandatory ones like kSecClass
):
kSecAttrAccessGroup
kSecAttrSynchronizable
If your app. is entitled to more than one access group, set the right group through kSecAttrAccessGroup
. Set kSecAttrSynchronizable
to kCFBooleanTrue
to search only iOS-style items (which are synchronized); setting it to kSecAttrSynchronizableAny
searches both synced and not-synced keys (like the ones in login keychain). Default is to search only local keys (kCFBooleanFalse
).
Example Query
This returns an array of dictionaries; one dictionary per key; all iCloud keys your app. has access to turn up. The key-value pairs in each dictionary are corresponding key's properties; this doesn't return the key's value 1
NSString* account = @"my_account";
NSString* service = @"some_service";
NSDictionary* query = @{
(id)kSecClass: (id) kSecClassGenericPassword,
// skip as this example app has only one access group
// (id)kSecAttrAccessGroup: (id) keychainAccessGroup,
(id)kSecAttrSynchronizable: (id)kCFBooleanTrue,
(id)kSecAttrAccount: (id) account,
(id)kSecAttrService: (id) service,
(id)kSecReturnAttributes: (id)kCFBooleanTrue,
(id)kSecMatchLimit: (id)kSecMatchLimitAll,
};
CFTypeRef result;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, &result);
if (status != errSecSuccess) {
CFStringRef err = SecCopyErrorMessageString(status, nil);
NSLog(@"Error: %@", err);
CFRelease(err);
} else {
NSArray* attributes_of_keys = (__bridge NSArray*)result;
}
Inspecting attributes_of_keys
under the debugger should tell you plenty.
Source: Apple Developer forum thread, SecItemCopyMatching
and beyond.
Apple has done a shoddy job of documenting the SecItem*
API family. Information is there but is strewn across different (irrelevant) places (some even outside the official documentation).
1: pass (id)kSecReturnData: (id)kCFBooleanTrue
to get key's data; however you can't use kSecReturnData
and kSecMatchLimitAll
together