如何导出从 CNG API 生成的 RSA 密钥?

How to export RSA Keys generated from CNG API?

我使用以下 CNG API 序列成功生成了 RSA 密钥:

BCryptOpenAlgorithmProvider(.., BCRYPT_RSA_ALGORITHM, ...);
BCryptGenerateKeyPair(..., 2048/*Key size*/, ...);
BCryptFinalizeKeyPair(...);
BCryptExportKey(..., BCRYPT_RSAPRIVATE_BLOB, ...);
BCryptExportKey(..., BCRYPT_RSAPUBLIC_BLOB, ...);

我对生成的缓冲区的哪一部分感到困惑,应该对其进行 Base64 编码以写出 PEM 文件?如果我对现有有效 PEM 文件所具有的整个缓冲区进行 Base64 编码,我在缓冲区中看不到 'MIIE'。

非常感谢任何指导。

确实不需要直接从 BCryptExportKeyNCryptExportKey 输出 base64 编码,但需要执行额外的步骤:

  1. 使用 BCryptExportKey(或 NCryptExportKeyBCRYPT_RSAFULLPRIVATE_BLOB (但不是 BCRYPT_RSAPRIVATE_BLOB )或 BCRYPT_RSAPUBLIC_BLOB
  2. 对结果 BCRYPT_RSAKEY_BLOB 进行编码 CNG_RSA_PRIVATE_KEY_BLOBCNG_RSA_PUBLIC_KEY_BLOB 并放入 CRYPT_PRIVATE_KEY_INFO
  3. PKCS_PRIVATE_KEY_INFO
  4. 编码CRYPT_PRIVATE_KEY_INFO
  5. 致电CryptBinaryToStringA

只有在这之后才会 'MIIE' 在缓冲区内

HRESULT bthr(BOOL b)
{
    return b ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}

HRESULT ExportToPem(_In_ BCRYPT_KEY_HANDLE hKey, BOOL bPrivate, _Out_ PSTR* ppsz, _Out_ PULONG pcch)
{
    HRESULT hr;
    CRYPT_PRIVATE_KEY_INFO PrivateKeyInfo = { 0, {const_cast<PSTR>(szOID_RSA_RSA)} };
    ULONG cbKey = 0;
    PUCHAR pbKey = 0;//really PBCRYPT_RSAKEY_BLOB

    PCWSTR pszBlobType;
    PCSTR lpszStructType;

    if (bPrivate)
    {
        pszBlobType = BCRYPT_RSAFULLPRIVATE_BLOB;
        lpszStructType = CNG_RSA_PRIVATE_KEY_BLOB;
    }
    else
    {
        pszBlobType = BCRYPT_RSAPUBLIC_BLOB;
        lpszStructType = CNG_RSA_PUBLIC_KEY_BLOB;
    }

    while (0 <= (hr = BCryptExportKey(hKey, 0, pszBlobType, pbKey, cbKey, &cbKey, 0)))
    {
        if (pbKey)
        {
            if (0 <= (hr = bthr(CryptEncodeObjectEx(X509_ASN_ENCODING, 
                lpszStructType, pbKey, CRYPT_ENCODE_ALLOC_FLAG, 0, 
                &PrivateKeyInfo.PrivateKey.pbData, &PrivateKeyInfo.PrivateKey.cbData))))
            {
                hr = bthr(CryptEncodeObjectEx(X509_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, 
                    &PrivateKeyInfo, CRYPT_ENCODE_ALLOC_FLAG, 0, 
                    &pbKey, &cbKey));

                LocalFree(PrivateKeyInfo.PrivateKey.pbData);

                if (0 <= hr)
                {
                    PSTR psz = 0;
                    ULONG cch = 0;
                    while (0 <= (hr = bthr(CryptBinaryToStringA(
                        pbKey, cbKey, CRYPT_STRING_BASE64, psz, &cch))))
                    {
                        if (psz)
                        {
                            *ppsz = psz, *pcch = cch;
                            break;
                        }

                        if (!(psz = (PSTR)LocalAlloc(0, cch)))
                        {
                            hr = HRESULT_FROM_WIN32(GetLastError());
                            break;
                        }
                    }

                    LocalFree(pbKey);
                }
            }
            break;
        }

        pbKey = (PUCHAR)alloca(cbKey);
    }

    return hr;
}

并使用它:

    PSTR psz;
    ULONG cch;
    if (0 <= ExportToPem(hKey, bPrivate, &psz, &cch))
    {
        PSTR pc = psz;
        ULONG cb;
        do 
        {
            cb = min(cch, 0x100);
            DbgPrint("%.*s", cb, pc);
        } while (pc += cb, cch -= cb);
        LocalFree(psz);
    }