2012-04-03 33 views
5

我需要訪問亞馬遜REST服務,就像前一個問題在「HMAC-SHA256 in Delphi」處詢問。由於這必須是在D2010我試圖使用最新的libeay32.dll通過測試向量在RFC 4231:德爾福 - 無法讓HMAC-SHA256通過RFC 4231測試向量

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

沒有人有通過使用這個庫在Delphi中這些測試方法? shunty在我提到的帖子中發佈的代碼通過了前兩個測試向量以及第五個,但是它在第三個和第四個測試中失敗了。這些向量超過64個字節,因爲我需要爲亞馬遜簽名的所有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應用程序...

回答

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; 
+0

在我的防守中,使用了UTF8.GetBytes,因爲那是我們正在開發的特定應用程序的內部要求 - 我們從未與應用程序之外的任何內容進行交互。在帖子底部有一個免責聲明,說明我沒有用它來對照參考數據。我們在過去兩年中有所改進:-) – shunty 2012-04-03 06:50:15

+0

有趣。有一次,我有一個重載的GetHMAC,它將TBytes作爲參數。不過,我不確定當時是否使用RFC編碼的十六進制測試。由於某些Unicode問題,它可能不起作用。我從來沒有必須像這樣直接處理它。對於亞馬遜,您必須採用URI,將其轉換爲HMAC SHA1或SHA256,然後將其轉換爲Base64,然後進行URL編碼,然後將簽名添加到原始URI。在Delphi中,它就像是從一個鯊魚坦克走進另一個鯊魚坦克。亞馬遜的.NET庫使用的代碼比較簡單。 – 2012-04-03 07:17:13

+0

@shunty - 感謝原文。看起來像我的評論,你確實說過你沒有測試過任何參考數據在編輯過程中被放棄。這很好,因爲你的免責聲明是讓我找到RFC並試圖讓他們的十六進制編碼測試正常工作的原因。 – 2012-04-03 07:28:05