如何将已添加的SecIdentityRef更新为iOS应用程序的钥匙串?

时间:2012-10-22 13:19:12

标签: ios keychain

我的app文档文件夹中有一个PKCS12文件,其中包含一个证书和一个私钥。 我可以打开这个.p12文件,提取身份对象并显示一些信息,这要归功于Apple的文档(https://developer.apple.com/library/ios/#documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks。 HTML#// apple_ref / DOC / UID / TP40001358-CH208-DontLinkElementID_10)

我现在要做的是将此标识存储到钥匙串中,以便稍后使用。我在iOS Keychain上阅读了很多不同的东西,我很难弄清楚它是如何工作的。

Apple的代码似乎使用persistent_ref来检索存储在Keychain中的Identity。但我真的不明白这是什么......它是一个简单的引用,如内存引用?如果是这种情况,那么当设备重新启动时会发生什么?

无法找到更多有关此内容的信息,我尝试使用kSecAttr属性进行不同的操作。 当前代码可以很好地将标识添加到钥匙串:

NSMutableDictionary * dictionary = [[[NSMutableDictionary alloc] init] autorelease];  

[dictionary setObject:@"LABEL" forKey:kSecAttrLabel];

[dictionary setObject:(id)newIdentity forKey:(id)kSecValueRef];

OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);

但是如果我第二次尝试添加它,我会收到-25299错误,因为它已经存在,所以“很好”。我尝试通过这样的更新来处理它:

NSMutableDictionary *searchDictionary = [[[NSMutableDictionary alloc] init] autorelease];
[searchDictionary setObject:@"LABEL" forKey:kSecAttrLabel];
[searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnRef];

NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
[updateDictionary setObject:(id)newIdentity forKey:(id)kSecValueRef];

OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,(CFDictionaryRef)updateDictionary);

使用此代码,我得到了-50状态错误,显然是因为我的参数无效...哪一个?为什么?我该怎么做才能正确更新我的钥匙串?

编辑:正如我所建议的那样,我尝试在添加之前删除现有元素,但我仍然使用相同的状态代码(-50)。以下是我尝试的代码:

NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
OSStatus status = SecItemDelete((CFDictionaryRef)searchDictionary);
NSAssert(status == noErr, @"Problem deleting current keychain item." );

setupSearchDirectoryForIdentifier只需创建一个带有我的项目标签的NSDictionnary:

- (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier {

    // Setup dictionary to access keychain.
    NSMutableDictionary *searchDictionary = [[[NSMutableDictionary alloc] init] autorelease];  

    [searchDictionary setObject:identifier forKey:kSecAttrLabel];

    return searchDictionary; 
}

谢谢

PS:我正在开发Xcode 4.2 / iPad 5.1.1

2 个答案:

答案 0 :(得分:4)

即使这是一篇旧文章,因为我最近在处理钥匙串中的身份(用于SSL客户端身份验证)时遇到了类似问题,我提供了自己的经验(使用XCode 5.0.1)对此的支持似乎仍然非常令人困惑。

SecItemAdd 确实必须使用很少的密钥。 kSecValueRef(带有标识)当然是强制性的,可选择kSecAttrLabel。使用其他几个密钥(例如kSecClass)导致无声失败,即没有报告错误,但未添加标识。这非常令人困惑。

我没有成功使用具有此类身份的 SecItemUpdate 。我的所有尝试都导致-50错误。

为了能够更新现有身份,我使用 SecItemDelete 删除了现有身份,然后将其添加到SecItemAdd。什么又令人困惑的是,尽管调用SecItemDelete返回-25300错误背面(errSecItemNotFound),它可以作为身份确实删除,因为有可能在事后(使用SecItemAdd)没有错误添加它忽略。

答案 1 :(得分:3)

SecIdentityRef s,SecKeyRef和类似的Sec...Ref值是钥匙串项目的短暂表示。当应用程序退出或保留计数达到零时,它们将变为无效。它们无法直接保存在持久存储中。

另一方面,持久性引用是CFDataRef的一部分,您可以使用它来稍后检索特定的钥匙串项。您可以将其存储在NSUserDefaults或您想要的任何其他位置的文件中。当应用程序退出或重启设备时,它不会变为无效。 (但是,当删除钥匙串本身时(即,当设备被恢复时),当它引用的项目被删除或者其中一个项目的标识属性被修改时,持久性引用可能变得无效。)

Apple的示例代码使用SecItemAdd的第二个参数来检索对它添加到钥匙串的项目的持久引用。然后,这可能存储在NSUserDefaults中。鉴于持续引用,该应用可以稍后使用SecItemCopyMatching将其转换为可以使用的SecIdentityRef

请注意,如果您不想,则不必使用持久性引用;如果您愿意,您还可以选择根据标签或任何其他标识属性检索钥匙串项目。

SecItemUpdate可能会失败,因为SecIdentityRef不是真正的钥匙串项目。它只是一个伪项,它是在公钥及其相关的私钥都在钥匙串上时构造的。因此,更新它没有多大意义 - 它没有任何属性。其所有属性都从其关联的SecCertificateRef(证书)和SecKeyRef(私钥)继承。更新这些项而不是标识应该有效。 (但是更简单地创建项目更容易:您可以通过将任何属性添加到SecItemAdd的第一个参数来初始化任何属性。)或者,您可以尝试简单地删除标识(SecItemDelete )在重新添加到钥匙串之前。