問題描述
我想我會嘗試將新的簽名請求邏輯添加到我的 facebook 畫布應(yīng)用程序中,以使這對我自己輕松"我去了 GitHub 上的 facebook PHP sdk 并查看了 單元測試.
我的實際問題是我無法讓請求中包含的哈希與我使用應(yīng)用程序秘密計算的哈希以及請求中發(fā)送的數(shù)據(jù)相匹配.
Facebook 的身份驗證頁面中描述了這是如何工作的.
<預(yù)> <代碼>私人字符串VALID_SIGNED_REQUEST = ZcZocIFknCpcTLhwsRwwH5nL6oq7OmKWJx41xRTi59E.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOiIxMjczMzU5NjAwIiwib2F1dGhfdG9rZW4iOiIyNTQ3NTIwNzMxNTJ8Mi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODV8dUk3R3dybUJVZWQ4c2VaWjA1SmJkekdGVXBrLiIsInNlc3Npb25fa2V5IjoiMi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODUiLCJ1c2VyX2lkIjoiMTY3Nzg0NjM4NSJ9";私有字符串 NON_TOSSED_SIGNED_REQUEST = "laEjO-az9kzgFOULdy1G7EyaP6tMQEsbFIDrB1RUamE.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiJ9";公共無效簽名請求示例(){var 編碼 = 新的 UTF8Encoding();字符串 ApplicationSecret = "904270b68a2cc3d54485323652da4d14";字符串 SignedRequest = VALID_SIGNED_REQUEST;字符串 ExpectedSignature = SignedRequest.Substring(0, SignedRequest.IndexOf('.'));字符串有效負(fù)載 = SignedRequest.Substring(SignedRequest.IndexOf('.') + 1);//返回 &帶簽名的第四個byte[] ActualSignature = FromUrlBase64String(ExpectedSignature);字符串 TestSignature = ToUrlBase64String(ActualSignature);//返回 &數(shù)據(jù)byte[] ActualPayload = FromUrlBase64String(Payload);字符串 Json = Encoding.GetString(ActualPayload);字符串 TestPayload = ToUrlBase64String(ActualPayload);//嘗試獲取相同的哈希var Hmac = SignWithHMAC(ActualPayload, Encoding.GetBytes(ApplicationSecret));var HmacBase64 = ToUrlBase64String(Hmac);var HmacHex = BytesToHex(Hmac);if (HmacBase64 != ExpectedSignature){//耶}別的{//噓}}私有靜態(tài)字符串 BytesToHex(byte[] 輸入){StringBuilder sb = new StringBuilder();foreach(輸入中的字節(jié) b){sb.Append(string.Format("{0:x2}", b));}返回 sb.ToString();}私有字符串 ToUrlBase64String(byte[] 輸入){return Convert.ToBase64String(Input).Replace("=", String.Empty).Replace('+', '-').Replace('/', '_');}//http://tools.ietf.org/html/rfc4648#section-5私有字節(jié) [] FromUrlBase64String(字符串 Base64UrlSafe){Base64UrlSafe = Base64UrlSafe.PadRight(Base64UrlSafe.Length + (4 - Base64UrlSafe.Length % 4) % 4, '=');Base64UrlSafe = Base64UrlSafe.Replace('-', '+').Replace('_', '/');返回 Convert.FromBase64String(Base64UrlSafe);}私有 byte[] SignWithHMAC(byte[] dataToSign, byte[] keyBody){使用 (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.關(guān)閉();byte[] hashResult = hmac.Hash;*/返回hmac.Hash;}}公共字符串 Base64ToHex(字符串輸入){StringBuilder sb = new StringBuilder();byte[] inputBytes = Convert.FromBase64String(input);foreach(輸入字節(jié)中的字節(jié) b){sb.Append(string.Format("{0:x2}", b));}返回 sb.ToString();}感謝下面的 Rasmus 的回答,為了幫助其他人,這里是更新的(清理代碼):
///PHPSDK 單元測試中的簽名請求變量示例私人字符串VALID_SIGNED_REQUEST = ZcZocIFknCpcTLhwsRwwH5nL6oq7OmKWJx41xRTi59E.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOiIxMjczMzU5NjAwIiwib2F1dGhfdG9rZW4iOiIyNTQ3NTIwNzMxNTJ8Mi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODV8dUk3R3dybUJVZWQ4c2VaWjA1SmJkekdGVXBrLiIsInNlc3Npb25fa2V5IjoiMi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODUiLCJ1c2VyX2lkIjoiMTY3Nzg0NjM4NSJ9";公共布爾 ValidateSignedRequest(){字符串 applicationSecret = "904270b68a2cc3d54485323652da4d14";字符串[] 簽名請求 = VALID_SIGNED_REQUEST.Split('.');字符串預(yù)期簽名=簽名請求[0];字符串有效負(fù)載 = 簽名請求 [1];//嘗試獲取相同的哈希var Hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(payload), UTF8Encoding.UTF8.GetBytes(applicationSecret));var HmacBase64 = ToUrlBase64String(Hmac);返回(HmacBase64 == 預(yù)期簽名);}私有字符串 ToUrlBase64String(byte[] 輸入){返回 Convert.ToBase64String(Input).Replace("=", String.Empty).替換('+', '-').代替('/', '_');}私有 byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody){使用 (var hmacAlgorithm = new HMACSHA256(keyBody)){hmacAlgorithm.ComputeHash(dataToSign);返回 hmacAlgorithm.Hash;}}
在計算 HMAC 之前,您不應(yīng)該對有效負(fù)載進行 base64 解碼.
使用這一行:
var Hmac = SignWithHMAC(Encoding.GetBytes(Payload), Encoding.GetBytes(ApplicationSecret));
它應(yīng)該可以工作.
還有幾點建議:
- 不要擺弄
Substring()
和IndexOf()
嘗試使用String.Split()
- 您已將 YAY 和 BOO 評論調(diào)換了
- 如果您遵循以小寫開頭的局部變量名稱的通用規(guī)則(例如:
var applicationSecret = "...";
),則 C# 代碼更具可讀性.
I thought I would try and get the new Signed Request logic added to my facebook canvas application, to make this "easy" on myself I went to the facebook PHP sdk over at GitHub and took a look at the unit tests.
My actual problem is that I cannot get the hash included in the request to match the hash I calculate using the application secret, and the data sent within the request.
How this is meant to work is described at 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();
}
Answer thanks to Rasmus below, to assist anyone else here is the updated (cleaned up 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;
}
}
You are not supposed to base64-decode the payload before calculating the HMAC.
Use this line:
var Hmac = SignWithHMAC(Encoding.GetBytes(Payload), Encoding.GetBytes(ApplicationSecret));
and it should work.
A few more pointers:
- Instead of fiddling with
Substring()
andIndexOf()
try usingString.Split()
- You have switched the YAY and BOO comments around
- C# code is more readable if you follow the common rule of starting the names of local variables with lowercase (like this:
var applicationSecret = "...";
)
這篇關(guān)于如何在 C# 中獲得與 PHP 單元測試相同的 HMAC256 結(jié)果?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!