我怎样才能让 BCrypt 将我的密钥解释为十六进制?

How can I get BCrypt to interpret my key as hex?

我正在尝试与接受 GET 和 POST 请求的 API 交互,这些请求包括从消息派生的签名和我的私钥,使用 HMAC SHA512 进行哈希处理。文档给出了一个例子:

密钥(base-64):

bEDtDJnW0y/Ll4YZitxb+D5sTNnEpQKH67EJRCmQCqN9cvGiB8+IHzB7HjsOs3mSlxLmu4aiPDRpe9anuWzylw==

留言:

/account/balance1515058794242

应生成以下 (base-64) 签名:

NjqZ8Mgdkj6hrtY/xdKBy1S0kLjU2tA7G+pR2TdOBF45b7+evfpzGH/C/PiNHEDvuiRChRBlRo3AGJ7Gcvlwqw==

文档还指出在 hmac 中使用之前需要从 base-64 解码密钥,但不清楚它应该采用什么格式。

我一直在玩在线 hmac 生成器,可以毫无问题地复制示例签名。例如在https://www.liavaag.org/English/SHA-Generator/HMAC/——输入上面的key作为input type=Base-64,上面的message string作为input type=TEXT,output type=base-64,输出签名同上。当我将密钥作为 HEX 类型并使用等效的十六进制时,它也能正常工作:

6C40ED0C99D6D32FCB9786198ADC5BF83E6C4CD9C4A50287EBB1094429900AA37D72F1A207CF881F307B1E3B0EB379929712E6BB86A23C34697BD6A7B96CF297

但是我无法使用我自己的程序使用 BCrypt 复制示例签名。 BCrypt hmac 似乎将我的密钥解释为 'TEXT' 类型的输入,其方式与在线生成器相同。也就是说,当我将密钥作为十六进制字符串时:

CONST BYTE key[] = { "6C40ED0C99D6D32FCB9786198ADC5BF83E6C4CD9C4A50287EBB1094429900AA37D72F1A207CF881F307B1E3B0EB379929712E6BB86A23C34697BD6A7B96CF297" };

我得到一个输出签名(十六进制):

16ab16ed3874fab51dbda66155edf269883d128de6067d77762dcee4129f1612b36fc556df10beb358c81262d034efe4c50d68d89ac43606df4318a8af56b

在在线生成器上,当我使用该十六进制字符串 (6C40...) 作为 TEXT 类型键并将输出作为 HEX 时,我得到相同的输出。

有什么方法可以强制 BCrypt 将我的密钥解释为十六进制?我什至尝试将密钥声明为十六进制文字,即:

CONST BYTE key[] = { 0x6C, 0x40, 0xed, 0x0c, 0x99, 0xd6, 0xd3, 0x2f, 
                         0xCB, 0x97, 0x86, 0x19, 0x8a, 0xdc, 0x5b, 0xf8,
                         0x3E, 0x6c, 0x4c, 0xd9, 0xc4, 0xa5, 0x02, 0x87,
                         0xeb, 0xb1, 0x09, 0x44, 0x29, 0x90, 0x0a, 0xa3,
                         0x7d, 0x72, 0xf1, 0xa2, 0x07, 0xcf, 0x88, 0x1f,
                         0x30, 0x7b, 0x1e, 0x3b, 0x0e, 0xb3, 0x79, 0x92,
                         0x97, 0x12, 0xe6, 0xbb, 0x86, 0xa2, 0x3c, 0x34,
                         0x69, 0x7b, 0xd6, 0xa7, 0xb9, 0x6c, 0xf2, 0x97 };

但这给出了另一个不同的签名。至少十六进制字符串密钥有点像复制在线转换器。对于我为什么使用十六进制而不是 base-64 的任何混淆,我深表歉意,这是我最终需要使用的 - 目前这只是一个额外的复杂步骤,所以现在我只是想得到十六进制等效工作,然后我可以专注于将其编码为 base-64。我试图获得的 base-64 签名的十六进制等价物是:

363a99f0c81d923ea1aed63fc5d281cb54b490b8d4dad03b1bea51d9374e045e396fbf9ebdfa73187fc2fcf88d1c40efba2442851065468dc0189ec672f970ab

我使用的完整代码如下:

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

#include <fstream>

int hexToInt(char ch)
{
    return 0;
}


void __cdecl wmain(
    int                      argc,
    __in_ecount(argc) LPWSTR *wargv)
{
    std::wofstream fout;
    fout.open("signature.txt");


    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 message[] = { "/account/balance1515058794242" };
    CONST BYTE key[] = { "6C40ED0C99D6D32FCB9786198ADC5BF83E6C4CD9C4A50287EBB1094429900AA37D72F1A207CF881F307B1E3B0EB379929712E6BB86A23C34697BD6A7B96CF297" };




    //open an algorithm handle
    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
        &hAlg,
        BCRYPT_SHA512_ALGORITHM,
        NULL,
        BCRYPT_ALG_HANDLE_HMAC_FLAG)))
    {
        fout << "**** 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)))
    {
        fout << "**** 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)
    {
        fout << "**** 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)))
    {
        fout << "**** 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)
    {
        fout << "**** memory allocation failed\n";
        goto Cleanup;
    }

    //create a hash
    if (!NT_SUCCESS(status = BCryptCreateHash(
        hAlg,
        &hHash,
        pbHashObject,
        cbHashObject,
        (PBYTE)key,
        sizeof(key) - 1,
        0)))
    {
        fout << "**** 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)))
    {
        fout << "**** Error 0x%x returned by BCryptHashData\n" << status;
        goto Cleanup;
    }

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


    fout << "\nThe hash is:  \n";
    for (DWORD i = 0; i < cbHash; i++)
    {
        fout <<  std::hex << pbHash[i];
    }



Cleanup:

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

    if (hHash)
    {
        BCryptDestroyHash(hHash);
    }

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

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


    fout.close();
};

您需要使用CONST BYTE key[] = { 0x6C, ... };方法来指定您的密钥。键和输入数据是二进制数据,在大多数编程语言中表示为字节数组。十六进制和 base 64 是 表示 编码 二进制值的两种方式,以便它们可以用作可打印文本。

但是,如果您使用字节数组,那么 sizeof 将 return 实际字节数。数组不是空终止的,因此没有理由删除最后的空字节。所以 sizeof(key) - 1 将删除键的最后一个字节作为输入参数,而不是删除 non-existent 空字节。

消息字符串空终止的,所以它应该能正常工作。但是,您可能想要明确提及消息需要 US-ASCII 或明确编码字符串(例如 UTF-8)。