Delphi & CryptoAPI - 如何计算 HMAC-SHA512 哈希值?
Delphi & CryptoAPI - how to calculate HMAC-SHA512 hash?
有人知道如何使用 MS CryptoAPI 在 Delphi 2010+ 中计算 HMAC-SHA512 哈希值吗?
来自 MS 网站的示例,https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--creating-an-hmac 生成了错误的结果。
我找到了这个答案 to be somehow usefull (because it is manual rewrite from 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 [=11=]F];
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]:= xor c;
o_key_pad[i]:=c 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
对于相同的输入
我的问题的完整解决方案,感谢@whosrdaddy 的帮助。
//
// HMAC-SHA512 - cryptoapi hash generation
//
// based on:
// https://en.wikipedia.org/wiki/HMAC
// https://github.com/ogay/hmac
//
// refactored from:
//
//
unit CryptoAPI_HMAC_SHA512;
interface
uses
SysUtils,
Classes;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: TBytes): TBytes; overload;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: String): String; overload;
implementation
uses
Windows,
JwaWinCrypt,
JwaWinError;
const
BLOCK_SIZE = 128; // bytes for SHA512
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 hmac(AKey, AMessage: TBytes; Algid: ALG_ID): TBytes;
function 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), 'CryptCreateHash');
try
len:=0;
len1:=Length(hData1);
len2:=Length(hData2);
cb:=SizeOf(DWORD);
WinError(CryptHashData(hHash, @hData1[0], len1, 0), 'CryptHashData(hData1)');
if len2 > 0 then
WinError(CryptHashData(hHash, @hData2[0], len2, 0), 'CryptHashData(hData1)');
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;
var
hProv: HCRYPTPROV;
i_key_pad, o_key_pad: TBytes;
data: TBytes;
emptyArray: TBytes;
len, i: Integer;
c: Byte;
ifree: Boolean;
begin
ifree:=False;
SetLength(Result, 0);
SetLength(emptyArray, 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, emptyArray);
len:=Length(data);
ifree:=True;
end;
//
i:=BLOCK_SIZE-1;
while i >= 0 do begin
c:=0;
if i < len then
c:=data[i];
i_key_pad[i]:= xor c;
o_key_pad[i]:=c xor c;
Dec(i);
end;
if ifree then
SetLength(data, 0);
data:=hash(hProv, i_key_pad, AMessage);
Result:=hash(hProv, o_key_pad, data);
SetLength(data, 0);
finally
SetLength(i_key_pad, 0);
SetLength(o_key_pad, 0);
WinError(CryptReleaseContext(hProv, 0), 'CryptReleaseContext');
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 [=10=]F];
end;
// source:
function MBCSString(const s: UnicodeString; CodePage: Word): RawByteString;
var
enc: TEncoding;
bytes: TBytes;
begin
enc:=TEncoding.GetEncoding(CodePage);
try
bytes:=enc.GetBytes(s);
SetLength(Result, Length(bytes));
Move(Pointer(bytes)^, Pointer(Result)^, Length(bytes));
SetCodePage(Result, CodePage, False);
finally
enc.Free;
end;
end;
function UnicodeStringToTBytes(const Value: String): TBytes;
var
ansi: AnsiString;
begin
ansi:=MBCSString(Value, 65001); // Unicode (UTF-8) codepage
Result:=BytesOf(ansi);
ansi:='';
end;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: TBytes): TBytes;
begin
SetLength(Result, 0);
if Length(Password) = 0 then
raise EHMACError.Create('Error: Password length must be greater then 0!');
Result:=hmac(Password, InString, CALG_SHA_512);
end;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: String): String;
var
input_bytes, input_password: TBytes;
begin
input_bytes:=UnicodeStringToTBytes(InString);
input_password:=UnicodeStringToTBytes(Password);
try
Result:=TBytesToHex(CryptoAPI_Hash_HmacSHA512(input_bytes, input_password));
finally
SetLength(input_password, 0);
SetLength(input_bytes, 0);
end;
end;
end.
有人知道如何使用 MS CryptoAPI 在 Delphi 2010+ 中计算 HMAC-SHA512 哈希值吗?
来自 MS 网站的示例,https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--creating-an-hmac 生成了错误的结果。
我找到了这个答案
有人可以帮我吗?
编辑:: 这是我遇到问题的代码:
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 [=11=]F];
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]:= xor c;
o_key_pad[i]:=c 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
对于相同的输入
我的问题的完整解决方案,感谢@whosrdaddy 的帮助。
//
// HMAC-SHA512 - cryptoapi hash generation
//
// based on:
// https://en.wikipedia.org/wiki/HMAC
// https://github.com/ogay/hmac
//
// refactored from:
//
//
unit CryptoAPI_HMAC_SHA512;
interface
uses
SysUtils,
Classes;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: TBytes): TBytes; overload;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: String): String; overload;
implementation
uses
Windows,
JwaWinCrypt,
JwaWinError;
const
BLOCK_SIZE = 128; // bytes for SHA512
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 hmac(AKey, AMessage: TBytes; Algid: ALG_ID): TBytes;
function 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), 'CryptCreateHash');
try
len:=0;
len1:=Length(hData1);
len2:=Length(hData2);
cb:=SizeOf(DWORD);
WinError(CryptHashData(hHash, @hData1[0], len1, 0), 'CryptHashData(hData1)');
if len2 > 0 then
WinError(CryptHashData(hHash, @hData2[0], len2, 0), 'CryptHashData(hData1)');
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;
var
hProv: HCRYPTPROV;
i_key_pad, o_key_pad: TBytes;
data: TBytes;
emptyArray: TBytes;
len, i: Integer;
c: Byte;
ifree: Boolean;
begin
ifree:=False;
SetLength(Result, 0);
SetLength(emptyArray, 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, emptyArray);
len:=Length(data);
ifree:=True;
end;
//
i:=BLOCK_SIZE-1;
while i >= 0 do begin
c:=0;
if i < len then
c:=data[i];
i_key_pad[i]:= xor c;
o_key_pad[i]:=c xor c;
Dec(i);
end;
if ifree then
SetLength(data, 0);
data:=hash(hProv, i_key_pad, AMessage);
Result:=hash(hProv, o_key_pad, data);
SetLength(data, 0);
finally
SetLength(i_key_pad, 0);
SetLength(o_key_pad, 0);
WinError(CryptReleaseContext(hProv, 0), 'CryptReleaseContext');
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 [=10=]F];
end;
// source:
function MBCSString(const s: UnicodeString; CodePage: Word): RawByteString;
var
enc: TEncoding;
bytes: TBytes;
begin
enc:=TEncoding.GetEncoding(CodePage);
try
bytes:=enc.GetBytes(s);
SetLength(Result, Length(bytes));
Move(Pointer(bytes)^, Pointer(Result)^, Length(bytes));
SetCodePage(Result, CodePage, False);
finally
enc.Free;
end;
end;
function UnicodeStringToTBytes(const Value: String): TBytes;
var
ansi: AnsiString;
begin
ansi:=MBCSString(Value, 65001); // Unicode (UTF-8) codepage
Result:=BytesOf(ansi);
ansi:='';
end;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: TBytes): TBytes;
begin
SetLength(Result, 0);
if Length(Password) = 0 then
raise EHMACError.Create('Error: Password length must be greater then 0!');
Result:=hmac(Password, InString, CALG_SHA_512);
end;
function CryptoAPI_Hash_HmacSHA512(const InString, Password: String): String;
var
input_bytes, input_password: TBytes;
begin
input_bytes:=UnicodeStringToTBytes(InString);
input_password:=UnicodeStringToTBytes(Password);
try
Result:=TBytesToHex(CryptoAPI_Hash_HmacSHA512(input_bytes, input_password));
finally
SetLength(input_password, 0);
SetLength(input_bytes, 0);
end;
end;
end.