2010-08-02 3 views
18

Ich dachte, ich würde versuchen, die neue Signed Request-Logik zu meiner Facebook-Canvas-Anwendung hinzuzufügen, um dies "einfach" auf mich selbst zu machen Ich ging zum Facebook PHP Sdk über GitHub und warf einen Blick auf die unit tests.Wie bekomme ich die gleichen HMAC256-Ergebnisse in C# wie in den PHP-Unit-Tests?

Mein aktuelles Problem ist, dass ich den in der Anfrage enthaltenen Hash nicht erhalten kann, um den Hash zu berechnen, den ich unter Verwendung des Anwendungsgeheimnisses und der in der Anfrage gesendeten Daten berechne.

Wie dies funktioniert, ist unter Facebook's authentication page beschrieben.

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(); 
} 

Antwort dank Rasmus unten, hier jemand helfen sonst ist die aktualisierte (bereinigt code):

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

Ich werde auf ein Glied hier gehen und sagen, dass Sie ein C sind/C++ - Programmierer, der kaum C# kennt. –

+0

Hi Steven, nicht so neu, hat mich heute in einen schlechten Platz gebracht. Dachte, die kollektiven Gedanken des Stapelüberlaufs könnten mich rausholen. – CameraSchoolDropout

+0

Meinst du nicht 'return (HmacBase64 == expectedSignature);'? Ich würde erwarten, dass die Methode true zurückgibt, wenn die Signatur korrekt war. –

Antwort

17

Sie nicht die Nutzlast auf Base64-decode sollen, bevor die HMAC Berechnung.

Verwenden Sie diese Zeile:

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

und es sollte funktionieren.

Noch ein paar Hinweise:

  • Statt mit Substring() und IndexOf() Hantieren Versuchen Sie es mit String.Split()
  • Sie haben die YAY und BOO Kommentare vertauscht
  • C# -Code ist besser lesbar, wenn Sie die gemeinsame folgen Regel zum Starten der Namen von lokalen Variablen in Kleinbuchstaben (so: var applicationSecret = "...";)
+0

Danke Rasmus - schätzen Sie Ihre Hilfe! – CameraSchoolDropout

+0

Wenn Sie das nicht alles selber machen wollen, benutzen Sie einfach Facebook.Net SDK auf Codeplex. Es wird eine unterschriebene Anfrage für Sie bearbeiten. http://facebooksdk.codeplex.com –

+0

@NathanTotten können Sie mir bitte sagen, welche Klasse.Methode für die Generierung der Hash verwenden, habe ich das Geheimnis und Schlüssel und ich brauche den Hash generiert? – Omu

0

Danke, James! Ihr Code hat mir sehr geholfen.

cdpnet, wie Newtonsoft.Json zu Ihrem Projekt hinzufügen, und es ist dann das:

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

-Kevin

+0

Keine Probleme Kevin - froh, dass es nützlich war. – CameraSchoolDropout

Verwandte Themen