使用 iphone sdk 在钥匙串中保存时出错

Posted

技术标签:

【中文标题】使用 iphone sdk 在钥匙串中保存时出错【英文标题】:Error saving in the keychain with iphone sdk 【发布时间】:2011-05-17 14:01:09 【问题描述】:

我使用Apple wraper for the keychain,并尝试在上面保存一个项目(在模拟器中运行,ios 4.1)。

我以前没有使用过钥匙串。

我收到此错误:

无法添加钥匙串项。错误 - 25299

在 KeychainItemWrapper.m 第 304 行:

// No previous item found; add the new one.
result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, @"Couldn't add the Keychain Item." );

我就是这样保存的:

- (void) saveKey:(NSString *)key value:(NSString *)value 
    KeychainItemWrapper *keyItem = [[KeychainItemWrapper alloc] initWithIdentifier:key accessGroup:nil];
    [keyItem setObject:value forKey:(id)kSecValueData];
    [keyItem release];

这是 api 尝试保存的值:

<CFBasicHash 0x7231f60 [0x320d380]>type = mutable dict, count = 5,
entries =>
2 : <CFString 0x2e6eb98 [0x320d380]>contents = "labl" = <CFString 0x2fb018 [0x320d380]>contents = ""
3 : <CFString 0x2e6efb8 [0x320d380]>contents = "v_Data" = <CFString 0x727de60 [0x320d380]>contents = "dit8"
4 : <CFString 0x2e6ebc8 [0x320d380]>contents = "acct" = <CFString 0x2fb018 [0x320d380]>contents = ""
5 : <CFString 0x2e6eb58 [0x320d380]>contents = "desc" = <CFString 0x2fb018 [0x320d380]>contents = ""
6 : <CFString 0x2e6ebe8 [0x320d380]>contents = "gena" = <CFString 0x2ffd08 [0x320d380]>contents = "userCode"

【问题讨论】:

我一直坚持这一点,所以我认为赏金是个好主意。相同的errorCode但iOS4.3但我猜这是PEBKAC问题,sdk没有问题。 我在- (void)resetKeychainItem 中添加[keychainItemData setObject:@"" forKey:(__bridge id)kSecAttrService]; 时出现了这个问题。然后,当我在下面阅读 user379075 的答案时,它响起一个铃声,如果你“重置”它,你还必须设置它和 v.v. 【参考方案1】:

我尝试了上面所有的解决方案,但对我没有任何帮助。它只能在实际设备上运行,而不能在模拟器上运行。

我在模拟器上运行它的解决方案是打开“共享钥匙串权利”。

Share Keychain entitlement

【讨论】:

【参考方案2】:

对我来说,解决方案是我创建了一个KeychainItemWrapper“singleton”并在整个应用程序中使用它。 (实际上,在我的例子中,我有一个充满KeychainItemWrapper-s 的单例字典,因为我使用的不止一个。)

这解决了我进入代码路径的问题,该路径有效地表示“钥匙串上是否存在此项目?不?然后添加它。哎呀!NSAssert() 我正在尝试添加一个已经存在的项目(错误 -25299)”

虽然我不确定,但我怀疑问题与钥匙串同步有关。我在NSUserDefaults 上遇到过类似的问题,当我写信给 NSUD 时,在代码的其他地方,获取standardUserDefaults 并从中读取,并且尚未进行更新(因为我还没有完成@987654326 @,但是。)

在代码中,我的例程如下所示:

+ (KeychainItemWrapper*) keyChainWrapperForKeyID: (NSString*) keyID

    static dispatch_once_t onceToken = 0;
    static NSMutableDictionary *rfcuKeyChains = nil;
    dispatch_once(&onceToken, ^
        rfcuKeyChains = [NSMutableDictionary new];
    );

    KeychainItemWrapper *keychain = nil;
    @synchronized (rfcuKeyChains)
    
        keychain = [rfcuKeyChains objectForKey: keyID];
        if (keychain == nil)
        
            keychain = [[KeychainItemWrapper alloc] initWithIdentifier: keyID accessGroup: nil];
            [rfcuKeyChains setObject: keychain forKey: keyID];
        
    

    return keychain;

我是这样使用它的:

KeychainItemWrapper *keychain = [RFCUtils keyChainWrapperForKeyID: keyID];
NSString *firstLaunch = [keychain objectForKey: (__bridge id)(kSecAttrAccount)];
if (firstLaunch == nil)

    [keychain setObject: MY_APP_KEY forKey: (__bridge id)(kSecAttrAccount)];

(等等,其他地方类似的调用。)

【讨论】:

【参考方案3】:

我也遇到了这个问题,感谢接受者的回答和 useyourloaf 的附加链接。

我遇到的问题很有趣,我只需要保存一个值并决定将其存储在 kSecValueData 字段中。那是因为我看到了其他关于使用钥匙串的帖子,并在转向 KeychainItemWrapper 之前开始了我自己的实现。 这导致了以下问题:在我测试的第一台设备(第一代 iPad)上,我在 writeToKeychain 中遇到错误。我更换了设备(也是 ipad 1st gen),它工作了!回到第一个设备它仍然无法工作。

所以我知道我之前在设备的钥匙串中做错了什么并且无法轻松恢复它。我得到的错误代码是:writeToKeychain 的 SecItemCopyMatching 上的 -25300(未找到项目)和 SecItemAdd 上的 -25299 之后。 (项目重复)

有了这个问题,这一切都是有道理的:设备有一个与 any 新密钥匹配的密钥,但 KeychainItemWrapper 无法删除它,但无法检索密钥。 一旦我将相同的值添加到字段 kSecAttrAccount,它就开始工作了。

长话短说,对于遇到此问题的其他用户,您的问题可能看起来不同,但请注意细节。如果您有 -25300(未找到项目),然后是 -25299(项目重复);确保您正在设置一个定义钥匙串项目唯一性的字段。如果它在一台设备上不起作用,请尝试另一台设备,如果可以的话,您可能能够将问题隔离到一台设备上。 苹果钥匙串错误代码:http://developer.apple.com/library/ios/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/uid/TP30000898-CH5g-CJBEABHG(搜索结果代码)

【讨论】:

如何找到我之前保存的项目?我已经以这种方式枚举了所有项目***.com/questions/10966969/…,但没有。它藏在哪里?【参考方案4】:

钥匙链是一件非常痛苦的事情。您应该使用 Buzz Andersen's STUtils 库作为包装器。这将使您的生活变得更加轻松。我从来没有遇到过问题。

【讨论】:

这个github项目现在已经被弃用了;作者建议github.com/ldandersen/STUtils/blob/master/Security 作为替代品。【参考方案5】:

我知道这是几个月前的事了,但我也遇到了同样的问题,而且很痛苦,所以我想分享一下。我通过添加这一行来解决它:

[self.keychainItemWrapper setObject:@"MY_APP_CREDENTIALS" forKey:(id)kSecAttrService];
//@"MY_APP_CREDENTIALS" can be any string.

我发现这篇博文很有帮助: “在数据库术语中,您可以认为它们是 kSecAttrAccount、kSecAttrService 这两个属性的唯一索引,要求这两个属性的组合对于钥匙串中的每个条目都是唯一的。” (来自http://useyourloaf.com/blog/2010/4/28/keychain-duplicate-item-when-adding-password.html)。

此外,在使用此代码的 Apple 示例项目中,他们在应用委托中实例化 KeychainItemWrapper。我不知道是否有必要,但我喜欢尽可能地效仿他们的例子:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//there will be some standard code here.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MY_APP_CREDENTIALS" accessGroup:nil];
self.keychainWrapper = wrapper;
[self.keychainWrapper setObject:@"MYOBJECT" forKey:(id)kSecAttrService];
[wrapper release];

我认为这是包装代码中的错误。逻辑基本上是说“这个条目是否已经存在?不,它不存在。好的,我会添加它。糟糕,你不能添加它,因为它已经存在了。”

您可能还需要设置 kSecAttrAccount;我从未尝试过不设置此值,因为它旨在保存与密码一起使用的用户名:

[self.wrapper setObject:txtUserName.text forKey:(id)kSecAttrAccount];   

【讨论】:

添加 MY_APP_CREDENTIALS 语句后工作正常。非常感谢:) 我认为,重要的是要注意,在查找钥匙串项目时,您应该只提供要搜索的帐户和服务属性。如果您提供更多属性,系统将搜索所有属性的匹配项,因此即使该帐户和服务已经存在项目,您也可能不会得到任何返回,因此尝试将该帐户和服务添加并输入到钥匙串会失败。【参考方案6】:

您可以使用 Buzz Andersen 的 SFHFKeychainUtils 使用钥匙串轻松存储和取回值。

    下载并复制到您的项目中 SFHFKeychainUtils.h 和 .m 将 Security.framework 添加到您的 Framework 文件夹 确保将这些文件添加到您的目标中 将 SFHFKeychainUtils.h 导入你想使用的地方

这是一个关于如何使用这个库的小例子。

// To store data
NSError *error = nil;
[SFHFKeychainUtils storeUsername:username andPassword:password forServiceName:kStoredData updateExisting:YES error:&error];

// To retrieve data
NSString *password = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:kStoredData error:&error];

// To delete data from keychain
[SFHFKeychainUtils deleteItemForUsername:username andServiceName:kStoredData error:&error];

【讨论】:

在这种情况下什么是 kStoredData?尝试执行此操作时出现构建错误。 在这种情况下,kStoredData 是一个已定义的键...您可以使用任何键...在此示例中,kStoreData 是一个服务名称的键,例如 @"com.company.app.serviceName" .您可以在 .h 文件中定义这样的密钥,然后使用它来存储和检索您的数据【参考方案7】:

根据the documentation,您得到的错误-25299 是“errSecDuplicateItem”,这意味着您尝试添加的项目已经存在。查看 KeychainItemWrapper 的链接代码,我猜想 SecItemCopyMatching 调用失败并出现除 errSecItemNotFound (–25300) 以外的错误。

【讨论】:

以上是关于使用 iphone sdk 在钥匙串中保存时出错的主要内容,如果未能解决你的问题,请参考以下文章

如何查看 iPhone 模拟器的钥匙串

如何在 iPhone 钥匙串中建立 SecIdentityRef? (没有 .p12)

如何检查用户名和密码是不是已保存在钥匙串中

钥匙串类不适用于 iPhone 4s/5 和 iPad

将 .p12 证书存储在钥匙串中以供以后使用

Keychainitemwrapper 类中的 SecItemCopyMatching 条件失败,因此无法检索钥匙串中保存的值