2012-04-03 12 views
5

Necesito acceder a los servicios de Amazon REST como una pregunta anterior en "HMAC-SHA256 in Delphi". Dado que este tiene que estar en D2010 Estoy tratando de utilizar la última libeay32.dll para pasar los vectores de prueba en el RFC 4231:Delphi - No se puede obtener HMAC-SHA256 para pasar los vectores de prueba RFC 4231

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

¿alguien tiene un método que pasa estas pruebas en Delphi que utilizan esta biblioteca ? El código publicado por shunty en la publicación a la que me he referido pasa los primeros dos vectores de prueba y el quinto, pero falla en el tercero y el cuarto. Esos vectores tienen más de 64 bytes y dado que todas las URL que debo firmar para Amazon tienen más de 64 bytes, esto es un problema. No he podido averiguar si estoy haciendo algo mal. La prueba OpenSSL está en hmactest.c, pero solo verifica EVP_md5 y los vectores de prueba no son todos iguales que en RFC. Necesito que esto funcione con SHA256 para que pueda verificar contra los vectores de prueba en el RFC. Estoy usando las siguientes constantes para las pruebas constantes (ahora actualizado para futuros espectadores para arreglar mis copiar y pegar errores mencionados en los comentarios a continuación):

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

No sé cómo este código por shunty se verá pegado porque se ve terrible aquí (soy un principiante de stackoverflow). Solía ​​RAND_seed en lugar de RAND_load_file como lo hizo, pero por lo demás es lo mismo:

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; 

El código que utilizo para la prueba es el siguiente. Este método particular es para la prueba 3 que falla. El resultado es bb861233f283aef2ef7aea09785245c9f3c62720c9d04e0c232789f27a586e44, pero debe ser igual al valor hexadecimal constante para 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; 

¿Alguna idea? Estoy a punto de darse por vencido y acaba de crear una aplicación .NET utilizando la biblioteca que proporcionan ...

Respuesta

6

Está utilizando D2009 + (como es evidente por su uso de TEncoding), lo que significa que se trata de UnicodeString, pero no tienen en cuenta a Unicode en tu lógica. El RFC no opera en caracteres, opera en bytes. Sus datos de prueba contienen cadenas codificadas en hexadecimal. Cuando los decodifica en valores (Unicode)String, muchos de los caracteres resultantes están fuera del rango de caracteres ASCII, lo que significa que tienen que ser interpretados por páginas de códigos Ansi antes de poder convertirlos a UTF-8 correctamente (que no debería usar en esta situación de todos modos).

necesita cambiar su aplicación para decodificar las cadenas hexadecimales directamente a TBytes lugar (se puede utilizar Classes.HexToBin() para eso) por lo que los valores de bytes correctos se conservan y se pasa a HMAC, y deshacerse de TEncoding.UTF8.GetBytes() completo:

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

En mi defensa, se usó el UTF8.GetBytes porque eso era lo que requería la parte interna de la aplicación en particular en la que estábamos trabajando, nunca interactuamos con nada fuera de la aplicación. Y hay un descargo de responsabilidad en la parte inferior de la publicación que indica que no lo había usado contra los datos de referencia. Y hemos mejorado en los últimos dos años :-) – shunty

+0

Interesante. En un momento tuve un GetHMAC sobrecargado que tomaba TBytes como parámetros. Sin embargo, no estoy seguro si estaba usando las pruebas codificadas Hex del RFC en ese momento. Probablemente no funcionó debido a algunos problemas Unicode. Nunca antes tuve que lidiar con esto directamente así. Para Amazon, debe tomar un URI, convertirlo con HMAC SHA1 o SHA256, luego convertirlo a Base64, luego codificarlo con URL, luego agregar la firma al URI original. En Delphi es como caminar de un tanque de tiburones a otro y a otro de nuevo. El código que usa la biblioteca .NET de Amazon es tan simple en comparación. –

+0

@shunty - Gracias por la publicación original.Parece mi comentario de que dijiste que no lo habías probado en comparación con los datos de referencia que se descartaron en algún momento de la edición. Eso estuvo bien porque tu descargo de responsabilidad es lo que me hizo encontrar el RFC y tratar de que funcionen sus pruebas con codificación hexadecimal. –

Cuestiones relacionadas