将私钥添加到iOS密钥链中

20

我正在尝试将一个私钥添加到iOS的钥匙串中。证书(公钥)可以正常工作,但是私钥始终无法添加......我完全不明白为什么以下代码不起作用。

首先,我检查当前的键(如果钥匙串是键/值存储,则为键)是否在钥匙串中“空闲”。然后我将添加私钥。

CFStringRef labelstring = CFStringCreateWithCString(NULL, [key cStringUsingEncoding:NSUTF8StringEncoding], kCFStringEncodingUTF8);

NSArray* keys = [NSArray arrayWithObjects:(__bridge id)kSecClass,kSecAttrLabel,kSecReturnData,kSecAttrAccessible,nil];
NSArray* values = [NSArray arrayWithObjects:(__bridge id)kSecClassKey,labelstring,kCFBooleanTrue,kSecAttrAccessibleWhenUnlocked,nil];
NSMutableDictionary* searchdict = [NSMutableDictionary dictionaryWithObjects:values forKeys:keys];

CFRelease(labelstring);

NSMutableDictionary *query = searchdict;


CFTypeRef item = NULL;
OSStatus error = SecItemCopyMatching((__bridge_retained CFDictionaryRef) query, &item);

if (error)
{
    NSLog(@"Error: %ld (statuscode)", error);
}

if(error != errSecItemNotFound)
{
    SecItemDelete((__bridge_retained CFDictionaryRef) query);
}

[query setObject:(id)data forKey:(__bridge id)kSecValueData];

OSStatus status = SecItemAdd((__bridge_retained CFDictionaryRef) query, &item);

if(status)
{
    NSLog(@"Keychain error occured: %ld (statuscode)", status);
    return NO;
}

以下是调试输出:

2012-07-26 15:33:03.772 App[15529:1b03] Error: -25300 (statuscode)
2012-07-26 15:33:11.195 App[15529:1b03] Keychain error occured: -25299 (statuscode)

第一个错误代码-25300代表errSecItemNotFound。这意味着没有为此键存储任何值。然后,当我尝试将私钥添加到Keychain时,我得到了-25299,这意味着errSecDuplicateItem。我不明白这是为什么。为什么会发生这种情况?

有人有线索或提示吗?

苹果的错误代码:

errSecSuccess                = 0,       /* No error. */
errSecUnimplemented          = -4,      /* Function or operation not implemented. */
errSecParam                  = -50,     /* One or more parameters passed to a function where not valid. */
errSecAllocate               = -108,    /* Failed to allocate memory. */
errSecNotAvailable           = -25291,  /* No keychain is available. You may need to restart your computer. */
errSecDuplicateItem          = -25299,  /* The specified item already exists in the keychain. */
errSecItemNotFound           = -25300,  /* The specified item could not be found in the keychain. */
errSecInteractionNotAllowed  = -25308,  /* User interaction is not allowed. */
errSecDecode                 = -26275,  /* Unable to decode the provided data. */
errSecAuthFailed             = -25293,  /* The user name or passphrase you entered is not correct. */ 

提前感谢!

更新 #1:我发现它只在第一次起作用。即使数据和密钥不同,在将其存储到钥匙串中的第一次之后,我无法存储更多的密钥。


我遇到了完全相同的问题。使用SecItemAdd添加第一个密钥没有问题,但是任何连续调用SecItemAdd都会失败,并显示errSecDuplicateItem,尽管SecItemCopyMatching返回errSecItemNotFound。你找到解决方法了吗? - 100grams
2个回答

8
以下代码对我有效:
```html

以下代码适用于我:

```
NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 
[query setObject:(id)kSecClassKey forKey:(id)kSecClass]; 
[query setObject:(id)kSecAttrAccessibleWhenUnlocked forKey:(id)kSecAttrAccessible]; 
[query setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];

//adding access key 
[query setObject:(id)key forKey:(id)kSecAttrApplicationTag];


//removing item if it exists 
SecItemDelete((CFDictionaryRef)query);

//setting data (private key) 
[query setObject:(id)data forKey:(id)kSecValueData];

CFTypeRef persistKey; OSStatus status = SecItemAdd((CFDictionaryRef)query, &persistKey);

if(status) {
    NSLog(@"Keychain error occured: %ld (statuscode)", status);
    return NO; 
}

我去年在WWDC上和一位负责Keychain的苹果员工交谈,他告诉我实际上他们目前没有提供另一种实现方式,但是他们有一个私有API即将发布... - Chris
1
我不明白你的意思,Chris。我曾经也遇到过同样的问题,并成功修复了我的代码以正确找到现有的项目。我的问题在于添加时将其定义为可以通过iCloud同步,但在搜索时没有将其包含在查询中,因此无法找到匹配项。我不需要删除并重新添加它。 - Jordan H
1
这个能用来将字符串转换为SecKeyRef吗? - Wojtek Turowicz
1
@Joey,这是一种不好的做法,特别是在OSX上,因为用户可能已经将密钥移动到不同的密钥链中(如果您喜欢,可以有数百个密钥链),当您删除并重新创建它时,它总是会在默认密钥链中重新创建,然后用户必须再次移动它。这是一种不好的做法,在iOS和OS X上,因为系统/用户设置的任何访问控制或其他应用程序添加的任何额外数据(如果该项是共享的,则可能)都会丢失。 - Mecki
@Chris 抱歉,我不知道你和谁交流过,但只要有 SecItemAdd,就一定有 SecItemUpdate,而且它的作用也很好。在创建新项目之前,首先查看它是否已存在,如果存在,则更新现有项目。仅当没有要更新的项目时才创建新项目。 - Mecki
显示剩余3条评论

1

抱歉,我无法调试您的代码。苹果提供了一些示例代码(KeychainItemWrapper),可以让您保存一个字符串(我记得)。这对于处理密钥链非常有帮助。网上有一个gist是该类的修改版本,但它保存和恢复一个字典(作为数据对象进行存档,这就是苹果代码对字符串所做的操作)。这使您可以在一个接口中保存多个项目到密钥链中。这里是gist链接Keychain for NSDictionary/data


1
谢谢,但它需要存储为kSecClassKey(相应的证书为kSecClassCertificate)。我知道苹果提供了这个样例代码,用于将用户凭据(但仅限字符串)存储到钥匙串中。考虑到有人想要验证证书或使用kSecClassKey的附加保护,不能使用苹果样例代码或您提供的链接的方法来存储。然而,我认为我已经找到了一个解决方案,但在发布到此处之前必须进行验证。 - Chris
1
根据我的经验,钥匙串包装器不允许将多个项目保存到同一钥匙串组中。这引起了一些重大的挫败感,但可以在此处找到解决方案:https://dev59.com/22XWa4cB1Zd3GeqPIwfd?lq=1 - rob
很有趣 - 因为我在我的应用程序中使用字典并保存与用户相关的电子邮件、密码和另一些上下文信息。但是我稍微修改了苹果的代码,虽然不是很多 - 你可以在我的回答链接中看到它。这是现在数千部手机上运行的工作代码(不是数百万 :-( )。 - David H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接