30

对 keychain 使用 Apple 包装器,并尝试在其上保存一个项目(在模拟器中运行,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"}
}
4

7 回答 7

62

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

[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];   
于 2011-09-23T14:24:52.253 回答
9

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

于 2011-04-08T13:21:09.880 回答
6

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

  1. 下载并复制到您的项目 SFHFKeychainUtils.h 和 .m
  2. 将 Security.framework 添加到您的框架文件夹
  3. 确保将这些文件添加到您的目标
  4. 将 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];
于 2011-04-11T17:36:59.230 回答
3

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

于 2011-04-07T21:59:20.380 回答
1

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

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

虽然我不确定,但我怀疑问题与钥匙串同步有关。我遇到了类似的问题NSUserDefaults,当我写信给 NSUD 时,在代码的其他地方,获取standardUserDefaults并从中读取,更新还没有发生(因为我还没有完成[ud synchronize]。)

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

+ (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)];
}

(等等,其他地方的类似电话。)

于 2015-05-21T14:16:51.867 回答
0

我也遇到了这个问题,并通过接受者的回答和使用你的面包的附加链接解决了这个问题。

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

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

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

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

于 2013-07-10T16:59:46.093 回答
0

我尝试了上面的所有解决方案,但对我没有任何帮助。它仅适用于实际设备,但不适用于模拟器。

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

共享钥匙串权利

于 2016-10-10T20:36:33.260 回答