Saturday, 13 June 2009

Delphi CryptoAPI

CryptoAPI es una Cryptographic API interface unit de Borland Delphi. Partes de la misma fueron creadas por Microsoft, y la creación y traducción para Pascal fue de Massimo Maria Ghisalberti. Podemos encontrar la última versión del fichero en Project JEDI (Joint Endeavour of Delphi Innovators) en http://delphi-jedi.org, y está sujeta a una licencia : Mozilla Public License Version 1.1. El fichero es Wcrypt2.pas y lo podemos encontrar en diferentes ubicaciones por la red de libre descarga. Aquí os dejo un enlace para su descarga : Wcrypt2 o CryptoAPI2.zip. Mediante esta librería, podemos generar diferentes hashes (MD2, MD4, MD5, SHA, etc), utilizar diferentes encriptaciones (RC4, DES, RC5, HMAC, etc) sobre texto o ficheros. Aquí os dejo el código fuente para la implementación de una pequeña aplicación para calcular el MD5 de un texto, y encriptar y desencriptar un fichero de texto utilizando un Hash SHA y una encriptación con RC4.:

El código fuente para la generación de la aplicación es el siguiente:




unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Wcrypt2, StdCtrls, hashes;

type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
CheckBox1: TCheckBox;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

procedure
CryptFile(const SourceFileName, DestinationFileName, Password: string; ToCrypt: Boolean);
var
hProv: HCRYPTPROV;
hash: HCRYPTHASH;
key: HCRYPTKEY;

Buffer: PByte;
len: dWord;
fsIn, fsOut: TFileStream;
IsEndOfFile: Boolean;
begin
{get context for crypt default provider}
CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
{create hash-object (SHA algorithm)}
CryptCreateHash(hProv, CALG_SHA, 0, 0, @hash);
{get hash from password}
CryptHashData(hash, @Password[1], Length(Password), 0);
{create key from hash by RC4 algorithm}
CryptDeriveKey(hProv, CALG_RC4, hash, 0, @key);
{destroy hash-object}
CryptDestroyHash(hash);

{open source+destination files}
fsIn := TFileStream.Create(SourceFileName, fmOpenRead or fmShareDenyWrite);
fsOut := TFileStream.Create(DestinationFileName, fmCreate);
try
{allocate buffer to read content from source file}
GetMem(Buffer, 512);

repeat
IsEndOfFile := (fsIn.Position >= fsIn.Size);
if IsEndOfFile then break;

{read content from source file}
len := fsIn.Read(Buffer^, 512);

if ToCrypt then
{crypt buffer}
CryptEncrypt(key, 0, IsEndOfFile, 0, Buffer, @len, len)
else
{decrypt buffer}
CryptDecrypt(key, 0, IsEndOfFile, 0, Buffer, @len);

{write changed buffer to destination file}
fsOut.Write(Buffer^, len)
until IsEndOfFile;

{release memory allocated for buffer}
FreeMem(Buffer, 512);
finally
fsIn.Free;
fsOut.Free;
end;

{release the context for crypt default provider}
CryptReleaseContext(hProv, 0);
end;

function MD5(const Value: string): string;
var
hCryptProvider: HCRYPTPROV;
hHash: HCRYPTHASH;
bHash: array[0..$7F] of Byte;
dwHashLen: dWord;
i: Integer;
begin
dwHashLen := 16;
if (Value = '') then
begin
Result := 'd41d8cd98f00b204e9800998ecf8427e';
exit;
end
else
Result := '';

{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 MD5}
if CryptCreateHash(hCryptProvider, CALG_MD5, 0, 0, @hHash) then
begin
{get hash from password}
if CryptHashData(hHash, @Value[1], Length(Value), 0) then
begin
if CryptGetHashParam(hHash, HP_HASHVAL, @bHash[0], @dwHashLen, 0) then
begin
for i := 0 to dwHashLen-1 do
Result := Result + IntToHex(bHash[i], 2);
end;
end;
{destroy hash-object}
CryptDestroyHash(hHash);
end;
{release the context for crypt default provider}
CryptReleaseContext(hCryptProvider, 0);
end;

Result := AnsiLowerCase(Result);
end;


{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
Edit2.text := MD5(Edit1.Text);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
CryptFile(Edit3.text, Edit4.text, Edit5.text, CheckBox1.Checked);
end;

end.





Parte de éste está disponible en Scalabium Software, tips 172 y 173.

Las pruebas realizadas muestran que el cálculo del MD5 no es del todo válido. Si lo comprobamos con los diversas aplicaciones On-line del MD5, podemos obtener el hash para el texto 'esto es una prueba' = 254d4a6b00b78b94f846b85528298d43, y no 60bb1c95ab70cc37d452ee1414dd2656 que es lo que aparece en nuestra aplicación. Éste cálculo, lo podemos encontrar bien hecho en la web de Seoane, donde nos muestra un ejemplo con la unit Hashes.pas. Aquí os dejo un enlace para el MD5 On-line.

  • Enlaces de interés:
http://www.lincoln.edu/math/rmyrick/ComputerNetworks/InetReference/143.htm
Cryptography and Multiple-Precision Arithmetic

0 comments:

Post a Comment