7

我正在实施 XML 数字签名。我从小步骤开始,所以现在我想解决 SHA-1 散列的问题。

SO中有很多关于这个的问题:

  1. 带密码箱的数字签名钥匙
  2. Delphi 的加密库
  3. 将此 php 数字签名转换为 Delphi
  4. Delphi:是否有适用于 Delphi-XE 的 LockBox 版本
  5. Delphi 2010 密码库

......可能还有更多。但是,我使用的是 Delphi XE。到目前为止,我已经尝试过 LockBox 2(Songbeamer 和 Sourceforge 版本)、Lock Box 3、DCPCrypto2 和其他一些(Hashes一个使用 Windows 加密功能的易于使用的单元)

我准备了一个小型试验台,它给了我以下信息:

锁箱2

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

锁盒3

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

4 回答 4

26

莱昂纳多,我认为你的问题是UNICODE当你使用一个函数来散列string你正在传递一个字节数组(缓冲区)时。所以当你abc在 Delphi XE 中传递字符串时,你正在像这样散列一个缓冲区61 00 62 00 63 00(十六进制表示)

Jwscl library检查这个使用(JEDI Windows 安全代码库)中的 Windows 加密函数的示例应用程序

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.

这个回报

abcAnsiString

A9993E364706816ABA3E25717850C26C9CD0D89D

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqAnsiString

84983E441C3BD26EBAAE4AA1F95129E5E54670F1 为

abc统一码

9F04F41A848514162050E3D68C1A7ABB441DC2B5

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq统一码

51D7D8769AC72C409C5B0E3F69C60ADC9A039014

于 2011-02-08T05:39:44.453 回答
19

我的 Cygwin 命令提示符告诉我确实是 Unicode 让你感到困惑:

~$ printf 'a\0b\0c\0' | sha1sum
9f04f41a848514162050e3d68c1a7abb441dc2b5 *-
~$ printf 'abc' | sha1sum
a9993e364706816aba3e25717850c26c9cd0d89d *-
于 2011-02-08T05:44:19.577 回答
6

预期值是否可以用于 ANSI 字符串,而您获得的哈希值是否用于 unicode 字符串?

于 2011-02-08T05:37:39.087 回答
4

好的,所以这是 Unicode 问题。以防万一你想知道,这是我的 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.
于 2011-02-09T03:31:23.257 回答