Home > database >  How to export RSA Keys generated from CNG API?
How to export RSA Keys generated from CNG API?

Time:11-11

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:

  1. use BCryptExportKey (or NCryptExportKey) with BCRYPT_RSAFULLPRIVATE_BLOB (but not BCRYPT_RSAPRIVATE_BLOB ) or BCRYPT_RSAPUBLIC_BLOB
  2. encode resulting BCRYPT_RSAKEY_BLOB with CNG_RSA_PRIVATE_KEY_BLOB or CNG_RSA_PUBLIC_KEY_BLOB and put to CRYPT_PRIVATE_KEY_INFO
  3. encode CRYPT_PRIVATE_KEY_INFO with PKCS_PRIVATE_KEY_INFO
  4. 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);
    }
  • Related