How to add and use SecIdentityRef with kSecAccessControlUserPresence requirement

When I add a password to the keychain as follows:
Code Block
   SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                        kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                        kSecAccessControlUserPresence, &error);
  NSDictionary *attributes = @{
    (bridge id)kSecClass: (bridge id)kSecClassGenericPassword,
    (bridge id)kSecValueData: [@"password" dataUsingEncoding:NSUTF8StringEncoding],
    (bridge id)kSecAttrLabel: @"passwordWithACL",
    (bridge id)kSecUseNoAuthenticationUI: @YES,
    (bridge id)kSecAttrAccessControl: (bridge_transfer id)sacObject
  };
  OSStatus status = SecItemAdd((bridge CFDictionaryRef)attributes, nil);

attempts to access via SecItemCopyMatching as below work fine. The user is prompted to enter device PIN and errSecSuccess is returned.
Code Block
   NSDictionary *attributes2 = @{
    (bridge id)kSecClass: (bridge id)kSecClassGenericPassword,
    (bridge id)kSecReturnData: @YES,
    (bridge id)kSecUseOperationPrompt: @"Authenticate",
    (bridge id)kSecAttrLabel: @"passwordWithACL",
  };
  CFTypeRef dataTypeRef = NULL;
  OSStatus status = SecItemCopyMatching((bridge CFDictionaryRef)(attributes2), &dataTypeRef);


When I add an identity using this similar code:

Code Block
   SecAccessControlRef access =
    SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                    kSecAccessControlUserPresence,
                    nil);
   
  NSMutableDictionary* dict = [[NSMutableDictionary alloc]init];
  [dict setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnPersistentRef];
  [dict setObject:(bridge id)identity forKey:(id)kSecValueRef];
  [dict setObject:@"identityWithACL" forKey:(id)kSecAttrLabel];
  [dict setObject:(bridge id)access forKey:(id)kSecAttrAccessControl];
  CFTypeRef persistent_ref;
  OSStatus status = SecItemAdd((CFDictionaryRef)dict, &persistent_ref);

attempts to access via SecItemCopyMatching as below fails. The user is prompted to enter device PIN and errSecItemNotFound is returned.

Code Block
   NSMutableDictionary * query = [[NSMutableDictionary alloc] init];
  [query setObject:(id)kSecClassIdentity forKey:(id)kSecClass];
  [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnRef];
  [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
  [query setObject:@"Authenticate to access identity" forKey:(id)kSecUseOperationPrompt];
  [query setObject:@"identityWithACL" forKey:(id)kSecAttrLabel];
  CFTypeRef items = nil;
  OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&items);


When I replace the kSecAttrAccessControl item in the dictionary passed SecItemAdd with the below, I can access the identity with no issue.

Code Block
[dict setObject:(id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly forKey:(id)kSecAttrAccessible];


What's the trick for adding identities with user presence required for access?


Answered by DTS Engineer in 641883022
I suspect that the issue here is that identities aren’t stored in the keychain as a real item but rather the private key and certificate are stored separately. If you get the private key and the certificate from the identity (using SecIdentityCopyPrivateKey and SecIdentityCopyCertificate) and then add them separately, specifying the access control object when you add the key, does that work?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Accepted Answer
I suspect that the issue here is that identities aren’t stored in the keychain as a real item but rather the private key and certificate are stored separately. If you get the private key and the certificate from the identity (using SecIdentityCopyPrivateKey and SecIdentityCopyCertificate) and then add them separately, specifying the access control object when you add the key, does that work?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
That works very nicely. Thank you.
How to add and use SecIdentityRef with kSecAccessControlUserPresence requirement
 
 
Q