2011-05-20 22 views
37

Estoy tratando de hacer uso de una API REST usando C#. El creador de API ha proporcionado bibliotecas de muestra en PHP, Ruby y Java. Me estoy colgando en una parte donde necesito generar un HMAC.¿Cómo generar HMAC-SHA1 en C#?

Así es como se hace en las bibliotecas de muestra que han proporcionado.

PHP

hash_hmac('sha1', $signatureString, $secretKey, false); 

Rubí

digest = OpenSSL::Digest::Digest.new('sha1') 
return OpenSSL::HMAC.hexdigest(digest, secretKey, signatureString) 

Java

SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA1_ALGORITHM); 

Mac mac = null; 
mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); 
mac.init(signingKey); 

byte[] bytes = mac.doFinal(signatureString.getBytes()); 

String form = ""; 
for (int i = 0; i < bytes.length; i++) 
{ 
    String str = Integer.toHexString(((int)bytes[i]) & 0xff); 
    if (str.length() == 1) 
    { 
     str = "0" + str; 
    } 

    form = form + str; 
} 
return form; 

Aquí es mi att empt en C#. No está funcionando. ACTUALIZACIÓN: El siguiente ejemplo de C# funciona bien. Descubrí que el verdadero problema se debía a algunas diferencias entre plataformas en los caracteres de nueva línea en mi signatureString.

var enc = Encoding.ASCII; 
HMACSHA1 hmac = new HMACSHA1(enc.GetBytes(secretKey)); 
hmac.Initialize(); 

byte[] buffer = enc.GetBytes(signatureString); 
return BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower(); 
+1

Resulta que mi código C# funciona bien.El problema real se debió a algunas diferencias entre plataformas en los caracteres de nueva línea en mi 'signatureString'. Voy a marcar esta pregunta para su eliminación. – jessegavin

+4

En lugar de eliminar la pregunta, publique su código como respuesta y acéptelo. Esta pregunta es muy útil para quienes buscan hacer lo mismo. –

+0

Agregué una actualización adicional sobre el texto "La API responde ..." para evitar posibles malentendidos –

Respuesta

5

Prueba esto:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.hmacsha1.aspx

rápida y sucia código:

public string Encode(string input, byte [] key) 
{ 
     HMACSHA1 myhmacsha1 = new HMACSHA1(key); 
     byte[] byteArray = Encoding.ASCII.GetBytes(input); 
     MemoryStream stream = new MemoryStream(byteArray); 
     byte[] hashValue = myhmacsha1.ComputeHash(stream); 
     return hashValue.ToString(); 
} 
+1

Gracias por la respuesta. Sin embargo, su código devuelve la cadena 'System.Byte []'. Así que modifiqué la última línea para 'devolver BitConverter.ToString (hashValue) .Replace (" - "," "). ToLower()' pero aún no puedo obtener este código C# para generar el mismo hash que los demás lenguajes. – jessegavin

+6

Súper tarde pero 'string.Join (" ", Array.ConvertAll (hashValue, b => b.ToString (" x2 "))) debería funcionar. –

25

una extensión a Vimvq1987's answer:

return hashValue.ToString(); no produce la salida que desea/necesitar. Debe convertir los bytes en la matriz hashValue a su representación de cadena hexadecimal.
puede ser tan simple como return BitConverter.toString(hashValue); (imprime letras mayúsculas AF) o si se quiere que sea un poco más complejo:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 
using System.IO; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     public static string Encode(string input, byte[] key) 
     { 
      HMACSHA1 myhmacsha1 = new HMACSHA1(key); 
      byte[] byteArray = Encoding.ASCII.GetBytes(input); 
      MemoryStream stream = new MemoryStream(byteArray); 
      return myhmacsha1.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}",e), s => s); 
     } 


     static void Main(string[] args) 
     { 
      byte[] key = Encoding.ASCII.GetBytes("abcdefghijklmnopqrstuvwxyz"); 
      string input = ""; 
      foreach (string s in new string[] { "Marry", " had", " a", " little", " lamb" }) 
      { 
       input += s; 
       System.Console.WriteLine(Encode(input, key)); 
      } 
      return; 
     } 
    } 
} 

que imprime

3545e064fb59bc4bfc02b6e1c3d4925c898aa504 
3249f4c8468d4d67f465937da05b809eaff22fdb 
87baaadf5d096677f944015e53d283834eb1e943 
6325376820c29a09e3ab30db000033aa71d6927d 
54579b0146e2476595381d837ee38863be358213 

y consigo el mismo resultado exacto para

<?php 
$secretKey = 'abcdefghijklmnopqrstuvwxyz'; 

$signatureString = ''; 
foreach(array('Marry',' had',' a',' little',' lamb') as $s) { 
    $signatureString .= $s; 
    echo hash_hmac('sha1', $signatureString, $secretKey, false), "\n"; 
} 

edición: Dmitriy Nemykin sugirió la siguiente edición

que fue rechazada. Pero como James ya señaló en un comentario a esta respuesta, al menos el using statementes un buen punto.

+0

'myhmacsha1.ComputeHash (stream) .Agregado' da un error: ese Agregado no se define para System.Array. ¿Me estoy perdiendo algo? –

+0

Depende de la versión de .net que esté utilizando, consulte http://msdn.microsoft.com/en-us/library/bb548651%28v=VS.90%29.aspx – VolkerK

+0

ho. esa es la cosa. Gracias. –

15

Este sitio tiene algunos bastante buenos ejemplos distintos idiomas: http://jokecamp.wordpress.com/2012/10/21/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/

El C# aplicación en el momento de la escritura es:

private string CreateToken(string message, string secret) 
{ 
secret = secret ?? ""; 
var encoding = new System.Text.ASCIIEncoding(); 
byte[] keyByte = encoding.GetBytes(secret); 
byte[] messageBytes = encoding.GetBytes(message); 
using (var hmacsha256 = new HMACSHA256(keyByte)) 
{ 
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes); 
return Convert.ToBase64String(hashmessage); 
} 
}