我正在尝试与一个API进行交互,该API接受GET和POST请求,其中包括从消息和我的私钥派生的签名,使用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/ - 输入上面的键作为输入类型= Base-64,上面的消息字符串作为输入类型= TEXT,输出类型= 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();
};
答案 0 :(得分:0)
您需要使用指定密钥的CONST BYTE key[] = { 0x6C, ... };
方法。键和输入数据是二进制数据,在大多数编程语言中表示为字节数组。十六进制和基数64是表示或编码二进制值的两种方式,因此它们可以用作可打印文本。
但是,如果使用字节数组,则sizeof
将返回实际字节数。数组不会终止,因此没有理由删除最后的空字节。因此sizeof(key) - 1
将删除键的最后一个字节作为输入参数,而不是删除不存在的空字节。
消息字符串 以null结尾,因此它应该可以正常工作。但是,您可能明确要提及消息需要是US-ASCII或显式编码字符串(例如,UTF-8)。