2010-08-02 7 views
18

Pensé que trataría de obtener la nueva lógica de Solicitud firmada agregada a mi aplicación de lienzo de Facebook, para hacer que esto fuera "fácil" para mí misma. Fui a facebook PHP sdk en GitHub y eché un vistazo al unit tests.¿Cómo puedo obtener los mismos resultados HMAC256 en C# como en las pruebas de unidad PHP?

Mi problema real es que no puedo obtener el hash incluido en la solicitud para que coincida con el hash que calculo usando el secreto de la aplicación, y los datos enviados dentro de la solicitud.

La forma en que esto debe funcionar se describe en Facebook's authentication page.

private string VALID_SIGNED_REQUEST = "ZcZocIFknCpcTLhwsRwwH5nL6oq7OmKWJx41xRTi59E.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOiIxMjczMzU5NjAwIiwib2F1dGhfdG9rZW4iOiIyNTQ3NTIwNzMxNTJ8Mi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODV8dUk3R3dybUJVZWQ4c2VaWjA1SmJkekdGVXBrLiIsInNlc3Npb25fa2V5IjoiMi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODUiLCJ1c2VyX2lkIjoiMTY3Nzg0NjM4NSJ9"; 

private string NON_TOSSED_SIGNED_REQUEST = "laEjO-az9kzgFOUldy1G7EyaP6tMQEsbFIDrB1RUamE.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiJ9"; 

public void SignedRequestExample() 
{ 
var Encoding = new UTF8Encoding(); 

string ApplicationSecret = "904270b68a2cc3d54485323652da4d14"; 

string SignedRequest = VALID_SIGNED_REQUEST; 
string ExpectedSignature = SignedRequest.Substring(0, SignedRequest.IndexOf('.')); 
string Payload = SignedRequest.Substring(SignedRequest.IndexOf('.') + 1); 

// Back & Forth with Signature 
byte[] ActualSignature = FromUrlBase64String(ExpectedSignature); 
string TestSignature = ToUrlBase64String(ActualSignature); 

// Back & Forth With Data 
byte[] ActualPayload = FromUrlBase64String(Payload); 
string Json = Encoding.GetString(ActualPayload); 
string TestPayload = ToUrlBase64String(ActualPayload); 

// Attempt to get same hash 
var Hmac = SignWithHMAC(ActualPayload, Encoding.GetBytes(ApplicationSecret)); 
var HmacBase64 = ToUrlBase64String(Hmac);    
var HmacHex = BytesToHex(Hmac); 

if (HmacBase64 != ExpectedSignature) 
{ 
    // YAY 
} 
else 
{ 
    // BOO 
} 
} 

private static string BytesToHex(byte[] input) 
{ 
StringBuilder sb = new StringBuilder(); 

foreach (byte b in input) 
{ 
    sb.Append(string.Format("{0:x2}", b)); 
} 
return sb.ToString(); 
} 
private string ToUrlBase64String(byte[] Input) 
{ 
return Convert.ToBase64String(Input).Replace("=", String.Empty).Replace('+', '-').Replace('/', '_'); 
} 

// http://tools.ietf.org/html/rfc4648#section-5    
private byte[] FromUrlBase64String(string Base64UrlSafe) 
{ 
Base64UrlSafe = Base64UrlSafe.PadRight(Base64UrlSafe.Length + (4 - Base64UrlSafe.Length % 4) % 4, '='); 
Base64UrlSafe = Base64UrlSafe.Replace('-', '+').Replace('_', '/'); 
return Convert.FromBase64String(Base64UrlSafe); 
} 

private byte[] SignWithHMAC(byte[] dataToSign, byte[] keyBody) 
{ 
using (var hmac = new HMACSHA256(keyBody)) 
{ 
    hmac.ComputeHash(dataToSign); 
    /* 
    CryptoStream cs = new CryptoStream(System.IO.Stream.Null, hmac, CryptoStreamMode.Write); 
    cs.Write(dataToSign, 0, dataToSign.Length); 
    cs.Flush(); 
    cs.Close(); 
    byte[] hashResult = hmac.Hash; 
    */ 
    return hmac.Hash; 
} 
} 

public string Base64ToHex(string input) 
{ 
StringBuilder sb = new StringBuilder(); 
byte[] inputBytes = Convert.FromBase64String(input); 
foreach (byte b in inputBytes) 
{ 
    sb.Append(string.Format("{0:x2}", b)); 
} 
return sb.ToString(); 
} 

respuesta gracias a Rasmus a continuación, para ayudar a alguien más aquí es la (código limpiado) actualización:

/// Example signed_request variable from PHPSDK Unit Testing 
private string VALID_SIGNED_REQUEST = "ZcZocIFknCpcTLhwsRwwH5nL6oq7OmKWJx41xRTi59E.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOiIxMjczMzU5NjAwIiwib2F1dGhfdG9rZW4iOiIyNTQ3NTIwNzMxNTJ8Mi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODV8dUk3R3dybUJVZWQ4c2VaWjA1SmJkekdGVXBrLiIsInNlc3Npb25fa2V5IjoiMi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODUiLCJ1c2VyX2lkIjoiMTY3Nzg0NjM4NSJ9"; 

public bool ValidateSignedRequest() 
{    
    string applicationSecret = "904270b68a2cc3d54485323652da4d14"; 
    string[] signedRequest = VALID_SIGNED_REQUEST.Split('.');    
    string expectedSignature = signedRequest[0]; 
    string payload = signedRequest[1]; 

    // Attempt to get same hash 
    var Hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(payload), UTF8Encoding.UTF8.GetBytes(applicationSecret)); 
    var HmacBase64 = ToUrlBase64String(Hmac); 

    return (HmacBase64 == expectedSignature);   
} 


private string ToUrlBase64String(byte[] Input) 
{ 
    return Convert.ToBase64String(Input).Replace("=", String.Empty) 
             .Replace('+', '-') 
             .Replace('/', '_'); 
} 

private byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody) 
{ 
    using (var hmacAlgorithm = new HMACSHA256(keyBody)) 
    { 
     hmacAlgorithm.ComputeHash(dataToSign); 
     return hmacAlgorithm.Hash; 
    } 
} 
+1

Voy a salir a un miembro aquí y decir que eres un C/Codificador de C++ que apenas conoce C#. –

+0

Hola Steven, no tan nuevo, acabo de meterme en un mal lugar hoy. Pensé que las mentes colectivas del desbordamiento de la pila podrían sacarme. – CameraSchoolDropout

+0

¿No quieres decir 'return (HmacBase64 == expectedSignature);'? Esperaría que el método sea verdadero si la firma era correcta. –

Respuesta

17

No se supone que en base 64 de decodificación de la carga útil antes de calcular el HMAC.

uso de esta línea:

var Hmac = SignWithHMAC(Encoding.GetBytes(Payload), Encoding.GetBytes(ApplicationSecret)); 

y debería funcionar.

Unas cuantas más punteros:

  • En lugar de tocar el violín con Substring() y IndexOf() trate de usar String.Split()
  • Ha cambiado los comentarios YAY y Boo alrededor
  • C# código es más legible si se sigue la común regla de comenzar los nombres de variables locales con minúsculas (como este: var applicationSecret = "...";)
+0

Gracias Rasmus: ¡Aprecia tu ayuda! – CameraSchoolDropout

+0

Si no quiere hacer esto, use Facebook.Net SDK en Codeplex. Tratará la solicitud firmada para usted. http://facebooksdk.codeplex.com –

+0

@NathanTotten ¿podría decirme qué método de clase usar para generar el hash, tengo el secreto y la clave y necesito el hash generado? – Omu

0

Gracias, James! Tu código me ayudó mucho.

cdpnet, añadir como Newtonsoft.Json a su proyecto, y entonces es la siguiente:

 JObject UnencodedPayload = JObject.Parse(Encoding.GetString(ActualPayload)); 

-Kevin

+0

Sin problemas Kevin - me alegro de que haya sido útil. – CameraSchoolDropout

Cuestiones relacionadas