如何使用MS CryptoAPI导出受密码保护的私钥?

时间:2016-06-09 05:58:09

标签: c++ winapi encryption cryptoapi

使用Microsoft CryptoAPI,我已经生成了一个新的RSA密钥对,现在正尝试将私钥导出到PKCS#8加密(密码保护)的PEM文件。

我首先调查了CryptExportPKCS8()和CryptExportPKCS8Ex(),但前者并不支持加密密钥,后者是not exported by crypt32.dll。 MSDN说这两个函数都已被弃用了。

我目前的尝试是将从密码派生的会话密钥传递给CryptExportKey():

HCRYPTPROV provider;
BOOL result = CryptAcquireContext(&provider, CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT);
HCRYPTKEY keyPair;
result = CryptGenKey(provider, CALG_RSA_KEYX, (2048 << 16) | CRYPT_EXPORTABLE, &keyPair);

HCRYPTHASH hash;
result = CryptCreateHash(provider, CALG_SHA1, 0, 0, &hash);
const char *password = "password";
result = CryptHashData(hash, (const BYTE *)password, strlen(password), 0);
HCRYPTKEY sessionKey;
result = CryptDeriveKey(provider, CALG_3DES, hash, CRYPT_EXPORTABLE, &sessionKey);

DWORD blobSize;
result = CryptExportKey(keyPair, sessionKey, PRIVATEKEYBLOB, 0, NULL, &blobSize);
BYTE *blobBytes = new BYTE[blobSize];
result = CryptExportKey(keyPair, sessionKey, PRIVATEKEYBLOB, 0, blobBytes, &blobSize);

DWORD derSize;
// This throws "First-chance exception ... Access violation reading ..." and returns FALSE
result = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, blobBytes, 0, NULL, NULL, &derSize);
// error is 3221225477 (0xC0000005)
DWORD error = GetLastError();
BYTE *derBytes = new BYTE[derSize];
result = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, blobBytes, 0, NULL, derBytes, &derSize);

// ... CryptBinaryToString() to convert to PEM

// ... Write PEM to file

所有调用都会一直持续到被注释的CryptEncodeObjectEx()。

如果我没有将会话密钥传递给CryptExportKey(),那么我可以成功使用CryptEncodeObjectEx()来编码私钥,但显然它是纯文本。

如何导出受密码保护的私钥?导出会话密钥的方式有问题吗? PKCS_RSA_PRIVATE_KEY是否是错误的编码类型?

我已经在Windows 7上的Visual Studio 2013中进行了测试。

1 个答案:

答案 0 :(得分:0)

PKCS_RSA_PRIVATE_KEY仅在私钥blob未加密时使用。加密时,必须使用PKCS_ENCRYPTED_PRIVATE_KEY_INFO。工作代码的例子

BOOL expKey(PCSTR password)
{
    BOOL fOk = FALSE;
    HCRYPTPROV hProv;
    if (CryptAcquireContext(&hProv, 0, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
    {
        HCRYPTKEY hKey, hExpKey;
        HCRYPTHASH hHash;

        BOOL f = FALSE;

        if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
        {
            if (CryptHashData(hHash, (PBYTE)password, (ULONG)strlen(password), 0))
            {
                f = CryptDeriveKey(hProv, CALG_3DES, hHash, 0, &hExpKey);
            }
            CryptDestroyHash(hHash);
        }

        if (f)
        {
            if (CryptGenKey(hProv, CALG_RSA_KEYX, RSA1024BIT_KEY*2|CRYPT_EXPORTABLE, &hKey))
            {
                CRYPT_ENCRYPTED_PRIVATE_KEY_INFO cepki = {{ szOID_RSA_DES_EDE3_CBC}};
                if (
                    CryptExportKey(hKey, hExpKey, PRIVATEKEYBLOB, 0, 0, &cepki.EncryptedPrivateKey.cbData) &&
                    CryptExportKey(hKey, hExpKey, PRIVATEKEYBLOB, 0, cepki.EncryptedPrivateKey.pbData = (PBYTE)alloca(cepki.EncryptedPrivateKey.cbData), &cepki.EncryptedPrivateKey.cbData)
                    )
                {
                    ULONG cb;
                    PVOID pvEncoded;
                    if (CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ENCRYPTED_PRIVATE_KEY_INFO, &cepki, CRYPT_ENCODE_ALLOC_FLAG, 0, &pvEncoded, &(cb = sizeof(PVOID))))
                    {
                        fOk = TRUE;
                        LocalFree(pvEncoded);
                    }
                }
            }
            CryptDestroyKey(hExpKey);
        }
        CryptReleaseContext(hProv, 0);
    }

    return fOk;
}