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

Posted

技术标签:

【中文标题】如何将已添加的 SecIdentityRef 更新到 iOS 应用的钥匙串?【英文标题】:How to update an already added SecIdentityRef to iOS app's keychain? 【发布时间】:2012-10-22 13:19:12 【问题描述】:

我的应用程序文件夹中有一个 PKCS12 文件,其中包含一个证书和一个私钥。 由于 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 中的身份。但我真的不明白这是什么......它是像内存引用这样的简单引用吗?如果是这种情况,当设备重新启动时会发生什么?

无法找到有关此的更多信息,我尝试使用 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 上开发

【问题讨论】:

【参考方案1】:

即使这是一篇旧帖子,因为我最近在使用钥匙串中的身份(用于 SSL 客户端身份验证)时遇到了类似的问题,我提供了自己的经验(使用 XCode 5.0.1)作为对此的支持似乎仍然很混乱。

SecItemAdd 确实必须与很少的键一起使用。 kSecValueRef(带有身份)当然是强制性的,并且是可选的 kSecAttrLabel。使用其他几个密钥(例如 kSecClass)会导致静默失败,即未报告错误但未添加身份。这非常令人困惑。

我也没有成功以这样的身份使用 SecItemUpdate。我所有的尝试都导致了 -50 错误。

为了能够更新现有身份,我使用 SecItemDelete 删除了现有身份,然后使用 SecItemAdd 添加了它。再次令人困惑的是,虽然对 SecItemDelete 的调用返回了 -25300 错误 (errSecItemNotFound),但它可以被忽略,因为身份确实被删除了,因为之后可以添加它(使用 SecItemAdd)而不会出错。

【讨论】:

【参考方案2】:

SecIdentityRefs、SecKeyRefs 和类似的Sec...Ref 值是钥匙串项的短暂表示。当应用程序退出或它们的保留计数达到零时,它们变得无效。它们不能直接保存在持久存储中。

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

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

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

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

【讨论】:

刚刚编辑了我的问题。我尝试在添加项目之前删除,但即使我的 searchDictionary 中只有一个元素,我仍然有相同的状态代码。对我来说没有多大意义。

以上是关于如何将已添加的 SecIdentityRef 更新到 iOS 应用的钥匙串?的主要内容,如果未能解决你的问题,请参考以下文章

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

如何将已创建的标签添加到联邦快递取件

如何将已输入的表单数据添加到表格行?

如何将已生成的 SSH 密钥添加到 git bash?

如何将已存储在 s3 上的文件添加到由同一数据存储支持的carrierwave?

NSDateComponents:如何将已弃用的 NSWeekCalendarUnit 替换为计算周开始日期?