I successfully generated RSA keys using the following sequence of CNG APIs:
BCryptOpenAlgorithmProvider(.., BCRYPT_RSA_ALGORITHM, ...);
BCryptGenerateKeyPair(..., 2048/*Key size*/, ...);
BCryptFinalizeKeyPair(...);
BCryptExportKey(..., BCRYPT_RSAPRIVATE_BLOB, ...);
BCryptExportKey(..., BCRYPT_RSAPUBLIC_BLOB, ...);
I'm confused though which part of the buffer generated, should be Base64 encoded to write out a PEM file? I'm not seeing 'MIIE' within the buffer if I Base64 encode the whole buffer that existing valid PEM files have.
Any guidance is greatly appreciated.
CodePudding user response:
really need not direct base64 encode output from BCryptExportKey
or NCryptExportKey
but do extra steps:
- use
BCryptExportKey
(orNCryptExportKey
) withBCRYPT_RSAFULLPRIVATE_BLOB
(but notBCRYPT_RSAPRIVATE_BLOB
) orBCRYPT_RSAPUBLIC_BLOB
- encode resulting
BCRYPT_RSAKEY_BLOB
withCNG_RSA_PRIVATE_KEY_BLOB
orCNG_RSA_PUBLIC_KEY_BLOB
and put toCRYPT_PRIVATE_KEY_INFO
- encode
CRYPT_PRIVATE_KEY_INFO
withPKCS_PRIVATE_KEY_INFO
- call
CryptBinaryToStringA
only after this will be 'MIIE' within the buffer
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;
}
and use it:
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);
}