SHA1 hashing in Delphi XE

为君一笑 提交于 2019-11-28 07:48:20

Leonardo, i think which your problem is the UNICODE when you uses a function to hash a string you are passing a array (buffer) of bytes. so when you pass the abc string in Delphi XE, your are hashing a buffer like this 61 00 62 00 63 00 (Hex representation)

check this sample application which uses the Windows crypto functions 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

84983E441C3BD26EBAAE4AA1F95129E5E54670F1 for

abc unicode

9F04F41A848514162050E3D68C1A7ABB441DC2B5

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq Unicode

51D7D8769AC72C409C5B0E3F69C60ADC9A039014

My Cygwin command-prompt tells me it is indeed Unicode that's confusing you:

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

Could the expected value be for an ANSI string and the hash you are getting is for a unicode string?

Okay, so it was Unicode issues. Just in case you want to know, this is my Unit1.pas source. You need a form with a memo and a button. Requires DCPCrypt2, LockBox2, LockBox3 and the Hashes unit.

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.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!