Delphi 德尔菲公司;CryptoAPI-如何计算HMAC-SHA512哈希?
有人知道如何使用MS CryptoAPI在Delphi 2010+中计算HMAC-SHA512哈希吗 来自MS网站的示例生成了不正确的结果 我发现这个答案在某种程度上是有用的(因为它是从手动重写的),但它不是在Pascal中,我试图将它重构为Pascal是没有运气的。它可以工作,但仍然计算错误的结果 有人能帮我吗 编辑::这是我遇到问题的代码:Delphi 德尔菲公司;CryptoAPI-如何计算HMAC-SHA512哈希?,delphi,hash,delphi-2010,hmac,cryptoapi,Delphi,Hash,Delphi 2010,Hmac,Cryptoapi,有人知道如何使用MS CryptoAPI在Delphi 2010+中计算HMAC-SHA512哈希吗 来自MS网站的示例生成了不正确的结果 我发现这个答案在某种程度上是有用的(因为它是从手动重写的),但它不是在Pascal中,我试图将它重构为Pascal是没有运气的。它可以工作,但仍然计算错误的结果 有人能帮我吗 编辑::这是我遇到问题的代码: uses Windows, JwaWinCrypt, JwaWinError; const BLOCK_SIZE = 64; typ
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;
例如:
tbytestochex(hmac('pass','test',CALG_SHA_512))代码>生成(十六进制编码)
1319BB7BAEFC3FBAF07824261C240CECD04A54CD83CDF0DEB68E56CADF20E7C644E2E956660AB9DF47A1950217390DF5EC3D0B9236D59917AFC4F3607CF980
鉴于产生
46BECA277A5FEC10BEBA65B0C2FB3917115F352EB8B2560E9ADA0A3DBAFB6C7A3FC456B113A07C4A9C856B633B70B2403907CA89402772393E3F97E78684
对于相同的输入我的问题的完整工作解决方案,感谢@whosrdaddy的帮助
//
// HMAC-SHA512 - cryptoapi hash generation
//
// based on:
// https://en.wikipedia.org/wiki/HMAC
// https://github.com/ogay/hmac
//
// refactored from:
// https://stackoverflow.com/questions/41384395/wrong-result-for-base64-string-of-hmac-sha1-using-crypto-api/41387095#41387095
//
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]:=$36 xor c;
o_key_pad[i]:=$5c 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 $0F];
end;
// source: https://stackoverflow.com/a/26892830/2111514
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.
//
//HMAC-SHA512-加密API哈希生成
//
//基于:
// https://en.wikipedia.org/wiki/HMAC
// https://github.com/ogay/hmac
//
//重构自:
// https://stackoverflow.com/questions/41384395/wrong-result-for-base64-string-of-hmac-sha1-using-crypto-api/41387095#41387095
//
单位CryptoAPI_HMAC_SHA512;
接口
使用
SysUtils,
班级;
函数CryptoAPI_Hash_HmacSHA512(常量指令,密码:TBytes):TBytes;超载;
函数CryptoAPI_Hash_HmacSHA512(常量指令,密码:字符串):字符串;超载;
实施
使用
窗户,
JwaWinCrypt,
JwaWinError;
常数
块大小=128;//SHA512的字节数
类型
EHMAError=类(异常);
函数WinError(const RetVal:BOOL;const FuncName:String):BOOL;
变量
dwResult:整数;
开始
结果:=RetVal;
如果没有返回,则开始
dwResult:=GetLastError();
raise EHMAError.CreateFmt('Error[x%x]:%s失败。“#13#10'%s',[dwResult,FuncName,SysErrorMessage(dwResult)];
结束;
结束;
函数hmac(AKey,AMessage:TBytes;Algid:ALG_ID):TBytes;
函数散列(const hProv:HCRYPTPROV;hData1、hData2:TBytes):TBytes;
变量
蓝,蓝1,蓝2,cb:DWORD;
hHash:HCRYPTHASH;
开始
SetLength(结果为0);
WinError(CryptCreateHash(hProv,Algid,0,0,hHash),“CryptCreateHash”);
尝试
len:=0;
len1:=长度(hData1);
len2:=长度(hData2);
cb:=SizeOf(DWORD);
WinError(CryptHashData(hHash,@hData1[0],len1,0),“CryptHashData(hData1)”;
如果len2>0,则
WinError(CryptHashData(hHash,@hData2[0],len2,0),“CryptHashData(hData1)”;
WinError(CryptGetHashParam(hHash,HP_HASHSIZE,@len,cb,0),“CryptGetHashParam(HP_HASHSIZE)”;
设置长度(结果,len);
WinError(CryptGetHashParam(hHash,HP_HASHVAL,@Result[0],len,0),“CryptGetHashParam(HP_HASHVAL)”;
最后
WinError(CryptDestroyHash(hHash),“CryptDestroyHash”);
结束;
结束;
变量
hProv:HCRYPTPROV;
i_键盘,o_键盘:t字节;
数据:t字节;
空数组:t字节;
len,i:整数;
c:字节;
ifree:布尔型;
开始
ifree:=假;
SetLength(结果为0);
设置长度(空数组,0);
设置长度(键盘、块大小);
设置长度(键盘、块大小);
WinError(CryptAcquireContext(hProv,Nil,MS_ENH_RSA_AES_PROV,PROV_RSA_AES,CRYPT_VERIFYCONTEXT),“CryptAcquireContext”);
尝试
数据:=AKey;
len:=长度(数据);
如果len>块大小,则开始
数据:=散列(hProv、数据、空数组);
len:=长度(数据);
ifree:=真;
结束;
//
i:=块_尺寸-1;
当i>=0开始时
c:=0;
如果我是莱恩的话
c:=数据[i];
i_key_pad[i]:=$36或c;
o_key_pad[i]:=$5c或xoc;
十二月一日;
结束;
如果我是自由的话
设置长度(数据,0);
数据:=散列(hProv、键盘、消息);
结果:=散列(hProv、o_键盘、数据);
设置长度(数据,0);
最后
设置长度(i_键盘,0);
设置长度(o_键盘,0);
WinError(CryptReleaseContext(hProv,0),'CryptReleaseContext');
结束;
结束;
函数tbytestochex(常量值:TBytes):字符串;
常数
字典:Char=('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f')的数组[0..15];
变量
i:整数;
开始
结果:='';
对于i:=0到高(值)do
结果:=结果+字典[Value[i]shr 4]+字典[Value[i]和$0F];
结束;
//资料来源:https://stackoverflow.com/a/26892830/2111514
函数MBCSString(const s:UnicodeString;CodePage:Word):RawByteString;
变量
enc:TEncoding;
字节:TBytes;
开始
enc:=TEncoding.GetEncoding(代码页);
尝试
字节:=enc.GetBytes(s);
SetLength(结果,长度(字节));
移动(指针(字节)^,指针(结果)^,长度(字节));
SetCodePage(结果、代码页、错误);
最后
免费附件;
结束;
结束;
函数UnicodestingToBytes(常量值:字符串):TBytes;
变量
ansi:AnsiString;
开始
ansi:=MBCSString(值65001);//Unicode(UTF-8)代码页
结果:=字节数(ansi);
ansi:='';
结束;
函数CryptoAPI_Hash_HmacSHA512(常量指令,密码:TBytes):TBytes;
开始
SetLength(结果为0);
如果长度(密码)=0,则
raise EHMASERROR.Create('错误:密码长度必须大于0!');
结果:=hmac(密码、指示、计算值512);
结束;
函数CryptoAPI_Hash_HmacSHA512(常量指令,密码:字符串):字符串;
变量
输入字节,输入密码:t字节;
开始
输入字节:=Unicodesringtotbytes(InString);
输入_密码:=UnicodestingToBytes(密码);
尝试
结果:=TBytesToHex(CryptoAPI_哈希_HmacSHA512(输入字节,输入密码));
最后
设置长度(输入密码,0);
SetLength(输入_字节,0);
结束;
结束;
结束。
我非常怀疑ms示例是否产生了错误的结果。您是否偶然使用字符串进行散列计算?不,所有内容都转换为ANSI,然后再转换为TBytes,与使用在线生成器测试时相比,结果有所不同。我肯定正在散列的数据
//
// HMAC-SHA512 - cryptoapi hash generation
//
// based on:
// https://en.wikipedia.org/wiki/HMAC
// https://github.com/ogay/hmac
//
// refactored from:
// https://stackoverflow.com/questions/41384395/wrong-result-for-base64-string-of-hmac-sha1-using-crypto-api/41387095#41387095
//
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]:=$36 xor c;
o_key_pad[i]:=$5c 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 $0F];
end;
// source: https://stackoverflow.com/a/26892830/2111514
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.