2012-04-03 11 views
5

"HMAC-SHA256 in Delphi" adlı bir önceki soru gibi Amazon REST hizmetlerine erişmem gerekiyor. Bu ben RFC 4231 test vektörleri geçmek son libeay32.dll kullanmaya çalışıyorum D2010 olmak zorunda beri:Delphi - HMAC-SHA256'nın RFC 4231 test vektörlerini geçmesine izin verilmiyor

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

herkes bu kütüphaneyi kullanan Delphi bu testleri geçecek bir yöntem var mı ? Gönderdiğim yazıdaki şantiyenin gönderdiği kod, ilk iki test vektörünü ve beşinciyi geçiyor, ancak üçüncü ve dördüncü sırada başarısız oluyor. Bu vektörler 64 bayttan fazladır ve Amazon için imzalamam gereken tüm URL'ler 64 bayttan fazla olduğundan bu bir problemdir. Yanlış bir şey yapıp yapmadığımı anlayamadım. OpenSSL testi hmactest.c dosyasındadır, ancak yalnızca EVP_md5'i kontrol eder ve test vektörleri RFC'deki ile aynı değildir. SHA256 ile çalışmak için buna ihtiyacım var, bu yüzden RFC'deki test vektörlerine karşı doğrulayabilirim. Ben (aşağıda yorum olarak bahsedilen benim kopyalama ve yapıştırma hataları düzeltmek için şimdi gelecekteki izleyiciler için güncellenmiş sabitler) testleri için aşağıdaki sabitleri kullanıyorum:

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'; 

Ben shunty bu kod nasıl görüneceğini bilmiyorum yapıştırılmış çünkü burada korkunç görünüyor (Ben bir stackoverflow acemi değilim). Daha doğrusu onun gibi RAND_load_file daha RAND_seed kullanılır, ancak aksi takdirde aynı: Ben testine kullanmak

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; 

kod buna benzemez. Bu özel yöntem, başarısız olan test 3 içindir. Sonuç bb861233f283aef2ef7aea09785245c9f3c62720c9d04e0c232789f27a586e44, ancak TEST3_DIGEST için sabit onaltılık değerine eşit olmalıdır:

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; 

Herhangi bir fikir? Vazgeçmek üzereyim ve sağladıkları kütüphaneyi kullanarak bir .NET uygulaması yaratıyorum ...

cevap

6

D2009 + (TEncoding kullanımınız ile belirttiğiniz gibi) kullanıyorsunuz, yani UnicodeString ile uğraşırken mantıkınızda Unicode'u dikkate almazsınız. RFC, karakterlerde çalışmaz, baytlarda çalışır. Test verileriniz hex kodlu dizeler içerir. Bunları (Unicode)String değerlerine çözdüğünüzde, ortaya çıkan karakterlerin birçoğu ASCII karakter aralığının dışındadır, yani bunları doğru olarak UTF-8'e dönüştürebilmek için Ansi kod sayfaları tarafından yorumlanması gerekir. zaten bu durum).

yerine (bunun için Classes.HexToBin() kullanabilirsiniz) düz TBytes için onaltılık dizeleri çözmek için uygulamanızı değiştirmeniz gerekir böylece doğru bayt değerleri korunacak ve HMAC geçirilen ve tamamen TEncoding.UTF8.GetBytes() kurtulmak gibidir:

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

Benim savunmamda, UTF8.GetBytes kullanıldı çünkü bu, üzerinde çalıştığımız özel uygulamanın içeriğinin ne olduğuydu - hiçbir zaman uygulama dışında hiçbir şeyle etkileşim kurmuyorduk. Ve gönderinin altında referans verilere karşı kullanmadığımı belirten bir yasal uyarı var. Ve son iki yılda geliştirdik :-) – shunty

+0

İlginç. Bir noktada, TBytes'i parametre olarak kullanan aşırı yüklenmiş bir GetHMAC vardı. Yine de, RFC'den Hex kodlanmış testlerini kullanıp kullanmadığımı bilmiyorum. Muhtemelen bazı Unicode sorunları nedeniyle işe yaramadı. Daha önce hiç böyle bir şeyle uğraşmak zorunda kalmamıştım. Amazon için bir URI almalısınız, HMAC SHA1 veya SHA256 ile dönüştürmeli, sonra bunu Base64'e dönüştürmeli, daha sonra URL bunu kodlayacak, ardından imzayı orijinal URI'ye ekleyecektir. Delphi'de bir köpekbalığı tankından diğerine ve tekrar diğerine yürümek gibi. Amazon'un .NET kütüphanesinin kullandığı kod, karşılaştırma yaparak çok basit. –

+0

@shunty - Orijinal gönderi için teşekkürler.Yorumda, düzenlemenin herhangi bir noktasında düşmüş herhangi bir referans verisine karşı test etmediğinizi söylediniz. Bu iyiydi çünkü feragatnameniz, RFC'yi bulmamı sağladı ve onaltılık kodlanmış testlerini işe almaya çalıştı. –

İlgili konular