2012-04-03 11 views
5

"HMAC-SHA256 in Delphi"で前の質問のようにAmazon RESTサービスにアクセスする必要があります。これはD2010にしておく必要がありますので、私は、RFC 4231でテストベクタを渡すために、最新のlibeay32.dllを使用しようとしている:Delphi - HMAC-SHA256にRFC 4231テストベクトルを渡すことができません

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

誰もがこのライブラリを使用して、Delphiで、これらのテストに合格する方法を持っています?私が参照した記事でshuntyが投稿したコードは、最初の2つのテストベクタと5番目のテストベクタを渡しますが、3番目と4番目のテストベクタは失敗します。これらのベクトルは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アプリケーションを作成しようよ...

答えて

6
あなたは UnicodeStringを扱っている意味、( TEncodingの使用によって明らかにされる)D2009の+を使用している

ていますあなたのロジックではUnicodeを考慮に入れていません。 RFCは文字に対しては動作せず、バイト単位で動作します。テストデータには16進数の文字列が含まれています。それらを(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が使用されました。なぜなら、それは私たちが取り組んでいた特定のアプリケーションの内部が必要だったからです。私たちは決してアプリケーションの外のものと対話していませんでした。私は参照データに対して使用していなかったという旨のポストの一番下に免責があります。私たちはここ2年間で改善しました:-) – shunty

+0

興味深い。ある時点で、私はパラメータとしてTBytesを取ったオーバーロードされたGetHMACを持っていました。私がRFCの当時の16進数でエンコードされたテストを使用していたかどうかはわかりませんが、いくつかのUnicodeの問題のためにおそらく動作しませんでした。私は前にこのように直接対処する必要はなかった。 Amazonの場合は、URIを取得してHMAC SHA1またはSHA256で変換し、Base64に変換してからURLエンコードし、元のURIに署名を追加する必要があります。デルファイでは、あるシャークタンクから別のシャークタンクに歩き回り、再び別のシャークタンクに移動するのが好きです。 Amazonの.NETライブラリが使用するコードは、比較すると非常に簡単です。 –

+0

@shunty - 元の投稿をありがとう。私のコメントのように、編集中のある時点で参照データが破棄されたことをテストしていないと言っていたようです。あなたの免責事項が私にRFCを見つけさせ、16進数でエンコードされたテストを動作させようとしているからです。 –

関連する問題