【问题标题】:Delphi - Can't get HMAC-SHA256 to pass RFC 4231 test vectorsDelphi - 无法让 HMAC-SHA256 通过 RFC 4231 测试向量
【发布时间】:2012-04-16 15:58:42
【问题描述】:

我需要访问 Amazon REST 服务,就像之前在“HMAC-SHA256 in Delphi”提出的问题一样。由于这必须在 D2010 中,我正在尝试使用最新的 libeay32.dll 来通过 RFC 4231 中的测试向量:

https://www.rfc-editor.org/rfc/rfc4231

有没有人使用这个库在 Delphi 中通过这些测试的方法? shunty 在我提到的帖子中发布的代码通过了前两个测试向量以及第五个,但在第三个和第四个中失败了。这些向量超过 64 个字节,因为我需要为亚马逊签名的所有 url 都超过 64 个字节,这是一个问题。我一直无法弄清楚我是否做错了什么。 OpenSSL 测试在 hmactest.c 中,但它只检查 EVP_md5 并且测试向量与 RFC 中的不同。我需要它与 SHA256 一起使用,以便我可以根据 RFC 中的测试向量进行验证。我正在使用以下常量进行测试(现在为将来的查看者更新了常量,以修复下面的 cmets 中提到的我的复制和粘贴错误):

const
  LIBEAY_DLL_NAME = 'libeay32.dll';
  EVP_MAX_MD_SIZE = 64;

  //RFC 4231 Test case 1
  TEST1_KEY: string = '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b';
  TEST1_DATA: string = '4869205468657265';
  TEST1_DIGEST: string = 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7';

  //RFC 4231 Test case 2
  TEST2_KEY = '4a656665';
  TEST2_DATA = '7768617420646f2079612077616e7420666f72206e6f7468696e673f';
  TEST2_DIGEST = '5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843';

  //RFC 4231 Test case 3
  TEST3_KEY = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
  TEST3_DATA = 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd';
  TEST3_DIGEST = '773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe';

  //RFC 4231 Test case 4
  TEST4_KEY = '0102030405060708090a0b0c0d0e0f10111213141516171819';
  TEST4_DATA = 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd';
  TEST4_DIGEST = '82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b';

  //RFC 4231 Test case 5
  TEST5_KEY = '0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c';
  TEST5_DATA = '546573742057697468205472756e636174696f6e';
  TEST5_DIGEST = 'a3b6167473100ee06e0c796c2955552b';

我不知道 shunty 粘贴的这段代码看起来如何,因为它在这里看起来很糟糕(我是 stackoverflow 新手)。我像他一样使用 RAND_seed 而不是 RAND_load_file,但除此之外都是一样的:

function TForm1.GetHMAC(const AKey, AData: string): TBytes;
var
  key, data: TBytes;
  md_len: integer;
  res: PByte;
  buf: PInteger;
  rand_val: Integer;
begin
  OpenSSL_add_all_algorithms;

  Randomize;
  rand_val := Random(100);
  GetMem(buf, rand_val);
  try
    RAND_seed(buf, rand_val);

    key := TEncoding.UTF8.GetBytes(AKey);
    data := TEncoding.UTF8.GetBytes(AData);
    md_len := EVP_MAX_MD_SIZE;
    SetLength(Result, md_len);
    res := HMAC(EVP_sha256, @key[0], Length(key), @data[0], Length(data), @result[0], md_len);
    if (res <> nil) then
      SetLength(Result, md_len);

  finally
    FreeMem(buf);
  end;
end;

我用来测试的代码如下所示。此特定方法适用于失败的测试 3。结果是 bb861233f283aef2ef7aea09785245c9f3c62720c9d04e0c232789f27a586e44,但它应该等于 TEST3_DIGEST 的常量十六进制值:

procedure TForm1.btnTestCase3Click(Sender: TObject);
var
  LBytesDigest: TBytes;
  LHashString: string;
  LHexDigest: string;
begin
  LBytesDigest := GetHMAC(HexToStr(TEST3_KEY), HexToStr(TEST3_DATA));

  LHexDigest := LowerCase(BytesToHex(LBytesDigest));

  if LHexDigest = TEST3_DIGEST then begin
    Memo1.Lines.Add('SUCCESS: Matches test case');
    Memo1.Lines.Add(LHexDigest);
  end else begin
    Memo1.Lines.Add('ERROR: Does not match test case');
    Memo1.Lines.Add('Result:    ' + LHexDigest);
    Memo1.Lines.Add('Test Case: ' + TEST3_DIGEST);
  end;
end;

有什么想法吗?我即将放弃,只使用他们提供的库创建一个 .NET 应用程序......

【问题讨论】:

    标签: delphi hmac amazon-mws


    【解决方案1】:

    您正在使用 D2009+(从您使用 TEncoding 可以看出),这意味着您正在处理 UnicodeString,但您的逻辑中没有考虑 Unicode。 RFC 不对字符进行操作,而是对字节进行操作。您的测试数据包含十六进制编码的字符串。当您将它们解码为 (Unicode)String 值时,许多生成的字符都超出了 ASCII 字符范围,这意味着它们必须由 Ansi 代码页解释,然后才能正确地将它们转换为 UTF-8(您不应该这样做)无论如何都在这种情况下使用)。

    您需要更改您的实现以将您的十六进制字符串直接解码为TBytes(您可以为此使用Classes.HexToBin()),以便保留正确的字节值并将其传递给HMAC,并完全摆脱TEncoding.UTF8.GetBytes()

    function TForm1.GetHMAC(const AKey, AData: TBytes): TBytes; 
    var 
      md_len: integer; 
      res: PByte; 
      buf: PInteger; 
      rand_val: Integer; 
    begin 
      OpenSSL_add_all_algorithms; 
    
      Randomize; 
      rand_val := Random(100); 
      GetMem(buf, rand_val); 
      try 
        RAND_seed(buf, rand_val); 
        md_len := EVP_MAX_MD_SIZE; 
        SetLength(Result, md_len); 
        res := HMAC(EVP_sha256, Pointer(AKey), Length(AKey), Pointer(AData), Length(AData), @Result[0], md_len); 
        if (res <> nil) then 
          SetLength(Result, md_len); 
      finally 
        FreeMem(buf); 
      end; 
    end; 
    
    function HexToBytes(const S: String): TBytes;
    begin
      SetLength(Result, Length(S) div 2);
      SetLength(Result, HexToBin(PChar(S), Pointer(Result), Length(Result)));
    en;
    
    procedure TForm1.btnTestCase3Click(Sender: TObject);  
    var  
      LBytesDigest: TBytes;  
      LHashString: string;  
      LHexDigest: string;  
    begin  
      LBytesDigest := GetHMAC(HexToBytes(TEST3_KEY), HexToBytes(TEST3_DATA));  
      LHexDigest := LowerCase(BytesToHex(LBytesDigest));  
      if LHexDigest = TEST3_DIGEST then begin  
        Memo1.Lines.Add('SUCCESS: Matches test case');  
        Memo1.Lines.Add(LHexDigest);  
      end else begin  
        Memo1.Lines.Add('ERROR: Does not match test case');  
        Memo1.Lines.Add('Result:    ' + LHexDigest);  
        Memo1.Lines.Add('Test Case: ' + TEST3_DIGEST);  
      end;  
    end;  
    

    【讨论】:

    • 在我的辩护中,使用 UTF8.GetBytes 是因为这是我们正在开发的特定应用程序的内部要求 - 我们从未与应用程序之外的任何东西进行交互。帖子底部有一个免责声明,说明我没有将它用于参考数据。在过去的两年里,我们取得了进步:-)
    • 有趣。有一次,我有一个以 TBytes 作为参数的重载 GetHMAC。不过,不确定我当时是否使用了 RFC 中的十六进制编码测试。由于某些 Unicode 问题,它可能无法正常工作。我以前从来没有像这样直接处理它。对于亚马逊,您必须获取一个 URI,使用 HMAC SHA1 或 SHA256 对其进行转换,然后将其转换为 Base64,然后对其进行 URL 编码,然后将签名添加到原始 URI。在 Delphi 中,这就像从一个鲨鱼缸走进另一个鲨鱼缸,然后又走进另一个鲨鱼缸。相比之下,Amazon 的 .NET 库使用的代码非常简单。
    • @shunty - 感谢您的原帖。看起来我的评论是您确实说过您没有针对任何参考数据对其进行测试,但在编辑的某个时候被删除了。这很好,因为你的免责声明让我找到了 RFC 并尝试让他们的十六进制编码测试工作。
    • 仍然不匹配第三个 RFC 测试向量。我现在收到 773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe。
    • @Ron - 我猜测试 3 失败了,因为您正在针对 384 位而不是 256 位的 hmac 进行测试。我认为来自 RFC 的一些复制和粘贴错误 :-)
    猜你喜欢
    • 1970-01-01
    • 2011-07-05
    • 2013-03-13
    • 2019-06-05
    • 2017-08-19
    • 2021-08-17
    • 2010-11-17
    • 2013-05-12
    • 1970-01-01
    相关资源
    最近更新 更多