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

时间:2010-11-29 23:22:04

标签: iphone ios4 keychain

我使用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"}
}

7 个答案:

答案 0 :(得分:60)

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

[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示例项目中,他们在app delegate中实例化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];   

答案 1 :(得分:9)

根据the documentation,你得到的错误-25299是“errSecDuplicateItem”,意味着你要添加的项目已经存在。查看KeychainItemWrapper的链接代码,我猜测SecItemCopyMatching调用失败,错误除了errSecItemNotFound(-25300)。

答案 2 :(得分:6)

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

  1. 下载并复制您的项目SFHFKeychainUtils.h和.m
  2. 将Security.framework添加到Framework文件夹
  3. 确保将这些文件添加到目标
  4. 导入SFHFKeychainUtils.h您要使用它的地方
  5. 这是关于如何使用此库的一个小例子。

    // 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];
    

答案 3 :(得分:3)

钥匙扣是一种彻底的痛苦。您应该使用Buzz Andersen's STUtils库作为包装器。它会让你的生活变得更加轻松。我从来没有遇到过这个问题。

答案 4 :(得分: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)];
}

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

答案 5 :(得分:0)

我也遇到了这个问题,并且由于接受者的答案以及使用你们的额外链接而解决了这个问题。

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

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

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

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

答案 6 :(得分:0)

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

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

Share Keychain entitlement