为什么SecPKCS12Import会自动将SecIdentities添加到Keychain?

时间:2015-10-16 23:45:31

标签: ios macos cocoa ssl keychain

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,您将看到证书和私钥已添加到钥匙串中。

此致 大卫。

3 个答案:

答案 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);
    }
}