2010-08-02 5 views
18

Je pensais que j'essaierais d'ajouter la nouvelle logique de demande signée à mon application de canevas facebook, pour que cela soit "facile" sur moi-même. Je suis allé au facebook PHP sdk chez GitHub et j'ai jeté un œil au unit tests.Comment puis-je obtenir les mêmes résultats HMAC256 en C# comme dans les tests unitaires PHP?

Mon problème réel est que je ne peux pas obtenir le hachage inclus dans la requête pour faire correspondre le hachage que je calcule en utilisant le secret d'application, et les données envoyées dans la demande.

Comment cela est censé fonctionner est décrit au 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(); 
} 

réponse grâce à Rasmus ci-dessous, pour aider quelqu'un d'autre ici est la mise à jour (Code nettoyé):

/// 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

Je vais sortir sur un membre ici et dire que vous êtes un C/C++ codeur qui connaît à peine le C#. –

+0

Salut Steven, pas si nouveau, m'a juste mis dans un mauvais endroit aujourd'hui. Pensé que les esprits collectifs de débordement de pile pourraient me sortir. – CameraSchoolDropout

+0

Ne voulez-vous pas dire 'return (HmacBase64 == expectedSignature);'? Je m'attendrais à ce que la méthode retourne vrai si la signature était correcte. –

Répondre

17

Vous n'êtes pas censé base64 décoder la charge utile avant de calculer le HMAC.

Utilisez cette ligne:

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

et cela devrait fonctionner.

Quelques autres pointeurs:

  • Au lieu de jongler avec Substring() et IndexOf() essayez d'utiliser String.Split()
  • Vous avez changé les commentaires YAY et BOO autour
  • code C# est plus lisible si vous suivez la commune règle de démarrage des noms de variables locales avec des minuscules (comme ceci: var applicationSecret = "...";)
+0

Merci Rasmus - appréciez votre aide! – CameraSchoolDropout

+0

Si vous ne voulez pas faire tout cela vous-même, utilisez simplement Facebook.SDK net sur Codeplex. Il va gérer la demande signée pour vous. http://facebooksdk.codeplex.com –

+0

@NathanTotten pourriez-vous s'il vous plaît dites-moi quel class.method à utiliser pour générer le hachage, j'ai le secret et la clé et j'ai besoin du hachage généré? – Omu

0

Merci, James! Votre code m'a beaucoup aidé.

cdpnet, ajouter comme Newtonsoft.Json à votre projet, puis il est ceci:

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

-Kevin

+0

Pas de problèmes Kevin - heureux que c'était utile. – CameraSchoolDropout

Questions connexes