iOS KeyChain无法从后台获取值


85

我目前在iOS KeyChain中存储用户名(电子邮件)以及电子邮件和密码的盐化哈希。我使用的是这里找到的ARC'ified版本。

KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
[wrapper setObject:APP_NAME forKey:(__bridge id)kSecAttrService];
[wrapper setObject:email forKey:(__bridge id)kSecAttrAccount];
[wrapper setObject:token forKey:(__bridge id)kSecValueData];

当我需要在应用程序处于活动状态时为网络调用提取令牌时,一切都很好。它适用于从干净启动启动的登录以及整个网络中的所有网络调用。当应用程序在后台运行时,麻烦就开始了。

请记住,这只是偶尔发生,我还没有将其固定到特定的iOS版本或设备。

用户跳到一个位置(区域监视),我想用其状态更新服务器。我尝试将令牌从钥匙串中拔出,就像我对其他每个网络呼叫所做的一样,并更新状态。但是对于某些用户,该值为nil。没有它,我将无法更新网络内容。为什么这对大多数人有用,但对少数人却无效?

KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
NSString *token = [wrapper objectForKey:(__bridge id)kSecValueData];

我回到了非ARC版本的keychainwrapper,但是我仍然得到相同的结果。我对此表示感谢。这只是我用户的一小部分,但这是我要解决的问题,不用担心。提前致谢。

另外,我所有的后台工作都在backgroundTask中进行设置,以防止事情超时。我在钥匙串周围的工作上没有任何问题,但是在我的令牌填满之前,我不会让事情继续进行。

编辑 我发现我的问题是他们的钥匙串没有从后台获取值。我将在下面发布答案并接受它,因为我觉得这个问题以后可能对其他人有价值。

Answers:


110

我的问题已经接近问题的原由,但并非如此。在浏览了一个又一个博客,一个又一个教程之后,我终于找到了一个暗示可能发生的情况的。

锁定主屏幕。钥匙串教程始终将钥匙串的可访问性设置保留为空白,因此它将默认设置为Apple的最低/最安全访问级别。但是,如果用户在锁定屏幕上输入密码,则该级别不允许钥匙串访问。答对了!这就解释了零星的行为以及为什么这种情况只发生在一小部分用户中。

一行代码解决了整个混乱。

[wrapper setObject:(__bridge id)kSecAttrAccessibleAlways forKey:(__bridge id)kSecAttrAccessible];

在我要设置用户名和密码值的位置添加此行。奇迹般有效。希望这会帮助某个人。这让我很困惑,直到我能够将它们拼凑在一起。


1
谢谢!这非常有帮助。
Rich Waters 2012年

3
实际上,我们已经处理了好几个星期。你是救命稻草!
OC Rickard

15
请尽量避免…AccessibleAlways,或存储仅提供有限特权的令牌(例如,允许您读取新供稿项目但不发布的令牌)。您这样做显然要放弃加密级别。如果您的应用程序可以等到首次解锁,那么最好使用…AfterFirstUnlock并指导用户首先解锁其设备。
millenomi

14
这是一个非常糟糕的主意,因为这意味着该凭据数据不再受保护。尽管需要做更多的工作,但是创建派生凭证非常重要,该凭证不能仅用于您期望在后台需要的有限访问权限,而不能用于其他用途。有限的凭证可以在一段时间后过期,并且每次打开应用程序都会创建一个新凭证,从而使旧凭证无效。万一衍生凭证遭到破坏,这可以确保用户安全。请参阅WWDC 2013会议204,以了解有关此信息。
Joey Hagedorn

7
在此处回显@JoeyHagedorn-在44:24标记处听WWDC 2013会议204“多任务处理的新功能”,在25:30标记处听WWDC 2013会议709“用钥匙扣保护秘密”。您可以在asciiwwdc.com上
Shazron 2013年

63

使用kSecAttrAccessibleAfterFirstUnlock代替kSecAttrAccessibleAlways


Apple的文档中

kSecAttrAccessibleAfterFirstUnlock
重新启动后才能访问钥匙串项中的数据,直到用户将设备解锁一次。

第一次解锁后,数据将保持可访问状态,直到下一次重新启动。建议将其用于需要后台应用程序访问的项目。使用加密备份时,具有此属性的项目将迁移到新设备。


4
这个答案应该是评论……
Frizlab 2014年

这个答案似乎很完美,因为kSecAttrAccessibleAlways它已被弃用
Sazzad Hissain Khan

1

就我而言,watchOS2在iOS端访问钥匙串数据。

首先,使用kSecAttrAccessibleWhenUnlockedThisDeviceOnly。无论iPhone是否锁定,我都可以读取数据。当手表试图访问钥匙串时,我将收到错误消息,这使我感到非常困惑::SecTrustEvaluate [leaf IssuerCommonName SubjectCommonName]

某些情况下,它将变为::SecOSStatusWith错误:[-25308]错误域= NSOSStatusErrorDomain代码= -25308“ ks_crypt:e00002e2无法'oe'锁物品(类6,袋子:0)钥匙串被锁定时试图访问该物品。 ” UserInfo = {NSDescription = ks_crypt:e00002e2无法'oe'锁物品(类6,袋子:0)在钥匙串被锁定时试图访问该物品。}

如果有更多信息,我将更新我的答案。


0

这可能是由于苹果公司的数据保护政策在某种程度上对开发人员而言是模糊的。解决方法是启动应用程序时检查钥匙串是否可访问,如果无法访问,则可能会杀死您的应用程序(带有适当的弹出窗口),具体取决于您的应用程序类型。

+(BOOL) isKeychainAccessible
{
    NSString *keychainTestKey = @"keychainTestKey";
    NSString *keychainTestValue = @"keychainTestValue";
    [self createKeychainValue:keychainTestValue forIdentifier:keychainTestKey];
    NSString *loadedValue = [self keychainStringFromMatchingIdentifier:keychainTestKey];
    [self deleteItemFromKeychainWithIdentifier:keychainTestKey];
    return ([keychainTestValue isEqualToString: loadedValue]);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.