Apple keychain stores client ID so that only my application can access it

goal

I need to store the client ID in an OS X application in a safe way so that only my application can access it. Do not request permission.

Problem

The problem arose immediately when I tried to store the client ID. Here is a sample code (which I attached to):

- (BOOL)saveClientIdentity:(SecIdentityRef)clientIdentity error:(NSError**) error { NSDictionary *attributes = @{ (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly, (__bridge id)kSecValueRef:(__bridge id)clientIdentity, (__bridge id)kSecAttrApplicationTag:[kMyKeychainAttrApplicationTag dataUsingEncoding: NSUTF8StringEncoding], (__bridge id)kSecAttrAccessGroup:kMyKeychainAttrAccessGroup }; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL); // status == -25299 … } 

I constantly get the code -25299 and the tool fixes the problem:

 $ security error -25299 Error: 0xFFFF9D2D -25299 The specified item already exists in the keychain. 

Therefore, it is trying to override the global client identifier (I have never deleted the client identifier for this application, so there should not be such a conflict), and I do not do this. It should be closed only for this application.

I checked what happens for the corresponding download code. It loads my developer id, and I don't want this.

 - (SecIdentityRef)clientIdentity { NSDictionary *attributes = @{ (__bridge id)kSecClass:(__bridge id)kSecClassIdentity, (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly, (__bridge id)kSecAttrApplicationTag:[kMyKeychainAttrApplicationTag dataUsingEncoding: NSUTF8StringEncoding], (__bridge id)kSecAttrAccessGroup:kMyKeychainAttrAccessGroup }; CFTypeRef universalResult = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributes, &universalResult); SecIdentityRef result = (SecIdentityRef)universalResult; if (result) { CFAutorelease(result); } if (status != noErr) { NSLog(@"Failed to load client identity: %@", NSErrorFromStatusErrorCode(status)); } return result; } 

Notes

I need to use the same code for iOS, but there should not be a problem here, since by default the iOS keychain is not used between applications.

0
source share
2 answers

I have found a good solution. The trick is to create a custom keychain, not to store the client ID in this keychain.

So basically there are tree steps.

  • First create or open your own keychain:

     NSString *keychainpath = self.customKeychainPath; unsigned char password[SHA_DIGEST_LENGTH]; GenerateCustomKeychainPassword(password); OSStatus status = SecKeychainCreate(keychainpath.UTF8String, SHA_DIGEST_LENGTH, password, NO, NULL, &customKeychain); if (status == errSecDuplicateKeychain) { status = SecKeychainOpen(keychainpath.UTF8String, &customKeychain); if (status == errSecSuccess) { status = SecKeychainUnlock(customKeychain, SHA_DIGEST_LENGTH, password, TRUE); if (status != errSecSuccess) { NSLog(@"%s Failed to unlock custom keychain: %@", __PRETTY_FUNCTION__, NSErrorFromStatusErrorCode(status)); } } } else if (status != errSecSuccess) { NSLog(@"%s Failed to unlock custom keychain: %@", __PRETTY_FUNCTION__, NSErrorFromStatusErrorCode(status)); } 
  • Then add the customer ID to this keychain

     OSStatus status = errSecSuccess; CFTypeRef persistent_ref = NULL; NSDictionary *dict = @{ (id)kSecValueRef:(id)secItem, (id)kSecReturnPersistentRef:(id)kCFBooleanTrue, #if !TARGET_OS_IPHONE (id)kSecUseKeychain:(__bridge id)customKeychain, #endif }; status = SecItemAdd((CFDictionaryRef)dict, &persistent_ref); NSCAssert(status != errSecParam, @"Wrong contents of dictionary"); if (status == errSecDuplicateItem) { NSLog(@"%s Item: %@ already exists", __PRETTY_FUNCTION__, secItem); return NULL; } return (CFDataRef)persistent_ref; 
  • And to read an item from the remote control ( persistent_ref can be saved in the default settings)

     NSDictionary *dict = @{ (id)kSecClass:(__bridge id)itemType,//kSecClassIdentity, (id)kSecReturnRef:(id)kCFBooleanTrue, (id)kSecValuePersistentRef:persistantRef, #if !TARGET_OS_IPHONE (id)kSecUseKeychain:(__bridge id)customKeychain, #endif }; OSStatus status = SecItemCopyMatching((CFDictionaryRef)dict, &result); NSCAssert(status != errSecParam, @"Invalid arguments"); return result; 
+1
source

I had great success with SSKeychain, which has recently been deprecated in favor of SAMKeychain. It works for both iOS and Mac, so it should also solve your cross-platform problem.

0
source

Source: https://habr.com/ru/post/1205067/


All Articles