SecPKCS12Import上的documentation声明如下:
[...]然后,您可以使用Keychain Services API(请参阅Keychain Services 参考)将身份和相关证书放入 钥匙串。
这意味着“items”参数(该函数的第三个参数)中返回的项不应自动添加到钥匙串中。但是,我发现在使用该功能时,这些项目会自动添加到钥匙串中。如果我尝试使用SecItemAdd添加它们,我会得到errSecDuplicateItem。
这是一个错误还是应该这样?为什么会自动添加项目?
以下是一些示例代码:
NSDictionary *options = [[NSDictionary alloc] initWithObjectsAndKeys:@"password", (id)kSecImportExportPassphrase, nil];
CFArrayRef items_ = NULL;
OSStatus ret = SecPKCS12Import((CFDataRef)pkcs12data /* get this from somewhere … */, (CFDictionaryRef)options, &items_);
如果您使用该代码然后打开Keychain Access,您将看到证书和私钥已添加到钥匙串中。
此致 大卫。
答案 0 :(得分:1)
似乎Apple的文档可能已过时(SecPKCS12Import),因为此链接https://developer.apple.com/library/ios/qa/qa1745/_index.html提到"读取PKCS#12格式的blob然后导入使用SecPKCS12Import函数将blob的内容导入应用程序的钥匙串中......"
根据文档修订日期,QA1745比证书,密钥和信任服务参考更新。
答案 1 :(得分:0)
这是一个错误还是应该这样?
这不是错误,只是文档不正确。嗯,对于 iOS 是正确的,对于 macOS 是不正确的。
<块引用>为什么会自动添加项目?
这是由该功能在 macOS 中的实现方式造成的。 the implementation 中的评论揭示了原因:
// SecPKCS12Import is implemented on Mac OS X in terms of the existing
// SecKeychainItemImport API, which supports importing items into a
// specified keychain with initial access control settings for keys.
macOS 中的 SecPKCS12Import
只是 SecKeychainItemImport
的包装器,正如函数名称所暗示的那样,该函数导入到钥匙串中。这也解释了以下代码:
if (!importKeychain) {
// SecKeychainItemImport requires a keychain, so use default
status = SecKeychainCopyDefault(&importKeychain);
}
在 iOS 上,该函数是独立实现的,而不是作为包装器实现的,因为 SecKeychainItemImport
在 iOS 上甚至不可用。
但是如果这对您来说是个问题,那么有一种方法可以解决。许多人通过创建一个临时钥匙串来解决这个问题,该钥匙串永远不会对系统或用户可见(因此对应用程序或钥匙串访问也不可见),当然,这也可以工作,但有点一个丑陋的黑客。
更好:使用 SecItemImport
并作为导入格式使用 kSecFormatPKCS12
,然后您还可以获得解析的身份,但除非您要求,否则不会在任何地方导入任何内容。
答案 2 :(得分:-1)
SecPKCS12Import
不会向钥匙串添加商品。但是,它会在钥匙串中查看是否已经存在导入的物品。如果找到现有项目,则会为SecIdentityRef (SecCertificateRef and SecKeyRef)
返回这些项目。这就是您在致电errSecDuplicateItem
后致电SecItemAdd
时获得SecPKCS12Import
的原因。
调试时,您可能希望使用以下代码删除钥匙串中的所有内容:
void _EraseKeychain()
{
NSMutableArray *toDelete = [NSMutableArray array];
NSArray *classes = @[(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassGenericPassword];
NSMutableDictionary *query = [NSMutableDictionary dictionary];
query[(id)kSecClass] = (__bridge id)kSecClassIdentity;
query[(id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
query[(id)kSecReturnPersistentRef] = @YES;
id class;
for( class in classes )
{
query[(__bridge id)kSecClass] = class;
CFTypeRef items = nil;
OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)query, &items);
if( result == errSecSuccess )
{
[toDelete addObjectsFromArray:(__bridge NSArray*)items];
CFRelease(items);
}
}
id deleteRef;
for( deleteRef in toDelete )
{
NSString *objectKind = @"unknown";
if( CFGetTypeID(deleteRef) == CFDataGetTypeID() )
{
objectKind = [[NSString alloc] initWithUTF8String:(char *)[(__bridge NSData*)deleteRef bytes]];
}
NSDictionary *delRequest = @{(id)kSecValuePersistentRef:deleteRef};
OSStatus deleteResult = SecItemDelete((__bridge CFDictionaryRef)delRequest);
if( deleteResult == errSecSuccess )
NSLog(@"Deleted item(%@) with persistent ref %@", objectKind, deleteRef);
else if( deleteResult == errSecItemNotFound )
NSLog(@"Already deleted item(%@) with persistent ref %@", objectKind, deleteRef);
else
NSLog(@"Can't delete keychain item(%@) with persistent ref %@", objectKind, deleteRef);
}
}