Tuesday, 14 July 2009

Calculando el Hash MD5 y SHA1

Últimamente si os fijáis cuando realizáis una descarga por internet, podréis ver que además os adjuntan un fichero md5 o sha1 para que comprobéis que realmente os habéis descargado lo que se supone que debería haber en ese fichero. Pues bien aquí os dejo una mini aplicación hecha por mi que os permitirá coger un fichero y generar su MD5 y SHA1 para que podáis comprobar que realmente coincide con el valor del hash del fichero. El código utilizado lo he sacado de ClubDelphi, donde encontrareis muchos ejemplos. El ejemplo de la mano de Domingo Seoane nos permite calcular el hash de un fichero, de un stream o de un texto. Podéis visitar su web en mi blog roll (la web de Seoane).
Para éste ejemplo, utilizaré una descarga típica de la web de eclipse, por ejemplo jetty (el cuál os hablaré en los próximos posts). Pues bien, podemos descargar éste componente desde la web de eclipse. Una vez en ésta página, podremos encontrar el fichero jetty-distribution-7.0.0.M4.zip, jetty-distribution-7.0.0.M4.zip.md5 y jetty-distribution-7.0.0.M4.zip.sha1 los cuales contienen la aplicación y los hashes MD5 y SHA1. Para el fichero descargado si abrimos los MD5 y SHA1, encontraremos los siguientes hashes:

MD5:
976bf42aa03a58accc042d2682e533da

SHA1:
92feb7386e486d2560663686449275588ddb056e

Pues bien, al descargar el fichero deberíamos comprobar que realmente el fichero descargado genera el mismo hash. Para esto utilizaré mi aplicación Thundax Calc Hash, que podréis descargar aquí.

Mediante la aplicación, podremos cargar un fichero y utilizando el algoritmo de Seoane generar los MD5 y SHA1:

Aquí os dejo el código fuente y su utilización:

unit hashes:




unit Hashes;

interface

uses
Windows, SysUtils, Classes;

type
THashAlgorithm = (haMD5, haSHA1);

function CalcHash(Stream: TStream; Algorithm: THashAlgorithm): string; overload;
function CalcHash(Archivo: string; Algorithm: THashAlgorithm): string; overload;
function CalcHash2(Str: string; Algorithm: THashAlgorithm): string;

implementation

type
HCRYPTPROV = ULONG;
PHCRYPTPROV = ^HCRYPTPROV;
HCRYPTKEY = ULONG;
PHCRYPTKEY = ^HCRYPTKEY;
HCRYPTHASH = ULONG;
PHCRYPTHASH = ^HCRYPTHASH;
LPAWSTR = PAnsiChar;
ALG_ID = ULONG;

const
CRYPT_NEWKEYSET = $00000008;
PROV_RSA_FULL = 1;
CALG_MD5 = $00008003;
CALG_SHA1 = $00008004;
HP_HASHVAL = $0002;

function CryptAcquireContext(phProv: PHCRYPTPROV;
pszContainer: LPAWSTR;
pszProvider: LPAWSTR;
dwProvType: DWORD;
dwFlags: DWORD): BOOL; stdcall;
external ADVAPI32 name 'CryptAcquireContextA';

function CryptCreateHash(hProv: HCRYPTPROV;
Algid: ALG_ID;
hKey: HCRYPTKEY;
dwFlags: DWORD;
phHash: PHCRYPTHASH): BOOL; stdcall;
external ADVAPI32 name 'CryptCreateHash';

function CryptHashData(hHash: HCRYPTHASH;
const pbData: PBYTE;
dwDataLen: DWORD;
dwFlags: DWORD): BOOL; stdcall;
external ADVAPI32 name 'CryptHashData';

function CryptGetHashParam(hHash: HCRYPTHASH;
dwParam: DWORD;
pbData: PBYTE;
pdwDataLen: PDWORD;
dwFlags: DWORD): BOOL; stdcall;
external ADVAPI32 name 'CryptGetHashParam';

function CryptDestroyHash(hHash: HCRYPTHASH): BOOL; stdcall;
external ADVAPI32 name 'CryptDestroyHash';

function CryptReleaseContext(hProv: HCRYPTPROV; dwFlags: DWORD): BOOL; stdcall;
external ADVAPI32 name 'CryptReleaseContext';

function CalcHash(Stream: TStream; Algorithm: THashAlgorithm): string; overload;
var
hProv: HCRYPTPROV;
hHash: HCRYPTHASH;
Buffer: PByte;
BytesRead: DWORD;
Algid: ALG_ID;
Data: array[1..20] of Byte;
DataLen: DWORD;
Success: BOOL;
i: integer;
begin
Result := EmptyStr;
Success := CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0);
if (not Success) then
if GetLastError() = DWORD(NTE_BAD_KEYSET) then
Success := CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL,
CRYPT_NEWKEYSET);
if Success then
begin
if Algorithm = haMD5 then
begin
Algid := CALG_MD5;
Datalen := 16
end
else
begin
Algid := CALG_SHA1;
Datalen := 20;
end;
if CryptCreateHash(hProv, Algid, 0, 0, @hHash) then
begin
GetMem(Buffer, 10 * 1024);
try
while TRUE do
begin
BytesRead := Stream.Read(Buffer^, 10 * 1024);
if (BytesRead = 0) then
begin
if (CryptGetHashParam(hHash, HP_HASHVAL, @Data, @DataLen, 0)) then
for i := 1 to DataLen do
Result := Result + LowerCase(IntToHex(Integer(Data[i]), 2));
break;
end;
if (not CryptHashData(hHash, Buffer, BytesRead, 0)) then
break;
end;
finally
FreeMem(Buffer);
end;
CryptDestroyHash(hHash);
end;
CryptReleaseContext(hProv, 0);
end;
end;

function CalcHash(Archivo: string; Algorithm: THashAlgorithm): string; overload;
var
Stream: TFileStream;
begin
Result := EmptyStr;
if FileExists(Archivo) then
try
Stream := TFileStream.Create(Archivo, fmOpenRead or fmShareDenyWrite);
try
Result := CalcHash(Stream, Algorithm);
finally
Stream.Free;
end;
except
end;
end;

function CalcHash2(Str: string; Algorithm: THashAlgorithm): string;
var
Stream: TStringStream;
begin
Result := EmptyStr;
Stream := TStringStream.Create(Str);
try
Result := CalcHash(Stream, Algorithm);
finally
Stream.Free;
end;
end;

end.




utilización:




procedure TForm1.Button1Click(Sender: TObject);
begin
if Edit1.text = '' then
Exit;
Memo1.Lines.clear;
Memo1.Lines.Add(CalcHash(Edit1.text, haMD5));
Memo2.Lines.clear;
Memo2.Lines.Add(CalcHash(Edit1.text, haSHA1));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
i: integer;
sPath: string;
begin
OpenDialog1.Execute(self.Handle);
for i := 0 to OpenDialog1.Files.Count - 1 do
sPath := OpenDialog1.Files.Strings[i];
if sPath <> '' then
Edit1.text := sPath;
end;




  • Enlaces de interés:
MD5 Hash.

0 comments:

Post a Comment