2012-04-03 11 views
5

Ho bisogno di accedere ai servizi Amazon REST come una domanda precedente a "HMAC-SHA256 in Delphi" richiesto. Dal momento che questo deve essere in D2010 sto cercando di utilizzare l'ultima libeay32.dll di passare i vettori di test nella RFC 4231:Delphi - Impossibile ottenere HMAC-SHA256 per passare i vettori di prova RFC 4231

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

Qualcuno ha un metodo che passa questi test in Delphi che utilizzano questa libreria ? Il codice pubblicato da shunty nel post a cui ho fatto riferimento passa i primi due vettori di test e anche il quinto, ma fallisce nel terzo e nel quarto. Questi vettori sono oltre 64 byte e poiché tutti gli URL che devo firmare per Amazon sono superiori a 64 byte, questo è un problema. Non sono stato in grado di capire se sto facendo qualcosa di sbagliato. Il test OpenSSL è in hmactest.c, ma controlla solo EVP_md5 ei vettori di test non sono tutti uguali a quelli della RFC. Ho bisogno di questo per lavorare con SHA256 in modo da poter verificare i vettori di prova nella RFC. Sto utilizzando le seguenti costanti per le prove (costanti ora aggiornato per gli spettatori futuri per correggere i miei errori di copia e incolla menzionati nei commenti qui sotto):

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

non so come questo codice shunty guarderà incollato perché sembra terribile qui (sono un novizio dello stackoverflow). Ho usato rand_seed piuttosto che RAND_load_file come ha fatto, ma per il resto è la stessa:

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; 

Il codice che uso per test di simile a questo. Questo particolare metodo è per il test 3 che fallisce. Il risultato è bb861233f283aef2ef7aea09785245c9f3c62720c9d04e0c232789f27a586e44, ma dovrebbe essere uguale al valore esadecimale costante per 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; 

Tutte le idee? Sto per mollare e basta creare una app .NET usando la libreria che forniscono ...

risposta

6

Stai usando D2009 + (come evidente dal tuo uso di TEncoding), il che significa che hai a che fare con UnicodeString, ma tu non stanno prendendo in considerazione Unicode nella tua logica. L'RFC non funziona sui caratteri, funziona su byte. I tuoi dati di test contengono stringhe con codifica esadecimale. Quando li decodifichi nei valori (Unicode)String, molti dei caratteri risultanti sono al di fuori dell'intervallo ASCII di caratteri, il che significa che devono essere interpretati dalle codepage di Ansi prima di poterli convertire correttamente in UTF-8 (che non dovresti usare in questa situazione comunque).

è necessario modificare l'implementazione per decodificare le stringhe esadecimali direttamente a TBytes invece (è possibile utilizzare Classes.HexToBin() per questo) in modo che i valori di byte corretti sono conservati e passati al HMAC, e sbarazzarsi di TEncoding.UTF8.GetBytes() completamente:

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

In mia difesa, è stato utilizzato UTF8.GetBytes perché era quello che richiedevano gli interni della particolare app su cui stavamo lavorando: non abbiamo mai interagito con nessun elemento esterno all'app. E c'è un disclaimer in fondo al post che afferma che non l'ho usato contro i dati di riferimento. E siamo migliorati negli ultimi due anni :-) – shunty

+0

Interessante. A un certo punto ho avuto un GetHMAC sovraccarico che ha preso TByte come parametri. Non sono sicuro se stavo usando i test codificati Hex dal RFC al momento, però. Probabilmente non ha funzionato a causa di alcuni problemi Unicode. Non ho mai dovuto affrontarlo direttamente come prima. Per Amazon devi prendere un URI, convertirlo con HMAC SHA1 o SHA256, quindi convertirlo in Base64, quindi codificare l'URL, quindi aggiungere la firma all'URI originale. In Delphi è come camminare da un carro di squali in un altro e di nuovo in un altro. Il codice utilizzato dalla libreria .NET di Amazon è così semplice al confronto. –

+0

@shunty - Grazie per il post originale.Sembra che il mio commento abbia affermato che non era stato testato su nessun dato di riferimento che è stato eliminato a un certo punto del montaggio. Questo andava bene perché il tuo disclaimer è ciò che mi ha fatto trovare la RFC e provare a far funzionare i loro test con codifica esadecimale. –

Problemi correlati