将证书插入钥匙串

时间:2014-11-06 14:13:47

标签: c macos security ssl core-foundation

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

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应用程序。

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

  

“发生了错误。无法导出项目。无法检索此项目的内容”

但是,如果通过双击pfx将证书和密钥添加到钥匙串中,则导出密钥将按预期工作。

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

1 个答案:

答案 0 :(得分:1)

在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的设置,它定义了可提取的密钥。

或者,可以使用旧的(已弃用的)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);

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