问题描述
我使用Microsoft CryptoAPI生成了一个新的RSA密钥对,现在正尝试将私钥导出到PKCS#8加密(受密码保护)的PEM文件中.
Using Microsoft CryptoAPI, I've generated a new RSA key pair, and am now trying to export the private key to a PKCS#8 encrypted (password-protected) PEM file.
我首先研究了CryptExportPKCS8()和CryptExportPKCS8Ex(),但是前者不支持加密密钥,而后者是未由crypt32.dll导出.MSDN说,无论如何,这两个功能都已被弃用.
I first investigated CryptExportPKCS8() and CryptExportPKCS8Ex(), but the former doesn't support encrypting the key, and the latter is not exported by crypt32.dll. MSDN says that both functions have been deprecated anyway.
我当前的尝试是将派生自密码的会话密钥传递给CryptExportKey():
My current attempt is to pass a session key derived from the password to 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()为止.
All calls succeed up until the commented CryptEncodeObjectEx().
如果我不将会话密钥传递给CryptExportKey(),那么我可以成功地使用CryptEncodeObjectEx()对私钥进行编码,但显然它是纯文本的.
If I don't pass the session key to CryptExportKey() then I can successfully use CryptEncodeObjectEx() to encode the private key, but obviously it is then plain-text.
如何导出受密码保护的私钥?我派生会话密钥的方式有问题吗?PKCS_RSA_PRIVATE_KEY是错误的编码类型吗?
How can I export a password-protected private key? Is there something wrong with the way I derive the session key? Is PKCS_RSA_PRIVATE_KEY the wrong encoding type?
我一直在Windows 7上的Visual Studio 2013中进行测试.
I've been testing in Visual Studio 2013 on Windows 7.
推荐答案
PKCS_RSA_PRIVATE_KEY仅在未加密私钥blob时使用.加密后,您必须使用PKCS_ENCRYPTED_PRIVATE_KEY_INFO.工作代码示例
PKCS_RSA_PRIVATE_KEY used only when private key blob not encrypted. when it encrypted you must use PKCS_ENCRYPTED_PRIVATE_KEY_INFO. example of working code
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;
}
这篇关于如何使用MS CryptoAPI导出受密码保护的私钥?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!