Delphi - 无法通过RFC 4231测试向量获得HMAC-SHA256

时间:2012-04-03 00:53:30

标签: delphi hmac amazon-mws

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

http://tools.ietf.org/html/rfc4231

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

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应用程序......

1 个答案:

答案 0 :(得分:6)

您正在使用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;