Mega Code Archive

 
Categories / Delphi / Files
 

Use the Windows Crypto API to get a Hash of a file

Title: Use the Windows Crypto API to get a Hash of a file. This FAQ is as the topic describes. You can use this code to get a MD5, SHA1, or a number of other things. WINCRYPT.H The major problem in using the crypto API is that Delphi doesn't seem to have it defined (Delphi 3 and Turbo Delphi 2006). To that end, I translated what was necessary for this task. CODE unit wincrypt; // stripped down version of WINCRYPT.H used for procedure calls in main program // all that is intended is to be able to support file hashing, though wincrypt // supports encryption and key exchange. interface uses windows; const advapi = 'ADVAPI32.DLL'; PROV_RSA_FULL = 1; CRYPT_VERIFYCONTEXT = $F0000000; // algo class ids ALG_CLASS_HASH = 4 shl 13; ALG_TYPE_ANY = 0; // Hash sub ids ALG_SID_MD2 = 1; ALG_SID_MD4 = 2; ALG_SID_MD5 = 3; ALG_SID_SHA = 4; ALG_SID_SHA1 = 4; ALG_SID_MAC = 5; ALG_SID_SSL3SHAMD5 = 8; ALG_SID_HMAC = 9; ALG_SID_TLS1PRF = 10; ALG_SID_HASH_REPLACE_OWF = 11; // algorithm identifier definitions CALG_MD2 = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_MD2; CALG_MD4 = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_MD4; CALG_MD5 = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_MD5; CALG_SHA = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_SHA; CALG_SHA1 = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_SHA1; CALG_MAC = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_MAC; CALG_SSL3_SHAMD5 = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_SSL3SHAMD5; CALG_HMAC = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_HMAC; CALG_TLS1PRF = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_TLS1PRF; CALG_HASH_REPLACE_OWF = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_HASH_REPLACE_OWF; // gethashparam tags HP_ALGID = $0001; HP_HASHVAL = $0002; HP_HASHSIZE = $0004; HP_HMAC_INFO = $0005; HP_TLS1PRF_LABEL = $0006; HP_TLS1PRF_SEED = $0007; type TCryptProv = THandle; TAlgID = integer; TCryptKey = pointer; TCryptHash = pointer; function CryptAcquireContext(var phProv: TCryptProv; szContainer: PAnsiChar; szProvider: PAnsiChar; dwProvType: DWord; dwFlags: DWord): boolean; stdcall; function CryptAcquireContextA(phProv: TCryptProv; szContainer: PAnsiChar; szProvider: PAnsiChar; dwProvType: DWord; dwFlags: DWord): boolean; stdcall; function CryptAcquireContextW(phProv: TCryptProv; szContainer: PWideChar; szProvider: PWideChar; dwProvType: DWord; dwFlags: DWord): boolean; stdcall; function CryptCreateHash(phProv: TCryptProv; Algid: TAlgID; hKey: TCryptKey; dwFlags: DWord; var phHash: TCryptHash): boolean; stdcall; function CryptHashData(phHash: TCryptHash; pbData: pointer; dwDataLen: DWord; dwFlags: DWord): boolean; stdcall; function CryptGetHashParam(phHash: TCryptHash; dwParam: Dword; pbdata: Pointer; var dwDataLen: DWord; dwFlags: DWord): Boolean; stdcall; function CryptDestroyHash(phHash: TCryptHash): Boolean; stdcall; function CryptReleaseContext(phProv: TCryptProv; dwFlags: DWord): boolean; stdcall; implementation function CryptAcquireContext; external advapi name 'CryptAcquireContextA'; function CryptAcquireContextA; external advapi name 'CryptAcquireContextA'; function CryptAcquireContextW; external advapi name 'CryptAcquireContextW'; function CryptCreateHash; external advapi name 'CryptCreateHash'; function CryptHashData; external advapi name 'CryptHashData'; function CryptGetHashParam; external advapi name 'CryptGetHashParam'; function CryptDestroyHash; external advapi name 'CryptDestroyHash'; function CryptReleaseContext; external advapi name 'CryptReleaseContext'; end. Wincrypt.H has more functionality than this, so if you are interested in the encryption functions, key functions, and other things defined within the file, consult the Windows SDK. Example: Get an SHA-1 Hash of a file CODE procedure TForm1.Button1Click(Sender: TObject); var infile: THandle; inbuffer: array[1..32767] of byte; outhash: array[1..20] of byte; amount_read: integer; cbHashDataLen: Integer; hProv: TCryptProv; hHash: TCryptHash; i: integer; outstr: string; begin if OpenDialog1.Execute then begin infile := CreateFile(PChar(OpenDialog1.FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0); try hHash := nil; hProv := 0; try if not CryptAcquireContext(hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then ShowMessage('1: ' + SysErrorMessage(GetLastError)); if not CryptCreateHash(hProv, CALG_SHA1, nil, 0, hHash) then ShowMessage('2: ' + SysErrorMessage(GetLastError)); ReadFile(infile, inbuffer, sizeof(inbuffer), amount_read, nil); repeat if not CryptHashData(hHash, @inbuffer, amount_read, 0) then ShowMessage('3: ' + SysErrorMessage(GetLastError)); ReadFile(infile, inbuffer, sizeof(inbuffer), amount_read, nil); until amount_read = 0; cbHashDataLen := 20; if not CryptGetHashParam(hHash, HP_HASHVAL, @outHash, cbHashDataLen, 0) then ShowMessage('4: ' + SysErrorMessage(GetLastError)); finally if not CryptDestroyHash(hhash) then ShowMessage('5: ' + SysErrorMessage(GetLastError)); if not CryptReleaseContext(hprov, 0) then ShowMessage('6: ' + SysErrorMessage(GetLastError)); end; finally CloseHandle(infile); end; outstr := ''; for i := 1 to cbHashDataLen do outstr := outstr + IntToHex(outHash[i], 2); ShowMessage('SHA1 hash is: ' + outstr); end; Notes: 1) In CryptCreateHash, the desired algorithm is specified. There are constants in Wincrypt which will denote what is available. 2) Take special note of cbHashDataLen. Here you must specify the proper hash key length for the desired algorithm, and this must be correct for the call to be successful.