【问题标题】:SHA1 hashing in Delphi XEDelphi XE 中的 SHA1 哈希
【发布时间】:2011-06-23 05:07:03
【问题描述】:

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

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

  1. Digitially Sign Key with Lockbox
  2. Encryption library for Delphi
  3. Convert this php digital signing to Delphi
  4. Delphi: is there a version of LockBox for Delphi-XE
  5. Delphi 2010 Cryptography libraries

...可能还有更多。但是,我使用的是 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 程序特别感兴趣。

编辑:我添加了带有固定源代码的this answer。谢谢大家的帮助,非常感谢。

【问题讨论】:

  • 没有示例代码,您的问题毫无意义。
  • +1 了解图书馆概览。请注意,散列适用于二进制数据,而不适用于字符串(它们的二进制表示取决于它们的编码)。我在writing on MD5 hashing 时写了一个类似的结论。 (阅读答案:这也是答案的结论)。
  • @Warren P - 我会用我的代码更新这个答案。
  • @Eugene Mayevski 'EldoS Corp - 伙计,我只是在评估 SecureBlackBox 工具包,我被吓坏了。你不是有一张漂亮的、厚实的折扣券可以给我吗? ;-)
  • @Leonardo 是的,请通过我们的帮助中心 (eldos.com/helpdesk) 与我联系

标签: delphi cryptography delphi-xe sha1


【解决方案1】:

Leonardo,我认为您的问题是UNICODE,当您使用函数对string 进行散列时,您正在传递一个字节数组(缓冲区)。所以当你在 Delphi XE 中传递 abc 字符串时,你正在散列一个像这样的缓冲区 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 为

abcunicode

9F04F41A848514162050E3D68C1A7ABB441DC2B5

abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqUnicode

51D7D8769AC72C409C5B0E3F69C60ADC9A039014

【讨论】:

  • 很好的答案。我现在明白了。我猜是 DCPCrypt2 SelfTest 函数让我走错了方向,它返回 false。
  • 好提示。我不知道你可以在 Windows 中使用加密库,使用 JWSCL 包装器。
【解决方案2】:

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

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

【讨论】:

  • @Leonardo 我猜你是在讽刺,但它确实显示了 Cygwin 在测试这些假设方面的实用性 - md5sum、sha1sum 和 openssl 等等。
  • 不,我不是在讽刺!我接受了 RRUZ 的答案,因为它更完整,但这是一个了不起的答案。谢谢!
  • 我不会说它更完整,它只是更冗长,不一定更好。无论如何,我不是想敲 RRUZ,但我和 Barry 在一起,所有优秀的开发人员都应该学会使用强大的命令行工具,这样他们才能更有效地完成这样的任务。
  • 我完全同意:不同的工具。我喜欢这个答案,它真的很清楚,实际上我在阅读 RRUZ 之前阅读了这个并立即得到了答案。如果我可以接受一个问题的两个答案,我会的! (RRUZ 的回答不仅更加冗长。)
【解决方案3】:

期望值是 ANSI 字符串,而您得到的哈希值是 unicode 字符串吗?

【讨论】:

    【解决方案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.
    

    【讨论】:

      猜你喜欢
      • 2018-11-26
      • 2011-07-24
      • 1970-01-01
      • 2017-10-30
      • 1970-01-01
      • 2011-03-11
      • 2019-07-12
      • 1970-01-01
      • 2013-03-15
      相关资源
      最近更新 更多