我想我会尝试将新的签名请求逻辑添加到我的 facebook canvas 应用程序中,为了让自己“轻松”完成此操作,我去了 GitHub 上的 facebook PHP sdk 并查看了 unit tests .
我的实际问题是,我无法获取请求中包含的哈希值来匹配我使用应用程序 secret 计算的哈希值,以及在请求中发送的数据。
这意味着如何工作在 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();
}
感谢下面的 Rasmus 回答,为了帮助这里的其他人,更新了(清理代码):
/// 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;
}
}
最佳答案
您不应该在计算 HMAC 之前对有效负载进行 base64 解码。
使用这一行:
var Hmac = SignWithHMAC(Encoding.GetBytes(Payload), Encoding.GetBytes(ApplicationSecret));
它应该可以工作。
还有一些提示:
- 与其摆弄
Substring()
和IndexOf()
,不如尝试使用String.Split()
- 你已经调换了 YAY 和 BOO 的评论
- 如果您遵循以小写开头的局部变量名称的通用规则(例如:
var applicationSecret = "...";
),C# 代码将更具可读性
关于c# - 如何在 C# 中获得与在 PHP 单元测试中相同的 HMAC256 结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3385593/