真的不需要从BCryptExportKey 或NCryptExportKey 直接对输出进行base64 编码,而是做一些额外的步骤:
- 使用
BCryptExportKey(或NCryptExportKey)
BCRYPT_RSAFULLPRIVATE_BLOB(但不是BCRYPT_RSAPRIVATE_BLOB)或BCRYPT_RSAPUBLIC_BLOB
- 编码结果
BCRYPT_RSAKEY_BLOB
CNG_RSA_PRIVATE_KEY_BLOB 或 CNG_RSA_PUBLIC_KEY_BLOB 并发送至 CRYPT_PRIVATE_KEY_INFO
- 用
PKCS_PRIVATE_KEY_INFO编码CRYPT_PRIVATE_KEY_INFO
- 致电
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);
}