How to export a password-protected private key using MS CryptoAPI?

微笑、不失礼 提交于 2019-12-11 06:05:05

问题


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.

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.

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

All calls succeed up until the commented 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.

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?

I've been testing in Visual Studio 2013 on Windows 7.


回答1:


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;
}


来源:https://stackoverflow.com/questions/37717931/how-to-export-a-password-protected-private-key-using-ms-cryptoapi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!