【发布时间】:2019-08-23 10:13:39
【问题描述】:
有人知道如何在 Delphi 2010+ 中使用 MS CryptoAPI 计算 HMAC-SHA512 哈希吗?
来自 MS 网站的示例,https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--creating-an-hmac 生成不正确的结果。
我发现这个答案https://stackoverflow.com/a/41387095/2111514 在某种程度上很有用(因为它是从https://en.wikipedia.org/wiki/HMAC 手动重写的),但它不在 Pascal 中,我尝试将它重构为 Pascal 没有运气。它有效,但仍然计算错误的结果。
有人可以帮帮我吗?
编辑::这是我遇到问题的代码:
uses
Windows,
JwaWinCrypt,
JwaWinError;
const
BLOCK_SIZE = 64;
type
EHMACError = class(Exception);
function WinError(const RetVal: BOOL; const FuncName: String): BOOL;
var
dwResult: Integer;
begin
Result:=RetVal;
if not RetVal then begin
dwResult:=GetLastError();
raise EHMACError.CreateFmt('Error [x%x]: %s failed.'#13#10'%s', [dwResult, FuncName, SysErrorMessage(dwResult)]);
end;
end;
function TBytesToHex(const Value: TBytes): String;
const
dictionary: Array[0..15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
var
i: Integer;
begin
Result:='';
for i:=0 to High(Value) do
Result:=Result + dictionary[Value[i] shr 4] + dictionary[Value[i] and $0F];
end;
function hmac(AKey, AMessage: TBytes; Algid: ALG_ID): TBytes;
function hash(const hProv: HCRYPTPROV; hData: TBytes): TBytes;
var
len, cb: DWORD;
hHash: HCRYPTHASH;
begin
SetLength(Result, 0);
WinError(CryptCreateHash(hProv, Algid, 0, 0, hHash), 'CryptCreateHash');
try
len:=Length(hData);
cb:=SizeOf(len);
WinError(CryptHashData(hHash, @hData[0], len, 0), 'CryptHashData');
WinError(CryptGetHashParam(hHash, HP_HASHSIZE, @len, cb, 0), 'CryptGetHashParam(HP_HASHSIZE)');
SetLength(Result, len);
WinError(CryptGetHashParam(hHash, HP_HASHVAL, @Result[0], len, 0), 'CryptGetHashParam(HP_HASHVAL)');
finally
WinError(CryptDestroyHash(hHash), 'CryptDestroyHash');
end;
end;
function double_hash(const hProv: HCRYPTPROV; hData1, hData2: TBytes): TBytes;
var
len, len1, len2, cb: DWORD;
hHash: HCRYPTHASH;
begin
SetLength(Result, 0);
WinError(CryptCreateHash(hProv, Algid, 0, 0, hHash), 'DH_CryptCreateHash');
try
len1:=Length(hData1);
len2:=Length(hData2);
cb:=SizeOf(DWORD);
WinError(CryptHashData(hHash, @hData1[0], len1, 0), 'DH_CryptHashData(hData1)');
WinError(CryptHashData(hHash, @hData2[0], len2, 0), 'DH_CryptHashData(hData1)');
WinError(CryptGetHashParam(hHash, HP_HASHSIZE, @len, cb, 0), 'DH_CryptGetHashParam(HP_HASHSIZE)');
SetLength(Result, len);
WinError(CryptGetHashParam(hHash, HP_HASHVAL, @Result[0], len, 0), 'DH_CryptGetHashParam(HP_HASHVAL)');
finally
WinError(CryptDestroyHash(hHash), 'DH_CryptDestroyHash');
end;
end;
var
hProv: HCRYPTPROV;
hHash: HCRYPTHASH;
i_key_pad, o_key_pad: TBytes;
data, ret: TBytes;
len, i: Integer;
c: Byte;
ifree: Boolean;
begin
ifree:=False;
SetLength(Result, 0);
SetLength(i_key_pad, BLOCK_SIZE);
SetLength(o_key_pad, BLOCK_SIZE);
WinError(CryptAcquireContext(hProv, Nil, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT), 'CryptAcquireContext');
try
data:=AKey;
len:=Length(data);
if len > BLOCK_SIZE then begin
data:=hash(hProv, data);
ifree:=True;
end;
//
i:=BLOCK_SIZE-1;
while i >= 0 do begin
if i < len then
c:=data[i]
else
c:=0;
i_key_pad[i]:=$36 xor c;
o_key_pad[i]:=$5c xor c;
Dec(i);
end;
data:=double_hash(hProv, i_key_pad, AMessage);
Result:=double_hash(hProv, o_key_pad, data);
SetLength(data, 0);
finally
if ifree then
SetLength(data, 0);
SetLength(i_key_pad, 0);
SetLength(o_key_pad, 0);
WinError(CryptReleaseContext(hProv, 0), 'CryptReleaseContext');
end;
end;
...它被调用:
Result:=hmac(Password, InString, CALG_SHA_512);
例子:
TBytesToHex(hmac('pass', 'test', CALG_SHA_512)); 产生(十六进制编码)
1319bb7baefc3fbaf07824261c240cecd04a54cd83cdf0deb68e56cadff20e7c644e2e956660ab9df47a19502173090df5ec3d0b9236d59917afc4f3607cf980
46beca277a5fec10beba65b0c2fb3917115f352eb8b2560e9ada0a3dbafb6c7a3fc456b1e13a07c4a9c856b633b70b2403907ca89894021772393e3f97e78684
对于相同的输入
【问题讨论】:
-
我非常怀疑 ms 示例会产生错误的结果。您是否偶然使用字符串进行哈希计算?
-
不,一切都转换为 ANSI,然后转换为 TBytes,与我使用在线生成器测试时相比,我的结果有所不同。而且我确信被散列的数据是单字节编码的,如果我的密码是“pass”并且我的散列消息是“test”,我有两个数组,每个数组有四个字符。在我的测试中 100% 没有 Unicode。
-
无论如何你必须展示你尝试过的东西,没有代码我们无法回答这个问题......
-
好的,编辑问题,附上代码,现在在线计算器用于 HMAC-SHA512 (freeformatter.com/hmac-generator.html) 输出
46beca277a5fec10beba65b0c2fb3917115f352eb8b2560e9ada0a3dbafb6c7a3fc456b1e13a07c4a9c856b633b70b2403907ca89894021772393e3f97e78684,我的代码生成1319bb7baefc3fbaf07824261c240cecd04a54cd83cdf0deb68e56cadff20e7c644e2e956660ab9df47a19502173090df5ec3d0b9236d59917afc4f3607cf980 -
请将该功能添加到您的问题中。无论如何,我看到您的代码存在多个问题。第一个也是最明显的一个是你的块大小对于 SHA512 是错误的,它应该是 128 字节(1024 位),而不是 64。
标签: delphi hash delphi-2010 hmac cryptoapi