SHA1 hashing in Delphi XE

I am implementing XML digital signatures. I start with small steps, so right now I want to solve the SHA-1 hash problem.

There are many questions in SO:

... and probably more. However, I am using Delphi XE. So far I have tried LockBox 2 (both versions of Songbeamer and Sourceforge), Lock Box 3, DCPCrypto2 and some others ( Hashes are an easy-to-use unit that uses Windows cryptographic functions)

I prepared a small test setup that gives me the following:

Lockbox2

FAILED: 1 ('abc') Got: '9f04f41a848514162050e3d68c1a7abb441dc2b5' Expected: 'a9993e364706816aba3e25717850c26c9cd0d89d' FAILED: 2 ('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') Got: '51d7d8769ac72c409c5b0e3f69c60adc9a039014' Expected: '84983e441c3bd26ebaae4aa1f95129e5e54670f1' 

Lockbox3

 FAILED: 1 ('abc') Got: '9f04f41a848514162050e3d68c1a7abb441dc2b5' Expected: 'a9993e364706816aba3e25717850c26c9cd0d89d' FAILED: 2 ('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') Got: '51d7d8769ac72c409c5b0e3f69c60adc9a039014' Expected: '84983e441c3bd26ebaae4aa1f95129e5e54670f1' 

DCPCrypto2

 FAILED: 1 ('abc') Got: '9f04f41a848514162050e3d68c1a7abb441dc2b5' Expected: 'a9993e364706816aba3e25717850c26c9cd0d89d' FAILED: 2 ('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') Got: '51d7d8769ac72c409c5b0e3f69c60adc9a039014' Expected: '84983e441c3bd26ebaae4aa1f95129e5e54670f1' 

Hash

 Test 1 passes Test 2 passes 

Did you manage to compile the specified libraries in Delphi XE and give them the appropriate values? I am particularly interested in the DCPCrypt2 SelfTest procedure.

Change I added this answer with fixed source code. Thanks to everyone for your help, this is much appreciated.

+7
source share
4 answers

Leonardo, I think your problem is UNICODE , when you use the hash a string function, you pass an array (buffer) of bytes. therefore, when you pass the string abc to Delphi XE, you haveh this buffer 61 00 62 00 63 00 (Hex view)

check out this Jwscl library application that uses Windows cryptographic features from the Jwscl library (JEDI Windows Security Code Lib)

 program Jwscl_TestHash; {$APPTYPE CONSOLE} uses JwsclTypes, JwsclCryptProvider, Classes, SysUtils; function GetHashString(Algorithm: TJwHashAlgorithm; Buffer : Pointer;Size:Integer) : AnsiString; var Hash: TJwHash; HashSize: Cardinal; HashData: Pointer; i : Integer; begin Hash := TJwHash.Create(Algorithm); try Hash.HashData(Buffer,Size); HashData := Hash.RetrieveHash(HashSize); try SetLength(Result,HashSize*2); BinToHex(PAnsiChar(HashData),PAnsiChar(Result),HashSize); finally TJwHash.FreeBuffer(HashData); end; finally Hash.Free; end; end; function GetHashSHA(FBuffer : AnsiString): AnsiString; begin Result:=GetHashString(haSHA,@FBuffer[1],Length(FBuffer)); end; function GetHashSHA_Unicode(FBuffer : String): String; begin Result:=GetHashString(haSHA,@FBuffer[1],Length(FBuffer)*SizeOf(Char)); end; begin try Writeln(GetHashSHA('abc')); Writeln(GetHashSHA('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')); Writeln(GetHashSHA_Unicode('abc')); Writeln(GetHashSHA_Unicode('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')); Readln; except on E:Exception do begin Writeln(E.Classname, ':', E.Message); Readln; end; end; end. 

this return

abc AnsiString

A9993E364706816ABA3E25717850C26C9CD0D89D

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq AnsiString

84983E441C3BD26EBAAE4AA1F95129Easer4670F1 for

abc unicode

9F04F41A848514162050E3D68C1A7ABB441DC2B5

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq Unicode

51D7D8769AC72C409C5B0E3F69C60ADC9A039014

+26
source

The command line of my Cygwin indicates that this is really Unicode, which confuses you:

 ~$ printf 'a\0b\0c\0' | sha1sum 9f04f41a848514162050e3d68c1a7abb441dc2b5 *- ~$ printf 'abc' | sha1sum a9993e364706816aba3e25717850c26c9cd0d89d *- 
+18
source

Could the expected value be for the ANSI string and the hash you get for the string in Unicode?

+6
source

Good, so these were Unicode issues. Just in case, if you want to know, this is my source Unit1.pas. You need a note and button form. Requires DCPCrypt2, LockBox2, LockBox3, and a hash block.

 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, LbCipher, LbClass, StdCtrls, DCPcrypt2, DCPsha1, Hashes, uTPLb_CryptographicLibrary, uTPLb_BaseNonVisualComponent, uTPLb_Hash; type THashProc = reference to procedure(src: AnsiString; var output: AnsiString); TForm1 = class(TForm) Memo1: TMemo; btnTest: TButton; function Display(Buf: TBytes): String; procedure LockBox2Test; procedure LockBox3Test; procedure DCPCrypto2Test; procedure HashesTest; procedure btnTestClick(Sender: TObject); private { Private declarations } procedure RunTests(Name: String; HashFunc: THashProc); public { Public declarations } end; var Form1: TForm1; implementation uses uTPLb_StreamUtils; {$R *.dfm} procedure TForm1.btnTestClick(Sender: TObject); begin LockBox2Test; LockBox3Test; DCPCrypto2Test; HashesTest; end; procedure TForm1.DCPCrypto2Test; begin RunTests('DCPCrypto2', procedure(src: AnsiString; var output: AnsiString) var Digest: TSHA1Digest; Bytes : TBytes; SHA1 : TDCP_sha1; begin SHA1 := TDCP_sha1.Create(nil); SHA1.Init; SHA1.UpdateStr(src); SHA1.Final(Digest); SHA1.Destroy; SetLength(Bytes, 20); Move(Digest, Bytes[0], 20); output := Form1.Display(Bytes); end); end; function TForm1.Display(Buf: TBytes): String; var i: Integer; begin Result := ''; for i := 0 to 19 do Result := Result + Format('%0.2x', [Buf[i]]); Result := LowerCase(Trim(Result)); end; procedure TForm1.HashesTest; begin RunTests('Hashes', procedure(src: AnsiString; var output: AnsiString) begin output := CalcHash2(src, haSHA1) end) end; procedure TForm1.LockBox2Test; begin RunTests('LockBox2', procedure(src: AnsiString; var output: AnsiString) var Digest: TSHA1Digest; Bytes : TBytes; SHA1 : TLbSHA1; begin SHA1 := TLbSHA1.Create(nil); SHA1.HashStringA(src); SHA1.GetDigest(Digest); SHA1.Destroy; SetLength(Bytes, 20); Move(Digest, Bytes[0], 20); output := Form1.Display(Bytes); end); end; procedure TForm1.LockBox3Test; begin RunTests('LockBox3', procedure(src: AnsiString; var output: AnsiString) var Digest: TSHA1Digest; bytes : TBytes; P, Sz: integer; aByte: byte; s: string; SHA1 : THash; Lib : TCryptographicLibrary; begin Lib := TCryptographicLibrary.Create(nil); SHA1 := THash.Create(nil); SHA1.CryptoLibrary := Lib; SHA1.HashId := 'native.hash.SHA-1'; SHA1.Begin_Hash; SHA1.HashAnsiString(src); if not assigned(SHA1.HashOutputValue) then output := 'nil' else begin SetLength(Bytes, 20); Sz := SHA1.HashOutputValue.Size; if Sz <> 20 then output := Format('wrong size: %d', [Sz]) else begin P := 0; SHA1.HashOutputValue.Position := 0; while SHA1.HashOutputValue.Read(aByte, 1) = 1 do begin bytes[P] := aByte; Inc(P); end; output := Form1.Display(Bytes); end; end; SHA1.Destroy; Lib.Destroy; end) end; procedure TForm1.RunTests(Name: String; HashFunc: THashProc); var i: Integer; Tests: array [1 .. 2, 1 .. 2] of AnsiString; src, res: AnsiString; expected: String; begin // http://www.nsrl.nist.gov/testdata/ Tests[1][1] := 'abc'; Tests[1][2] := 'a9993e364706816aba3e25717850c26c9cd0d89d'; Tests[2][1] := 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'; Tests[2][2] := '84983e441c3bd26ebaae4aa1f95129e5e54670f1'; Memo1.Lines.Add(''); Memo1.Lines.Add('**' + Name + '**'); Memo1.Lines.Add(''); for i := 1 to 2 do begin src := Tests[i][1]; expected := Tests[i][2]; HashFunc(src, res); res := Trim(LowerCase(res)); if res = expected then begin Memo1.Lines.Add(Format(' Test %d passes', [i])) end else begin Memo1.Lines.Add(Format(' FAILED: %d (''%s'') ', [i, src])); Memo1.Lines.Add(Format(' Got: ''%s''', [res])); Memo1.Lines.Add(Format(' Expected: ''%s''', [expected])); end; end; end; end. 
+4
source

All Articles