IInspectable
IInspectable

Reputation: 51345

How to convert a public key in PEM format to a CNG key blob?

I need to verify the signature of a message using Windows' Cryptography API: Next Generation. I have the message, its signature, and a public key in PEM format, plus a prototype implementation that exhibits the desired behavior.

There's one issue with the code that I need to fix: Converting the public key into a key blob as used by CNG.

Currently, I'm decoding the public key

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETHfi8foQF4UtSNVxSFxeu7W+gMxd
SGElhdo7825SD3Lyb+Sqh4G6Kra0ro1BdrM6Qx+hsUx4Qwdby7QY0pzxyA==
-----END PUBLIC KEY-----

using CryptStringToBinaryA, which produces the following binary data:

30 59 30 13 06 07 2A 86  48 CE 3D 02 01 06 08 2A
86 48 CE 3D 03 01 07 03  42 00 04 4C 77 E2 F1 FA
10 17 85 2D 48 D5 71 48  5C 5E BB B5 BE 80 CC 5D
48 61 25 85 DA 3B F3 6E  52 0F 72 F2 6F E4 AA 87
81 BA 2A B6 B4 AE 8D 41  76 B3 3A 43 1F A1 B1 4C
78 43 07 5B CB B4 18 D2  9C F1 C8

Everything looks fine. Presumably, this is in DER format, using ASN.1. It's 91 bytes in size, specifying elliptic curve cryptography (ECDSA prime256v1) with the [x] and [y] entries starting at offset 27 (for a total of 32 bytes each).

Since I need a key blob to pass into BCryptImportKeyPair, specifically a BCRYPT_ECCKEY_BLOB:

typedef struct _BCRYPT_ECCKEY_BLOB
{
    ULONG   dwMagic;
    ULONG   cbKey;
} BCRYPT_ECCKEY_BLOB, *PBCRYPT_ECCKEY_BLOB;

my prototype creates one ad-hoc, by supplying the BCRYPT_ECDSA_PUBLIC_P256_MAGIC and a length of 32, followed by the respective 64 bytes from the public key above.

Now that works, but I'd clearly want to implement something more robust. The key seems to encode all data necessary to populate an appropriate CNG key blob, but I'm failing to find any APIs that would help me parse out this information.

Does the CNG (or Wincrypt) provide any functionality that would allow me to either construct a CNG key blob, or otherwise get a BCRYPT_KEY_HANDLE out of a PEM key? Or am I left with implementing my own ASN.1 parser?

Upvotes: 4

Views: 3182

Answers (3)

trindflo
trindflo

Reputation: 349

And here is my promised solution for larger (> 1024 bit) keys

// BCryptLoadLongDSA.cpp : Example of using BCrypt API to import long (> 1024 bit) DSA key.
//


#include <ntstatus.h>
#define WIN32_NO_STATUS 1
#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>

ULONG PemToBCrypt(char *pszPub, char *pszP, char *pszQ, char *pszG, BCRYPT_KEY_HANDLE *phKey);
void CStringAsciiHexToBin(char *pSrc, char *pDest);

// Generated a strong (2048 bit) DSA key using openssl then dumped using: openssl dsa -pubin -in pubkey.pem -text
// Important: the number of bytes must be exactly keysize/8 for long keys. You cannot add a leading zero byte or the BCryptImportKeyPair will fail with STATUS_INVALID_PARAMETER (maybe?)
// openssl will dump the parameters with an extra first byte of 00 if the first byte has the most significant bit set. You must remove the extra 00 byte if it is there.
char cPub[] = "c4e4502c8c2210060de6724486276e5eab1ed33ba02b5d02a872e833dafab545e8c0219d7fe430f4a368f846c9d4391dc26ea6dec39cfb8e3b047d0d1d40c1ab552aa17df3f446fe762b83eceb15260bb9e902641c4c6c17156b07895c5fcf54f86751ec45c85224c46d9c1cea364e58f582432802d3398b3b96c03a01cc7ff5712bfc497bbb40a1d3f547a19226209a19bae5c155e226bae9e72c7d855900a78621c23cb00569d74afe1e0cfd6baef05699d777bda9e397602859f1cc74ac8a1dc564c2c90a40104b3c929d0393a56b395ccb9237edc0a2c57cc341ffdf5aa8ffa14e6907bc548874dec2fa794dcfdb7afc081ce9a71e4bcbcf6ebfb46e22ee";
char cP[] = "ec112ddd6629e4b7a206a7103af77747316ed716af34e025368a60a5c9211db25a27aa19512c11cb8338b0e7b7f52eded2a042007668badee747cf1e6d77b30faf24e24cf67a0d461a21703c8e206a2180c31f3e273f8b223b3eb98d049dc727808e8c60e97e20da952f5b83c0c41260c3c6933118c561dbeee2e371c50fee7a528fbd4897d14dc218744f612886235cf755b9b79db2e7fdf226e8e1ae32f8f68804a027cef9e6bc92135243afd34144c1aaae583cf30ccb3dac3835aa72c9fa099ef6cc4e9c482b251bad985b3c4e8dcf16ed833cdb9fc62326e9f14a69ab20b7562c5164dbdf2728ec9f0a80f176e871f4b223d39b5999198facf3583772f5";
char cG[] = "c47c7d8f19e66cba79efa3a7990ac559bd7145dec9fad6ce329692df419fac606b56114b96d69e36a9f9b824ff18641ddce06807ccd1d0cbb444e23252c18dd7d4518bdc600398e015f3794509bc28c355ac4a99cb306604560d4dc50fa63673f610816e17f32cc6475a51dd4c3d36c79b4e614df842b2e7a15e06d3e0ffbe85296df71bcaaf94f4b9eaf9b8ac02e779356b0f43346b122cdb362a84891e3319af70c621aa374b582400a0e565105e1a4581248860ef8a997152c9a91faa1bb9db1dc2908e755b1644c5a46f42a9144dccf7dae154c97cbac214eafe6ec99434d056200e803109124b8b526e7f98d4f967ca667b2b0cefc7180fc53a8726645f";
// Q must be exactly 32 bytes for a long key. It also looks like Q and Seed must always be the same size. In this case, I needed to add leading bytes to get to 32.
// When adding leading bytes, do not just add zeroes! Instead you must sign-extend the value. In this sample case, I add 'ff's to the beginning of the string.
// The dump from openssl reads: 
//Q:
//    00:90:d5:39:ef:5e:90:bb:90:78:b7:52:f1:b3:b3:
//    f8:4c:22:11:bc:a2:bd:84:08:ed:07:bd:e8:a9
// 
// The leading 00: was added by openssl because :90: has the most significant bit set. I needed to remove that, then because :90: has the most significant bit set I needed
// to sign-extend that with 'ff's in order to get to the 32 byte size Microsoft defined for this field.
// How did I find that out? I started from my previous working example and exhausted every other possibility! This has been a nightmare. Or this is a triumph? Huge success?
//
char cQ[] = "ffffffff90d539ef5e90bb9078b752f1b3b3f84c2211bca2bd8408ed07bde8a9";

int main()
{
    NTSTATUS nts;
    BCRYPT_KEY_HANDLE hKey = NULL;

    nts = PemToBCrypt(cPub, cP, cQ, cG, &hKey);
    printf("PemToBCrypt returns 0x%X\n", nts);

    if (NULL != hKey)
        nts = BCryptDestroyKey(hKey);

    return nts;
}

// This is the struct that must be passed to the BCryptImportKeyPair API if defining a long (> 1024 bit) public key
// We don't know cbKey until run time, so we must dynamically allocate the array
//typedef struct _BCRYPT_DSAKEY_BLOB_PUBLIC {
//    BCRYPT_DSA_KEY_BLOB_V2 h;      // Long DSA blobs start with this header 
//    UCHAR Seed[cbSeedLength];         // In header for short keys
//    UCHAR q[cbGroupSize];             // In header for short keys
//    UCHAR Modulus[cbKeyValue];        // Everyone calls this 'p' (thanks Microsoft!)
//    UCHAR Generator[cbKeyValue];      // Everyone calls this 'g'
//    UCHAR Public[cbKeyValue];         // This is the actual Public key and not 'P'
//} BCRYPT_DSAKEY_BLOB_PUBLIC;


/// <summary>
/// Convert DSA Public key values to BCrypt parameters, Import, and return BCrypt key handle
/// </summary>
/// <param name="pszPub"></param>
/// <param name="pszP"></param>
/// <param name="pszQ"></param>
/// <param name="pszG"></param>
/// <param name="phKey"></param>
/// <returns>NTSTATUS</returns>
ULONG PemToBCrypt(char *pszPub, char *pszP, char *pszQ, char *pszG, BCRYPT_KEY_HANDLE *phKey)
{
    NTSTATUS nts;
    ULONG dwError = STATUS_SUCCESS;
    BCRYPT_ALG_HANDLE hAlg = NULL;

    ULONG cbKeyValue = strlen(pszPub) / 2;
    ULONG cbGroup = 32;         // Defined for keys > 1024 bits. This field must be 32 bytes long
    ULONG cbSeed = cbGroup;     // Assuming this must be the same size as Group - they are equal for keys <= 1024
        // See layout of BCRYPT_DSAKEY_BLOB_PUBLIC above
    ULONG cbBlob = sizeof(BCRYPT_DSA_KEY_BLOB_V2) + 3 * cbKeyValue + cbGroup + cbSeed;

    // These Heap functions align memory among other features. Alignment required. Otherwise similar to calloc
    HANDLE HHeap = GetProcessHeap();
    BCRYPT_DSA_KEY_BLOB_V2 *h = (BCRYPT_DSA_KEY_BLOB_V2 *)HeapAlloc(HHeap, HEAP_ZERO_MEMORY, cbBlob);
    if (NULL == h)
    {
        return STATUS_NO_MEMORY;
    }
    char *pSeed = ((char *)h) + sizeof(BCRYPT_DSA_KEY_BLOB_V2);     // past header (See layout of BCRYPT_DSAKEY_BLOB_PUBLIC above)
    char *pQ = pSeed + cbSeed;
    char *pModulus = pQ + cbGroup;
    char *pGenerator = pModulus + cbKeyValue;
    char *pPublic = pGenerator + cbKeyValue;

    *phKey = NULL;      // Default to failure

    // Get access to the algorithm
    nts = BCryptOpenAlgorithmProvider(&hAlg,
        BCRYPT_DSA_ALGORITHM,
        MS_PRIMITIVE_PROVIDER,              // The default; this parameter could be NULL
        0 // | BCRYPT_PROV_DISPATCH         // This flag may only be used in a driver and allows it to be called from DISPATCH level
    );
    if (STATUS_SUCCESS != nts)
    {
        printf("BCryptOpenAlgorithmProvider failed with status 0x%X\n", nts);
        BCryptCloseAlgorithmProvider(hAlg, 0);
        HeapFree(HHeap, 0, h);
        return nts;
    }

    h->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC_V2;        // Long blobs use this magic
    h->cbKey = cbKeyValue;
    // You need to go to NIST-FIPS.186-4 for this to see there are only 4 legal values for (bits,hash): (1024, 160), (2048, 224), (2048, 256), and (3072, 256)
    h->hashAlgorithm = DSA_HASH_ALGORITHM_SHA256;   // There is only SHA1, SHA256, and SHA512 so by process of elimination, if > 1024 this must be 256
    h->standardVersion = DSA_FIPS186_3;             // There were two options, I went with the latest and it works
    h->cbSeedLength = cbSeed;
    h->cbGroupSize = cbGroup;
    // Only worked out this next line by using BCryptExportKey on https://stackoverflow.com/users/6401656/rbmm solution that uses CryptImportPublicKeyInfoEx2
    h->Count[0] = h->Count[1] = h->Count[2] = h->Count[3] = 0xff;

    CStringAsciiHexToBin(pszPub, pPublic);      // Convert ASCII hex into binary
    CStringAsciiHexToBin(pszP, pModulus);
    CStringAsciiHexToBin(pszQ, pQ);
    CStringAsciiHexToBin(pszG, pGenerator);

    nts = BCryptImportKeyPair(
        hAlg,
        NULL,
        BCRYPT_DSA_PUBLIC_BLOB,
        phKey,
        (PUCHAR)h,
        cbBlob,
        0 // BCRYPT_NO_KEY_VALIDATION
    );
    if (STATUS_SUCCESS != nts)
    {
        dwError = GetLastError();
        printf("BCryptImportKeyPair failed with status=0x%X, LastError=0x%X\n", nts, dwError);
        // Import failed and GetLastError() reported success while I was working out the parameters
        if (STATUS_SUCCESS == dwError)
            dwError = nts;
    }

    HeapFree(HHeap, 0, h);  // We have our key, all done with the info
    BCryptCloseAlgorithmProvider(hAlg, 0);
    return dwError;
}

/// <summary>
/// Convert and copy an ASCII-HEX string (in lower case) into a destination binary array. No error checking
/// </summary>
/// <param name="pSrc">Assumed AsciiHex C string with only lower case letters</param>
/// <param name="pDest">Binary array assumed large enough for output - strlen(pSrc)/2</param>
void CStringAsciiHexToBin(char *pSrc, char *pDest)
{
    for (; *pSrc; )
    {
        // Upper nibble
        unsigned char ch = *pSrc++ - '0';    // If char is 0-9, we are done here
        if (ch > 9)             // Otherwise it is a letter a-f in lower case
            ch = ch + '0' - 'a' + 10;

        // Lower nibble
        unsigned char ch2 = *pSrc++ - '0';    // If char is 0-9, we are done here
        if (ch2 > 9)             // Otherwise it is a letter a-f in lower case
            ch2 = ch2 + '0' - 'a' + 10;

        *pDest = (ch << 4) | ch2;

        pDest++;
    }
}

Upvotes: 0

trindflo
trindflo

Reputation: 349

I needed to use RbMm's answer (which is a great answer) to make my solution work. The advantage of my solution is that it uses only BCrypt calls. CryptImportPublicKeyInfoEx2 fails for keys > 1024. Having a working solution allowed me to BCryptExportKey() and dump the fields to find how the undocumented Count parameter must be initialized.

This solution is for short keys. I will post a solution for long keys next. The error checking is limited.

// BCryptLoadShortDSA.cpp : Example of using BCrypt API to import short (<= 1024 bit) DSA key.
//

#include <ntstatus.h>
#define WIN32_NO_STATUS 1
#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>

ULONG PemToBCrypt(char *pszPub, char *pszP, char *pszQ, char *pszG, BCRYPT_KEY_HANDLE *phKey);
void CStringAsciiHexToBin(char *pSrc, char *pDest);

// Generated a weak (512 bit) DSA key using a Java program then dumped the public key using openssl: openssl dsa -pubin -in pubkey.pem -text
// Important: the number of bytes must be exactly keysize/8 for short keys. You cannot add a leading zero byte or the BCryptImportKeyPair will fail with STATUS_INVALID_PARAMETER
// openssl will dump the parameters with an extra first byte of 00 if the first byte has the most significant bit set. You must remove the extra 00 byte if it is there.
char cPub[] = "582d05345052ae29a34850295cf555d2ff7239dadc12571f5e71eb6d89ab91c44fc80f2ffc3beecf656ef67465b6627772340ad50e041b780f487b4e762ab5c1";
char cP[] = "fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e17";
char cG[] = "678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4";
// Q must be exactly 20 bytes for a short key. It also looks like Q and Seed must always be the same size. Again, openssl might add a leading zero that you must remove
char cQ[] = "962eddcc369cba8ebb260ee6b6a126d9346e38c5";

int main()
{
    NTSTATUS nts;
    BCRYPT_KEY_HANDLE hKey = NULL;

    nts = PemToBCrypt(cPub, cP, cQ, cG, &hKey);
    printf("PemToBCrypt returns 0x%X\n", nts);

    if (NULL != hKey)
        nts = BCryptDestroyKey(hKey);

    return nts;
}

// This is the struct that must be passed to the BCryptImportKeyPair API if only defining a public key
// We don't know cbKey until run time, so we must dynamically allocate the array
//typedef struct _BCRYPT_DSAKEY_BLOB_PUBLIC {
//    BCRYPT_DSA_KEY_BLOB h;        // Short DSA blobs start with this header 
//    UCHAR Modulus[cbKeyValue];        // Everyone calls this 'p' (thanks Microsoft!)
//    UCHAR Generator[cbKeyValue];      // Everyone calls this 'g'
//    UCHAR Public[cbKeyValue];         // This is the actual Public key and not 'P'
//} BCRYPT_DSAKEY_BLOB_PUBLIC;


/// <summary>
/// Convert DSA Public key values to BCrypt parameters, Import, and return BCrypt key handle
/// </summary>
/// <param name="pszPub"></param>
/// <param name="pszP"></param>
/// <param name="pszQ"></param>
/// <param name="pszG"></param>
/// <param name="phKey"></param>
/// <returns>NTSTATUS</returns>
ULONG PemToBCrypt(char *pszPub, char *pszP, char *pszQ, char *pszG, BCRYPT_KEY_HANDLE *phKey)
{
    NTSTATUS nts;
    ULONG dwError = STATUS_SUCCESS;
    BCRYPT_ALG_HANDLE hAlg = NULL;

    ULONG cbKeyValue = strlen(pszPub) / 2;
    ULONG cbBlob = sizeof(BCRYPT_DSA_KEY_BLOB) + 3 * cbKeyValue;    // See layout of BCRYPT_DSAKEY_BLOB_PUBLIC above
    
    // These Heap functions align memory among other features. Alignment required. Otherwise similar to calloc
    HANDLE HHeap = GetProcessHeap();
    BCRYPT_DSA_KEY_BLOB *h = (BCRYPT_DSA_KEY_BLOB *)HeapAlloc(HHeap, HEAP_ZERO_MEMORY, cbBlob);
    if (NULL == h)
    {
        return STATUS_NO_MEMORY;
    }
    char *pModulus = ((char *)h) + sizeof(BCRYPT_DSA_KEY_BLOB);     // past the header (See layout of BCRYPT_DSAKEY_BLOB_PUBLIC above)
    char *pGenerator = pModulus + cbKeyValue;
    char *pPublic = pGenerator + cbKeyValue;

    *phKey = NULL;      // Default to failure

    // Get access to the algorithm
    nts = BCryptOpenAlgorithmProvider(&hAlg,
        BCRYPT_DSA_ALGORITHM,
        MS_PRIMITIVE_PROVIDER,              // The default; this parameter could be NULL
        0 // | BCRYPT_PROV_DISPATCH         // This flag may only be used in a driver and allows it to be called from DISPATCH level
    );
    if (STATUS_SUCCESS != nts)
    {
        printf("BCryptOpenAlgorithmProvider failed with status 0x%X\n", nts);
        HeapFree(HHeap, 0, h);
        return nts;
    }

    h->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC;       // Short blobs use this magic
    h->cbKey = cbKeyValue;
    // Only worked out this next line by using BCryptExportKey on RbMm solution that uses CryptImportPublicKeyInfoEx2
    h->Count[0] = h->Count[1] = h->Count[2] = h->Count[3] = 0xff;

    CStringAsciiHexToBin(pszPub, pPublic);      // Convert ASCII hex into binary
    CStringAsciiHexToBin(pszP, pModulus);
    CStringAsciiHexToBin(pszQ, (char *)h->q);
    CStringAsciiHexToBin(pszG, pGenerator);

    nts = BCryptImportKeyPair(
        hAlg,
        NULL,
        BCRYPT_DSA_PUBLIC_BLOB,
        phKey,
        (PUCHAR)h,
        cbBlob,
        0 //BCRYPT_NO_KEY_VALIDATION
    );
    if (STATUS_SUCCESS != nts)
    {
        dwError = GetLastError();
        printf("BCryptImportKeyPair failed with status=0x%X, LastError=0x%X\n", nts, dwError);
        // Import failed and GetLastError() reported success while I was working out the parameters
        if (STATUS_SUCCESS == dwError)
            dwError = nts;
    }

    HeapFree(HHeap, 0, h);  // We have our key, all done with the info
    BCryptCloseAlgorithmProvider(hAlg, 0);
    return dwError;
}

/// <summary>
/// Convert and copy an ASCII-HEX string (in lower case) into a destination binary array. No error checking
/// </summary>
/// <param name="pSrc">Assumed AsciiHex C string with only lower case letters</param>
/// <param name="pDest">Binary array assumed large enough for output - strlen(pSrc)/2</param>
void CStringAsciiHexToBin(char *pSrc, char *pDest)
{
    for (; *pSrc; )
    {
        // Upper nibble
        unsigned char ch = *pSrc++ - '0';    // If char is 0-9, we are done here
        if (ch > 9)             // Otherwise it is a letter a-f in lower case
            ch = ch + '0' - 'a' + 10;

        // Lower nibble
        unsigned char ch2 = *pSrc++ - '0';    // If char is 0-9, we are done here
        if (ch2 > 9)             // Otherwise it is a letter a-f in lower case
            ch2 = ch2 + '0' - 'a' + 10;

        *pDest = (ch << 4) | ch2;

        pDest++;
    }
}

Upvotes: 1

RbMm
RbMm

Reputation: 33706

for convert PEM public key to CNG - generic steps is next:

  1. CryptStringToBinaryA for convert string to binary
  2. CryptDecodeObjectEx with X509_PUBLIC_KEY_INFO - convert binary to CERT_PUBLIC_KEY_INFO
  3. CryptImportPublicKeyInfoEx2 - import CERT_PUBLIC_KEY_INFO to CNG

example of code

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

ULONG PemToCNG(_In_ PCSTR pszString, _Out_ BCRYPT_KEY_HANDLE* phKey)
{
    PBYTE pb = 0;
    ULONG cb = 0;

    ULONG dwError;

    while (NOERROR == (dwError = BOOL_TO_ERROR(
        CryptStringToBinaryA(pszString, 0, CRYPT_STRING_BASE64_ANY, pb, &cb, 0, 0))))
    {
        if (pb)
        {
            PCERT_PUBLIC_KEY_INFO PublicKeyInfo;

            if (NOERROR == (dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 
                X509_PUBLIC_KEY_INFO, pb, cb, 
                CRYPT_DECODE_ALLOC_FLAG|
                CRYPT_DECODE_NOCOPY_FLAG|
                CRYPT_DECODE_SHARE_OID_STRING_FLAG, 
                0, &PublicKeyInfo, &cb))))
            {
                dwError = BOOL_TO_ERROR(CryptImportPublicKeyInfoEx2(
                    X509_ASN_ENCODING, PublicKeyInfo, 0, 0, phKey));

                LocalFree(PublicKeyInfo);
            }
            break;
        }

        if (!(pb = (PBYTE)LocalAlloc(0, cb)))
        {
            dwError = GetLastError();
            break;
        }
    }

    if (pb)
    {
        LocalFree(pb);
    }

    return dwError;
}

void TestPem()
{
    static const char Pem[] = 
        "-----BEGIN PUBLIC KEY-----\r\n"
        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETHfi8foQF4UtSNVxSFxeu7W+gMxd\r\n"
        "SGElhdo7825SD3Lyb+Sqh4G6Kra0ro1BdrM6Qx+hsUx4Qwdby7QY0pzxyA==\r\n"
        "-----END PUBLIC KEY-----";

    BCRYPT_KEY_HANDLE hKey;
    if (NOERROR == PemToCNG(Pem, &hKey))
    {
        PBYTE pb = 0;
        ULONG cb = 0;
        while (0 <= BCryptExportKey(hKey, 0, BCRYPT_PUBLIC_KEY_BLOB, pb, cb, &cb, 0))
        {
            if (pb)
            {
                PSTR psz = 0;
                ULONG cch = 0;
                while (CryptBinaryToStringA(pb, cb, CRYPT_STRING_HEXASCII, psz, &cch))
                {
                    if (psz)
                    {
                        DbgPrint(psz);
                        break;
                    }

                    psz = (PSTR)alloca(cch * sizeof(char));
                }
                break;
            }

            pb = (PBYTE)alloca(cb);
        }

        BCryptDestroyKey(hKey);
    }

}

output is

45 43 53 31 20 00 00 00  4c 77 e2 f1 fa 10 17 85   ECS1 ...Lw......
2d 48 d5 71 48 5c 5e bb  b5 be 80 cc 5d 48 61 25   -H.qH\^.....]Ha
85 da 3b f3 6e 52 0f 72  f2 6f e4 aa 87 81 ba 2a   ..;.nR.r.o.....*
b6 b4 ae 8d 41 76 b3 3a  43 1f a1 b1 4c 78 43 07   ....Av.:C...LxC.
5b cb b4 18 d2 9c f1 c8                            [.......

Upvotes: 7

Related Questions