c - 将证书插入钥匙串(keychain)

标签 c macos security ssl core-foundation

我有一个客户端从服务器检索证书 (.pfx),包括私钥,我使用以下代码将其添加到本地钥匙串(keychain):-

void AddCertToKeyChain(const QByteArray& cert, const QString& password)
{
    SecKeychainRef keyChain = nil;

    OSStatus err = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &keyChain);
    if (err != errSecSuccess)
    {
        emit Log("Failed to access system keychain: " + LogMessageForStatus(err));
        return;
    }

    SecExternalFormat format = kSecFormatPKCS12;
    SecExternalItemType itemType = kSecItemTypeAggregate;
    SecItemImportExportFlags flags = 0;

    SecItemImportExportKeyParameters params;
    memset(&params, 0, sizeof(params));

    params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
    params.flags = 0;
    params.passphrase = password.toCFString();

    params.alertTitle = NULL;
    params.alertPrompt = NULL;
    params.accessRef = NULL;

    // create and populate the key usage array
    CFMutableArrayRef keyUsage = CFArrayCreateMutable(
            kCFAllocatorDefault,
            0,
            &kCFTypeArrayCallBacks
        );

    CFArrayAppendValue(keyUsage, kSecAttrCanEncrypt);
    CFArrayAppendValue(keyUsage, kSecAttrCanDecrypt);
    CFArrayAppendValue(keyUsage, kSecAttrCanDerive);
    CFArrayAppendValue(keyUsage, kSecAttrCanSign);
    CFArrayAppendValue(keyUsage, kSecAttrCanVerify);
    CFArrayAppendValue(keyUsage, kSecAttrCanWrap);
    CFArrayAppendValue(keyUsage, kSecAttrCanUnwrap);

    keyUsage = NULL; // Error without this - Failed to import certificate: The key usage mask is not supported.

    // create and populate the key attributes array
    CFMutableArrayRef keyAttributes = CFArrayCreateMutable(
            kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks
        );

    // required for import
    params.keyUsage = keyUsage;
    params.keyAttributes = keyAttributes;

    OSStatus status = SecItemImport(cert.toCFData(), CFSTR(".p12"), &format, &itemType, flags, &params, keyChain, NULL);
    if(status == errSecSuccess)
        emit Log("Certificate successfully imported");
    else
    {
        emit Log("Failed to import certificate: " + LogMessageForStatus(status));
    }
}

如预期的那样,证书和私钥出现在钥匙串(keychain)中。

但是,无论是通过编程还是使用 Keychain 应用程序,尝试检索证书都是一个问题。

如果我选择从钥匙串(keychain)导出私钥,我会在对话框中看到以下错误:-

"An error has occurred. Unable to export an item. The contents of this item cannot be retrieved"

但是,如果通过双击 pfx 将证书和 key 添加到钥匙串(keychain),则可以按预期导出 key 。

那么,为什么上面的代码会导致无法导出 key 的问题呢?

最佳答案

在 Apple 的 Quinn 的帮助下,问题中描述的方法似乎应该有效,但没有。

使用旧的 CDSA 风格标志实际上是可行的,做这样的事情:-

OSStatus                            err;
SecExternalFormat                   format;
SecItemImportExportKeyParameters    params;

params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
params.flags = 0;
params.passphrase = (__bridge CFStringRef) pkcs12Password;
params.alertTitle = NULL;
params.alertPrompt = NULL;
params.accessRef = NULL;
params.keyUsage = NULL;
params.keyAttributes = (__bridge CFArrayRef) @[ @(CSSM_KEYATTR_EXTRACTABLE) ];

format = kSecFormatPKCS12;
err = SecItemImport(
    (__bridge CFDataRef) pkcs12Data, 
    CFSTR("p12"), 
    &format, 
    NULL, 
    0, 
    &params, 
    keychain, 
    NULL
);

注意 params.keyAttributes 的设置,它定义了可提取的 key 。

或者,可以使用旧的(已弃用的)SecKeychainItemImport API:-

BOOL                            success;
OSStatus                        err;
NSArray *                       result;
SecExternalFormat               format;
SecKeyImportExportParameters    params;
CFArrayRef                      importedItems;

result = nil;
importedItems = NULL;

format = kSecFormatPKCS12;
memset(&params, 0, sizeof(params));
params.passphrase = password;
params.keyAttributes = CSSM_KEYATTR_EXTRACTABLE;

err = SecKeychainItemImport(
   (CFDataRef) pkcs12Blob,     // importedData
   NULL,                       // fileNameOrExtension
   &format,                    // inputFormat
   NULL,                       // itemType
   0,                          // flags
   &params,                    // keyParams
   self->keychain,             // importKeychain
   &importedItems              // outItems
);
success = (err == noErr);

虽然函数 SecKeychainItemImport 在 Apple 的文档中被定义为已弃用,但我被告知它不太可能很快被删除。

关于c - 将证书插入钥匙串(keychain),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26781597/

相关文章:

javascript - 将 CORS 与 AWS API Gateway 结合使用有哪些安全隐患?

security - 保护生成的 PDF

c# - 从 RS232(com) 接收数据

c++ - Mongoose 网络库 : how to get authenticated user?

检查点是否在线段上

cocoa - 淡入/淡出 WebView

macos - Nginx 未运行。没有这样的文件或目录消息 -Mac

ruby-on-rails - ruby 存储在 mac 上的什么位置?

java - Google App Engine + Spring + 域对象实例安全

c - 如何使用STM32F4从MAX30100读取FIFO数据