Issue with Keychain sharing in Mac

I have 2 Mac apps. My requirement is to create a RSA asymmetric private key and store it to the default login key chain from App 1. Now when App 2 tries to retrieve this key it should be able to retrieve this without any login prompt (Key chain asks for login when apps(non owner) try to access it).

In order to solve this the first solution I tried is to edit the ACL (Access Control List)while creating the key and include the apps which tries to access this key. This works as well. However, this can’t be used since the TrustedApplication is created using SecTrustedApplicationCreateFromPath API. This API is deprecated in 10.15 and there is no way to create a SecTrustedApplication and we will not be able to use this anymore. Apple has not mentioned an alternative. It seems Apple no more wants users to manipulate the ACL from within an application without asking for the user prompt since Catalina(10.15).

So the solution which I ended up implementing is Keychain sharing and I followed the Apple documentation:

https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps?language=objc

App 1 creates the key and saves the private key in keychain. App 2 tries to access it and generate public key from it.
I have created the Keychain access group in both the app and ensured that the App identifier prefix is the same for both these apps.

Attributes for Key creation from App 1:


    NSString *accessGroup = [NSString stringWithFormat:@"%@com.sample.keychaingroup",appIdentifierPrefix];

NSDictionary* attributes = @{
(id)kSecAttrLabel:              @"Sample Private Key",
       (id)kSecAttrKeyType:               (id)kSecAttrKeyTypeRSA,
       (id)kSecAttrKeySizeInBits:         @(2048),
       (id)kSecAttrAccessGroup: accessGroup,
           (id)kSecPrivateKeyAttrs:
           @{
               (id)kSecAttrApplicationTag: tag,
               (id)kSecAttrIsPermanent:    @YES,
               (id)kSecAttrIsExtractable : @YES,
              (id)kSecAttrDescription:  @"Sample Key Desc",
           },

    }

And key is created using:
SecKeyRef privateKey = SecKeyCreateRandomKey((_bridge CFDictionaryRef)attributes,  &error);

The creation part works perfectly fine and I can see the key(Sample Private Key) in the keychain.

Now to access this from App 2, I make use of the following:

    NSDictionary *queryDict = @{

         (id)kSecClass: (id)kSecClassKey,
         (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
         (id)kSecReturnRef: @YES,
         (id)kSecAttrApplicationTag: same tag used above,
         (id)kSecUseDataProtectionKeychain: @YES,

    };

OSStatus status = SecItemCopyMatching((
_bridge CFDictionaryRef)queryDict, (CFTypeRef *)&fullKey);

Here I always receive the status as -25300, which means the item is not found in the keychain.

The kSecUseDataProtectionKeychain is used here because as per Apple documentation it is needed for Keychain sharing to work in Mac.

Here I tried n number of permutation and combinations for the query, but never ended up in succeeding. If I remove the kSecUseDataProtectionKeychain, it works as earlier. It shows the prompt when we try to access the public key. We get the fullKey value, but following method will show the prompt. I also tried including the access group while querying, but none of it helped.

publicKey = SecKeyCopyPublicKey(fullKey);

I can't exactly figure out what is going wrong at my end, and feeling kind of stuck here. If anyone has any insights to this, please share your thoughts.


The creation part works perfectly fine and I can see the key … in the
keychain.

You mean in Keychain Access, right? If so, where in Keychain Access? What does the Keychain column show for this item?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Yes in the Keychain access. The private key gets created in the Login Keychain and the Access control list shows the name of the owner app in it.

Name : Sample Private Key
Kind: private key
Keychain: login

The private key gets created in the Login Keychain

Right, that’s what I suspected. The issue here is that you’re generating your key in the file-based keychain and then trying to share it via a keychain access group, which only makes sense for the iOS-style keychain. You’ll need to change your generation code to put your key in the iOS-style keychain.

I’m not sure if it’s possible to do this using SecKeyCreateRandomKey directly. Passing in kSecUseDataProtectionKeychain might just work. If not, you should create an ephemeral key (remove the kSecAttrIsPermanent attribute) and then manually add the resulting SecKeyRef to the iOS-style keychain using SecItemAdd.

Share and Enjoy

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

Thanks a lot for your info.
Sorry for the late reply. I manged to get this working using SecKeyCreateRandomKey.

Query used for key creation:

Code Block
NSDictionary *writeQueryAttributes = @{
(id)kSecAttrLabel:                 @"Sample Label",
       (id)kSecAttrKeyType:               (id)kSecAttrKeyTypeRSA,
       (id)kSecAttrKeySizeInBits:         @(2048),
       (id)kSecAttrAccessGroup: accessGroup,
       (id)kSecUseDataProtectionKeychain: @YES,
       (id)kSecReturnRef: @YES,
       (id)kSecPrivateKeyAttrs:
           @{
               (id)kSecAttrApplicationTag: tag,
               (id)kSecAttrIsPermanent:    @YES,
           },
    };
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)writeQueryAttributes, &error);


Query used to accessing key:

Code Block
NSDictionary *readQueryAttr = @{
         (id)kSecClass: (id)kSecClassKey,
         (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
         (id)kSecReturnRef: @YES,
         (id)kSecAttrAccessGroup: [self getKCAccessGroup],
         (id)kSecAttrApplicationTag: tag,
         (id)kSecUseDataProtectionKeychain: @YES,
    };
SecKeyRef fullKey = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)queryDict, (CFTypeRef *)&fullKey);
CFErrorRef error;
NSData *publicKeyData = (NSData *)CFBridgingRelease( SecKeyCopyExternalRepresentation(publicKey, &error)


As you mentioned here, I used the kSecUseDataProtectionKeychain in the creation query.
Now I am able to get the key created and also access it back.

So earlier when I used to do this, and when I dint see it in the Login keychain I assumed that the key is not created in the first place. Now I realised that the key is created, but it is not in the login keychain , instead it is in a different keychain.

I manged to get this working using SecKeyCreateRandomKey.

Yay!

instead it is in a different keychain.

Right. This is the iOS-style keychain database. Keychain Access shows you some items in there under the label iCloud (or Local Items if you have iCloud Keychain disabled) but I believe it only shows password items, not keys and certificates.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Yes. I tried with a generic password first and I could see it in the iCloud Keychain in the keychain access app. But this key which was generated I could not see it in the keychain access app.
In that case, which would be the exact physical location for this key?

In that case, which would be the exact physical location for this key?

In your accounts iOS-style keychain database. I believe you’ll find it in Library/Keychains/ but its location and format isn’t something we consider to be API.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Issue with Keychain sharing in Mac
 
 
Q