3

I've been trying to perform a straight forward SHA256 HMAC using wincrypt/cryptoapi/Cryptography API: Next Generation (CNG) and i'm really struggling. My target is Windows 8.

I can not find the correct methods or find any examples anywhere. I am looking to do the following in C/C++, that is demonstrated in C# below

        HMAC hashMaker = new HMACSHA256(Encoding.ASCII.GetBytes("SecretKey"));
        byte[] hash = hashMaker.ComputeHash(Encoding.ASCII.GetBytes("<SomeXmlData />"));
        string hashStr = BitConverter.ToString(hash);

it returns the hash: B2-42-48-67-5A-B8-03-87-5B-00-D7-8C-65-5A-AE-B7-92-E3-F9-27-40-C1-01-A5-37-74-E1-65-51-9F-F6-6A.

Has anybody succeeded to perform a straight forward HMAC using the cryptoapi?

Dan H
  • 706
  • 1
  • 6
  • 15
  • 1
    There is an [example of using SHA256](http://msdn.microsoft.com/en-us/library/windows/desktop/aa376217(v=vs.85).aspx) all you would need to do is substitute the algorithm from vanilla SHA256 to use [`BCRYPT_SP800108_CTR_HMAC_ALGORITHM`](http://msdn.microsoft.com/en-us/library/windows/desktop/aa375534(v=vs.85).aspx) instead. You'll note in the example they create an algorithm and a hash, you need to change the *algorithm*. Also you don't want the ASCII of your XML, certain characters may get replaced. – Mgetz Mar 03 '14 at 13:29
  • yes I agree using ASCII for XML would be a bad idea. Some char's would get encoded incorrectly. Lucky this was just an example content as I knew the conversion to c would be easier, than say UFT16. – Dan H Mar 03 '14 at 19:42

1 Answers1

11

Thank you for the information Mgetz. I never knew about the BCrypt set of methods. It is a lot easier for HMAC than CryptHashData of the wincrypt/cryptoapi. From the example of using hashing using SHA256 I was able to create the HMAC code. You only need to add BCRYPT_ALG_HANDLE_HMAC_FLAG to the last parameter of BCryptOpenAlgorithmProvider and include the key in the call to BCryptCreateHash.

This is the completed code:

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib") 
#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)

#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)

void __cdecl wmain(
    int                      argc,
    __in_ecount(argc) LPWSTR *wargv)
{
    BCRYPT_ALG_HANDLE       hAlg = NULL;
    BCRYPT_HASH_HANDLE      hHash = NULL;
    NTSTATUS                status = STATUS_UNSUCCESSFUL;
    DWORD                   cbData = 0,
        cbHash = 0,
        cbHashObject = 0;
    PBYTE                   pbHashObject = NULL;
    PBYTE                   pbHash = NULL;
    CONST BYTE key[] = { "SecretKey" };
    CONST BYTE message[] = { "<SomeXmlData />" };

    //open an algorithm handle
    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
        &hAlg,
        BCRYPT_SHA256_ALGORITHM,
        NULL,
        BCRYPT_ALG_HANDLE_HMAC_FLAG)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    //calculate the size of the buffer to hold the hash object
    if (!NT_SUCCESS(status = BCryptGetProperty(
        hAlg,
        BCRYPT_OBJECT_LENGTH,
        (PBYTE)&cbHashObject,
        sizeof(DWORD),
        &cbData,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    //allocate the hash object on the heap
    pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
    if (NULL == pbHashObject)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    //calculate the length of the hash
    if (!NT_SUCCESS(status = BCryptGetProperty(
        hAlg,
        BCRYPT_HASH_LENGTH,
        (PBYTE)&cbHash,
        sizeof(DWORD),
        &cbData,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    //allocate the hash buffer on the heap
    pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash);
    if (NULL == pbHash)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    //create a hash
    if (!NT_SUCCESS(status = BCryptCreateHash(
        hAlg,
        &hHash,
        pbHashObject,
        cbHashObject,
        (PBYTE)key,
        sizeof(key)-1,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status);
        goto Cleanup;
    }

    //hash some data
    if (!NT_SUCCESS(status = BCryptHashData(
        hHash,
        (PBYTE)message,
        sizeof(message)-1,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status);
        goto Cleanup;
    }

    //close the hash
    if (!NT_SUCCESS(status = BCryptFinishHash(
        hHash,
        pbHash,
        cbHash,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status);
        goto Cleanup;
    }

    printf("The hash is:  ");
    for (DWORD i = 0; i < cbHash; i++)
    {
        printf("%2.2X-", pbHash[i]);
    }


Cleanup:

    if (hAlg)
    {
        BCryptCloseAlgorithmProvider(hAlg, 0);
    }

    if (hHash)
    {
        BCryptDestroyHash(hHash);
    }

    if (pbHashObject)
    {
        HeapFree(GetProcessHeap(), 0, pbHashObject);
    }

    if (pbHash)
    {
        HeapFree(GetProcessHeap(), 0, pbHash);
    }
};
Dan H
  • 706
  • 1
  • 6
  • 15
  • People may also find this [BCrypt NIST security policy document](https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp892.pdf) useful if using windows+bcrypt. – GilesDMiddleton Jun 01 '18 at 08:46
  • Great answer, and thanks for the complete code sample! There are two small bugs: (1) `sizeof(key)-1` should be `sizeof(key)`; (2) `sizeof(message)-1` should be `sizeof(message)` – Douglas Patriarche Jan 16 '19 at 23:05
  • @DouglasPatriarche No bugs, afaik: -1 to NOT pass the last byte, which is 0. – manuell Nov 18 '21 at 19:54
  • Ah, okay, the code is less general-purpose code, but a reasonable thing when the key and the message are known to be null-terminated strings. – Douglas Patriarche Nov 19 '21 at 20:37