【问题标题】:Is it possible to do a HMAC with wincrypt?可以用wincrypt做一个HMAC吗?
【发布时间】:2014-03-03 13:19:47
【问题描述】:

我一直在尝试使用 wincrypt/cryptoapi/Cryptography API: Next Generation (CNG) 执行直接的 SHA256 HMAC,我真的很挣扎。我的目标是 Windows 8。

我在任何地方都找不到正确的方法或找到任何示例。我希望在 C/C++ 中执行以下操作,这在下面的 C# 中进行了演示

        HMAC hashMaker = new HMACSHA256(Encoding.ASCII.GetBytes("SecretKey"));
        byte[] hash = hashMaker.ComputeHash(Encoding.ASCII.GetBytes("<SomeXmlData />"));
        string hashStr = BitConverter.ToString(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。

有没有人成功地使用 cryptoapi 执行直接的 HMAC?

【问题讨论】:

  • 有一个example of using SHA256,你需要做的就是用普通的SHA256算法替换为BCRYPT_SP800108_CTR_HMAC_ALGORITHM。您会注意到在示例中他们创建了一个算法和一个散列,您需要更改算法。此外,您不想要 XML 的 ASCII,某些字符可能会被替换。
  • 是的,我同意将 ASCII 用于 XML 是个坏主意。一些字符会被错误地编码。幸运的是,这只是一个示例内容,因为我知道转换为 c 会比 UFT16 更容易。

标签: c++ c winapi hmac cryptoapi


【解决方案1】:

感谢您提供信息 Mgetz。我从来不知道 BCrypt 方法集。 HMAC 比 wincrypt/cryptoapi 的 CryptHashData 容易得多。从使用hashing using SHA256 的示例中,我能够创建 HMAC 代码。您只需在 BCryptOpenAlgorithmProvider 的最后一个参数中添加 BCRYPT_ALG_HANDLE_HMAC_FLAG 并将密钥包含在对 BCryptCreateHash 的调用中。

这是完成的代码:

#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);
    }
};

【讨论】:

  • 如果使用 windows+bcrypt,人们也会发现这个BCrypt NIST security policy document 很有用。
  • 很好的答案,感谢您提供完整的代码示例!有两个小bug:(1)sizeof(key)-1应该是sizeof(key); (2)sizeof(message)-1应该是sizeof(message)
  • @DouglasPatriarche 没有错误,afaik:-1 表示不传递最后一个字节,即 0。
  • 啊,好吧,代码不是通用代码,但是当密钥和消息已知为空终止字符串时,这是合理的事情。
猜你喜欢
  • 2012-10-19
  • 1970-01-01
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-18
  • 2021-12-30
相关资源
最近更新 更多