将 .p12 证书存储在钥匙串中以供以后使用
Posted
技术标签:
【中文标题】将 .p12 证书存储在钥匙串中以供以后使用【英文标题】:Storing a .p12 certificate in keychain to use later 【发布时间】:2015-06-02 14:20:03 【问题描述】:我正在尝试按照苹果文档处理客户端 p12 证书:
https://developer.apple.com/library/ios/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html#//apple_ref/doc/uid/TP40001358-CH208-SW13
我已成功从文件系统加载了一个 .p12 证书:
- (SecIdentityRef)getClientCertificate:(NSString *) certificatePath
SecIdentityRef identity = nil;
NSData *PKCS12Data = [NSData dataWithContentsOfFile:certificatePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef password = CFSTR("password");
const void *keys[] = kSecImportExportPassphrase ;
const void *values[] = password ;
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
CFRelease(options);
CFRelease(password);
if (securityError == errSecSuccess)
NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
identity = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
else
NSLog(@"Error opening Certificate.");
return identity;
然后我获得该身份的证书:
- (CFArrayRef)getCertificate:(SecIdentityRef) identity
SecCertificateRef certificate = nil;
SecIdentityCopyCertificate(identity, &certificate);
SecCertificateRef certs[1] = certificate ;
CFArrayRef array = CFArrayCreate(NULL, (const void **) certs, 1, NULL);
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(array, myPolicy, &myTrust);
if (status == noErr)
NSLog(@"No Err creating certificate");
else
NSLog(@"Possible Err Creating certificate");
return array;
但我真正想做的是将证书(或身份)存储在我的应用程序钥匙串中,这样我就不会从文件系统中读取它。
几个问题:
-
我应该存储哪个?证书还是身份?
如何存储和检索它?
上面的链接谈到“获取和使用持久钥匙串引用”,这让我很困惑。
它还谈到了“在钥匙串中查找证书”,但它提到使用证书的名称来查找它。我不确定“名称”的来源。
【问题讨论】:
你能帮我解决上述情况吗,我也在尝试做同样的事情。您是如何在项目中实现这一点的? 【参考方案1】:我想不出将证书存储在钥匙串中的充分理由,尽管我确信可能有一些。我只将身份(即私钥部分)存储在钥匙串中。为了更容易在钥匙串中找到身份,您生成对其的持久引用(参见链接中的清单 2-3),然后将该持久引用保存在应用程序的文件系统中。持久引用只是一个 CFDataRef,您可以免费桥接到 NSData 对象,然后轻松保存/加载。当您想要加密/其他的私钥时,您可以使用该持久引用从钥匙串中加载身份(请参阅链接中的清单 2-4)。我会为你发布一些代码,但我现在正在重建我的开发机器,还没有安装 Xcode。
【讨论】:
谢谢!我一直在尝试保存/检索对/从 NSUserDefaults 的持久引用,但没有成功。想知道我是否可以将其写入 NSUserDefaults,如果可以,是否正确。 我建议不要将该引用存储在 NSUserDefaults 中,它只是不适合它的地方,而且可能很挑剔。只需使用-writeToFile:atomically: 方法【参考方案2】:我应该存储哪个?证书还是身份?
这取决于您在做什么,以及您是否需要设备上的私钥进行身份验证。 SecIdentityRef
包含证书和私钥。如果您使用 .p12 文件进行身份验证,那么您可能希望存储和使用完整的身份。如果您只需要证书,那么我首先不会将完整的 .p12 加载到驱动器上,因为它包含私钥。
如何存储和检索它?
我建议将您的身份(或证书)存储在钥匙串中,并使用kSecAttrLabel
作为查询的唯一参考。
您需要查看的文档是 Storing an Identity in the Keychain,它将您定向到 Storing a Certificate in the Keychain,并概述了存储身份和证书之间所需的一些细微差别。
按如下方式完成(改编自上面的链接):
保存到钥匙串
// Create a query (with unique label for reference later)
NSDictionary* addquery = @ (id)kSecValueRef: (__bridge id)identity,
(id)kSecClass: (id)kSecClassIdentity,
(id)kSecAttrLabel: @"My Identity",
;
// Add the identity to the keychain
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
if (status != errSecSuccess)
// Handle the error
从钥匙串加载
// Query the keychain for your identity
NSDictionary *getquery = @ (id)kSecClass: (id)kSecClassIdentity,
(id)kSecAttrLabel: @"My Identity",
(id)kSecReturnRef: @YES,
;
// Retrieve the identity from the keychain
SecIdentityRef identity = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)getquery,
(CFTypeRef *)&identity);
if (status != errSecSuccess) <# Handle error #>
else <# Use identity #>
if (identity) CFRelease(identity); // After you are done with it
正如 RyanR 所提到的,您还可以在保存后创建对钥匙串项的持久引用,然后将其保存到文件中。我建议将[kSecReturnPersistentRef][3]
添加到您的addquery
以实现此目的。
【讨论】:
以上是关于将 .p12 证书存储在钥匙串中以供以后使用的主要内容,如果未能解决你的问题,请参考以下文章