Хеширование SHA1 в Delphi XE - PullRequest
7 голосов
/ 08 февраля 2011

Я нахожусь в процессе внедрения цифровых подписей XML. Я начинаю с маленьких шагов, поэтому сейчас я хочу решить проблему хеширования SHA-1.

В SO много вопросов по этому поводу:

  1. Цифровой ключ для подписи с замком
  2. Библиотека шифрования для Delphi
  3. Преобразование этой цифровой подписи php в Delphi
  4. Delphi: есть ли версия LockBox для Delphi-XE
  5. Библиотеки криптографии Delphi 2010

... и, возможно, больше. Тем не менее, я использую Delphi XE. До сих пор я пробовал LockBox 2 (как версии Songbeamer, так и Sourceforge), Lock Box 3, DCPCrypto2 и некоторые другие ( Hashes - это простое в использовании устройство, использующее функции шифрования Windows)

Я подготовил небольшую испытательную установку, которая дает мне следующее:

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'

Хэши

Test 1 passes
Test 2 passes

Удалось ли вам скомпилировать упомянутые библиотеки под Delphi XE и заставить их давать соответствующие значения? Мне особенно интересна процедура DCPCrypt2 SelfTest.

Редактировать : я добавил этот ответ с фиксированным исходным кодом. Спасибо всем за помощь, это очень ценится.

Ответы [ 4 ]

26 голосов
/ 08 февраля 2011

Леонардо, я думаю, что ваша проблема в UNICODE, когда вы используете функцию для хеширования string, вы передаете массив (буфер) байтов.поэтому, когда вы передаете строку abc в Delphi XE, вы хэшируете буфер, подобный этому 61 00 62 00 63 00 (шестнадцатеричное представление)

проверьте этот пример приложения, которое использует криптографические функции Windows из Jwscl library (JEDIКод безопасности Windows 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.

это возвращение

abc AnsiString

A9993E364706816ABA3E25717850C26C9CD0D89D

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq AnsiString

* 10261F5AEEFE5EB5505A1505A1505A1505A1505A1505A1505Bдля

abc Unicode

9F04F41A848514162050E3D68C1A7ABB441DC2B5

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq Unicode

51D7D8769AC72C409C5B0E3F690014444

19 голосов
/ 08 февраля 2011

Моя командная строка Cygwin сообщает, что вас действительно смущает Unicode:

~$ printf 'a\0b\0c\0' | sha1sum
9f04f41a848514162050e3d68c1a7abb441dc2b5 *-
~$ printf 'abc' | sha1sum
a9993e364706816aba3e25717850c26c9cd0d89d *-
6 голосов
/ 08 февраля 2011

Может ли ожидаемое значение быть строкой ANSI, а полученный хеш - строкой Юникода?

4 голосов
/ 09 февраля 2011

Хорошо, это были проблемы с Юникодом. На всякий случай, если вы хотите знать, это мой источник Unit1.pas. Вам нужна форма с напоминанием и кнопкой. Требуется DCPCrypt2, LockBox2, LockBox3 и блок хэшей.

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.
...