Files Delphi

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.