问题
I'm using the code below with the Crypto API and I'm not getting the results I would expect based on testing with other API's and libraries.
I'm using the key, "key" and the data is "message"
For example, using Indy's TidHMACSHA1, I get 2088df74d5f2146b48146caf4965377e9d0be3a4
I get the same result using online generators (such as http://www.freeformatter.com/hmac-generator.html, for example).
With the code I've written (see below) I get 4a52c3c0abc0a06049d1ab648bb4057e3ff5f359
The code is below, I'm using the JEDI wcrypt2.pas header
function Hashhmacsha1(const Key, Value: AnsiString): AnsiString;
var
hCryptProvider: HCRYPTPROV;
hHash: HCRYPTHASH;
hKey: HCRYPTKEY;
bHash: array[0..$7F] of Byte;
dwHashLen: dWord;
i: Integer;
hHmacHash: HCRYPTHASH;
bHmacHash: array[0..$7F] of Byte;
dwHmacHashLen: dWord;
hmac_info : Wcrypt2.HMAC_INFO;
begin
dwHashLen := 32;
dwHmacHashLen := 32;
{get context for crypt default provider}
if CryptAcquireContext(@hCryptProvider, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT or CRYPT_MACHINE_KEYSET) then
begin
{create hash-object }
if CryptCreateHash(hCryptProvider, CALG_SHA1, 0, 0, @hHash) then
begin
{get hash from password}
if CryptHashData(hHash, @Key[1], Length(Key), 0) then
begin
// hHash is now a hash of the provided key, (SHA1)
// Now we derive a key for it
if CryptDeriveKey(hCryptProvider, CALG_RC4, hHash, 0, @hKey) then
begin
//hkey now holds our key. So we have do the whole thing over again
//ZeroMemory( hmac_info, SizeOf(hmac_info) );
hmac_info.HashAlgid := CALG_SHA1;
if CryptCreateHash(hCryptProvider, CALG_HMAC, hKey, 0, @hHmacHash) then
begin
{get hash from password}
if CryptSetHashParam( hHmacHash, HP_HMAC_INFO, @hmac_info, 0) then
begin
if CryptHashData(hHmacHash, @Value[1], Length(Value), 0) then
begin
if CryptGetHashParam(hHmacHash, HP_HASHVAL, @bHmacHash[0], @dwHmacHashLen, 0) then
begin
for i := 0 to dwHmacHashLen-1 do
Result := Result + IntToHex(bHmacHash[i], 2);
end
else
WriteLn( 'CryptGetHashParam ERROR --> ' + SysErrorMessage(GetLastError)) ;
end
else
WriteLn( 'CryptHashData ERROR --> ' + SysErrorMessage(GetLastError)) ;
{destroy hash-object}
CryptDestroyHash(hHmacHash);
CryptDestroyKey(hKey);
end
else
WriteLn( 'CryptSetHashParam ERROR --> ' + SysErrorMessage(GetLastError)) ;
end
else
WriteLn( 'CryptCreateHash ERROR --> ' + SysErrorMessage(GetLastError)) ;
end
else
WriteLn( 'CryptDeriveKey ERROR --> ' + SysErrorMessage(GetLastError)) ;
end;
{destroy hash-object}
CryptDestroyHash(hHash);
end;
{release the context for crypt default provider}
CryptReleaseContext(hCryptProvider, 0);
end;
Result := AnsiLowerCase(Result);
end;
I'm obviously doing something incorrectly, but I'm no idea what ??
回答1:
So I found a solution, which when generating an HMAC_SHA1 for the data "message" with the key "key" generates the expected hash of 2088df74d5f2146b48146caf4965377e9d0be3a4
As you can see, this code uses CryptImportKey
instead of CryptDeriveKey
, which seems to resolve the issue. It seems that using CryptDeriveKey is actually generating an HMAC_SHA1 hash using the data "message" and the SHA1 Hash of the key "key" encoded as RC4 instead of the plaintext key as initially thought.
The code works for keys upto 16 characters in length, any larger and only uses the first 16 characters anyway. I'm posting a second quesiton to enquire about that!!
Code is posted below.
function Hashhmacsha1(const Key, Value: AnsiString): AnsiString;
const
KEY_LEN_MAX = 16;
var
hCryptProvider: HCRYPTPROV;
hHash: HCRYPTHASH;
hKey: HCRYPTKEY;
bHash: array[0..$7F] of Byte;
dwHashLen: dWord;
i: Integer;
hPubKey : HCRYPTKey;
hHmacHash: HCRYPTHASH;
bHmacHash: array[0..$7F] of Byte;
dwHmacHashLen: dWord;
hmac_info : Wcrypt2.HMAC_INFO;
keyBlob: record
keyHeader: BLOBHEADER;
keySize: DWORD;
keyData: array[0..KEY_LEN_MAX-1] of Byte;
end;
keyLen : INTEGER;
begin
dwHashLen := 32;
dwHmacHashLen := 32;
{get context for crypt default provider}
if CryptAcquireContext(@hCryptProvider, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
begin
{create hash-object MD5}
if CryptCreateHash(hCryptProvider, CALG_SHA1, 0, 0, @hHash) then
begin
{get hash from password}
if CryptHashData(hHash, PByte(Key), Length(Key), 0) then
begin
// hHash is now a hash of the provided key, (SHA1)
// Now we derive a key for it
hPubKey := 0;
FillChar(keyBlob, SizeOf(keyBlob), 0);
keyBlob.keyHeader.bType := PLAINTEXTKEYBLOB;
keyBlob.keyHeader.bVersion := CUR_BLOB_VERSION;
keyBlob.keyHeader.aiKeyAlg := CALG_RC4;
KeyBlob.keySize := KEY_LEN_MAX;
if(Length(key) < (KEY_LEN_MAX))then
KeyLen := Length(key)
else
KeyLen := KEY_LEN_MAX;
Move(Key[1], KeyBlob.keyData[0], KeyLen );
if CryptImportKey(hCryptProvider, @keyBlob, SizeOf(KeyBlob), hPubKey, 0, @hKey) then
begin
//hkey now holds our key. So we have do the whole thing over again
ZeroMemory( @hmac_info, SizeOf(hmac_info) );
hmac_info.HashAlgid := CALG_SHA1;
if CryptCreateHash(hCryptProvider, CALG_HMAC, hKey, 0, @hHmacHash) then
begin
if CryptSetHashParam( hHmacHash, HP_HMAC_INFO, @hmac_info, 0) then
begin
if CryptHashData(hHmacHash, @Value[1], Length(Value), 0) then
begin
if CryptGetHashParam(hHmacHash, HP_HASHVAL, @bHmacHash[0], @dwHmacHashLen, 0) then
begin
for i := 0 to dwHmacHashLen-1 do
Result := Result + IntToHex(bHmacHash[i], 2);
end
else
WriteLn( 'CryptGetHashParam ERROR --> ' + SysErrorMessage(GetLastError)) ;
end
else
WriteLn( 'CryptHashData ERROR --> ' + SysErrorMessage(GetLastError)) ;
{destroy hash-object}
CryptDestroyHash(hHmacHash);
CryptDestroyKey(hKey);
end
else
WriteLn( 'CryptSetHashParam ERROR --> ' + SysErrorMessage(GetLastError)) ;
end
else
WriteLn( 'CryptCreateHash ERROR --> ' + SysErrorMessage(GetLastError)) ;
end
else
WriteLn( 'CryptDeriveKey ERROR --> ' + SysErrorMessage(GetLastError)) ;
end;
{destroy hash-object}
CryptDestroyHash(hHash);
end;
{release the context for crypt default provider}
CryptReleaseContext(hCryptProvider, 0);
end;
Result := AnsiLowerCase(Result);
end;
回答2:
I was able to get the correct result for keys longer than 16 bytes by using OpenSLL. Instead of about 10 Win32 Crypt calls, it was done in three: init, HMAC, and cleanup.
回答3:
I do not work with your functions, so correct me if I am wrong: I do not see where you compute HMACSHA1('message', 'key').
After CryptCreateHash(hCryptProvider, CALG_HMAC, hKey, 0, @hHmacHash)
I guess you are computing HMACSHA1('message', hkey), where the derived key hkey is somehow computed with RC4.
BTW: there is a misleading comment related to MD5 (artefact from old version?)
来源:https://stackoverflow.com/questions/25436416/cryptoapi-returns-incorrect-result-for-hmac-sha1