iOS8 + Apple's KeychainItemWrapper results in

2020-07-09 03:11发布

问题:

I've been digging around Apple forums and SO for this issue to no avail. Using Apple's KeychainItemWrapper (ARCified), trying to set the kSecAttrAccessible attribute to anything except the default (kSecAttrAccessibleWhenUnlocked) results in an assertion failure from SecItemUpdate returning an error.

KeychainItemWrapper *wrapper = [[KeyChainItemWrapper alloc] initWithIdentifier:kMyIdentifier accessGroup:nil];
[wrapper setObject:kMyServiceName forKey:(__bridge NSString*)kSecAttrService];
[wrapper setObject:kMyAccountToken forKey:(__bridge NSString*)kSecAttrAccount];
[wrapper setObject:(__bridge NSString*)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly forKey:(__bridge NSString*)kSecAttrAccessible];

The rest of our keychain updates seem to be going through fine, but the last line results in:

*** Assertion failure in -[KeychainItemWrapper writeToKeychain], /Users/john.hammerlund/.../KeychainItemWrapper.m:299

The assertion failure is due to SecItemUpdate() returning a status of -50, which appears to be a generic "invalid parameters" error.

Immediately setting the kSecAttrAccessible key has no impact, but setting it to the default kSecAttrAccessibleWhenUnlocked mitigates the issue (but eliminates the point). This other question is the only additional info I've found relating to iOS 8 causing the KeychainItemWrapper to crash. Building to a device with iOS 7 or a simulator on iOS 7/8 eliminates the issue; it's only flaring up on a real device using iOS 8.

Update

Here's a broad overview of the query dictionary:

{ accc = "<SecAccessControlRef: 0x1687cc70>"; acct = ...; agrp = ...; cdat = "2014-10-13 22:22:47 +0000"; desc = ""; gena = ...; labl = ""; mdat = "2014-10-13 22:34:16 +0000"; pdmn = cku; <-- kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly svce = ...; sync = 0; tomb = 0; "v_Data" = <>; }

and the attributesToUpdate parameter:

{ accc = "<SecAccessControlRef: 0x1687cc70>"; acct = ...; agrp = ...; cdat = "2014-10-13 22:22:47 +0000"; desc = ""; gena = ...; labl = ""; mdat = "2014-10-13 22:34:16 +0000"; pdmn = ak; <-- kSecAttrAccessibleWhenUnlocked svce = ...; sync = 0; tomb = 0; "v_Data" = <>; }

I've confirmed that changing other fields (i.e. kSecAttrService, kSecAttrAccount) have the same effect on the corresponding fields in the dictionaries, but with an expected status of 0.

回答1:

I also had the exact problem. Gave me:

OSStatus error -50 - conflicting kSecAccess and kSecAccessControl attributes

Crashed to all my users in the app store just after they updated.

Did the same as Peter. Grabed the data, deleted the item and inserted it as new item instead of trying to update the existing one.

I guess this is an Apple bug.

I opened a TSI but they didn't contact me yet.

From what i understand, It happens to users updated from iOS7 to iOS 8, where their first app was compiled with XCode for iOS7 (Before iOS 8 was out), and then on iOS8 updated to the new app which was compiled with XCode to iOS8.



回答2:

I had the same problem. I ended up testing kSecAttrAccessibile and if it wasn't what I wanted I recorded the value and attributes in the keychain in local variables, reset the keychain, set kSecAttrAccessible as desired and then set the value and attributes in the keychain to their original settings.



回答3:

A shot in the dark here:

Maybe the iOS device has iCloud synching enabled and adding an item that is not device specific and then making it ThisDeviceOnly results in the error. iOS8 may also have changed the behavior.

Can you try changing the order of attributes that are set to keychain

KeychainItemWrapper *wrapper = [[KeyChainItemWrapper alloc] initWithIdentifier:kMyIdentifier accessGroup:nil];
[wrapper setObject:(__bridge NSString*)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly forKey:(__bridge NSString*)kSecAttrAccessible];
[wrapper setObject:kMyServiceName forKey:(__bridge NSString*)kSecAttrService];
[wrapper setObject:kMyAccountToken forKey:(__bridge NSString*)kSecAttrAccount];

If this doesn't help, you should change KeychainItemWrapper to look like this

- (void)resetKeychainItem
{
    if (!keychainItemData)
    {
        keychainItemData = [[NSMutableDictionary alloc] init];
        [keychainItemData setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
    }