When Facebook sends real-time updates, they include a X-Hub-Signature in the HTTP header. According to their documentation (http://developers.facebook.com/docs/api/realtime), they're using SHA1 and the application secret as the key. I tried to verify the signature like this:
public void MyAction() {
string signature = request.Headers["X-Hub-Signature"];
request.InputStream.Position = 0;
StreamReader reader = new StreamReader(request.InputStream);
string json = reader.ReadToEnd();
var hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(json), UTF8Encoding.UTF8.GetBytes("MySecret"));
var hmacBase64 = ToUrlBase64String(hmac);
bool isValid = signature.Split('=')开发者_如何学Python[1] == hmacBase64;
}
private static byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody) {
using (var hmacAlgorithm = new System.Security.Cryptography.HMACSHA1(keyBody)) {
hmacAlgorithm.ComputeHash(dataToSign);
return hmacAlgorithm.Hash;
}
}
private static string ToUrlBase64String(byte[] Input) {
return Convert.ToBase64String(Input).Replace("=", String.Empty)
.Replace('+', '-')
.Replace('/', '_');
}
But I can't seem to get this to ever validate. Any thoughts on what I'm doing wrong?
Thanks in advance.
In case someone will need this information:
What Kelvin offered might work, but it seems very cumbersome. All you need is instead of using the ToUrlBase64String function just use the ConvertToHexadecimal function.
See fully updated code below:
public void MyAction() {
string signature = request.Headers["X-Hub-Signature"];
request.InputStream.Position = 0;
StreamReader reader = new StreamReader(request.InputStream);
string json = reader.ReadToEnd();
var hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(json), UTF8Encoding.UTF8.GetBytes("MySecret"));
var hmacHex = ConvertToHexadecimal(hmac);
bool isValid = signature.Split('=')[1] == hmacHex ;
}
private static byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody) {
using (var hmacAlgorithm = new System.Security.Cryptography.HMACSHA1(keyBody)) {
return hmacAlgorithm.ComputeHash(dataToSign);
}
}
private static string ConvertToHexadecimal(IEnumerable<byte> bytes)
{
var builder = new StringBuilder();
foreach (var b in bytes)
{
builder.Append(b.ToString("x2"));
}
return builder.ToString();
}
The code below will resolve the problem for you:
public String hmacSha1(String keyString, byte[] in) throws GeneralSecurityException {
Mac hmac = Mac.getInstance("HmacSHA1");
int keySize = keyString.length();
byte[] keyBytes = new byte[keySize];
for (int i = 0; i < keyString.length(); i++) {
keyBytes[i] = (byte) keyString.charAt(i);
}
Key key = new SecretKeySpec(keyBytes, "HmacSHA1");
hmac.init(key);
hmac.update(in);
byte[] bb = hmac.doFinal();
StringBuilder v = new StringBuilder();
for (int i = 0; i < bb.length; i++) {
int ch = bb[i];
int d1 = (ch >> 4) & 0xf;
int d2 = (ch) & 0xf;
if (d1 < 10) {
v.append((char) ('0' + d1));
} else {
v.append((char) ('a' + d1 - 10));
}
if (d2 < 10) {
v.append((char) ('0' + d2));
} else {
v.append((char) ('a' + d2 - 10));
}
}
return v.toString();
}
public String callback(HttpServletRequest request) throws IOException {
InputStream in = request.getInputStream();
StringBuilder builder = new StringBuilder();
byte[] buffer = new byte[1024];
while (in.read(buffer) > 0) {
builder.append(new String(buffer));
}
String signature = request.getHeader("X-Hub-Signature");
try {
String signed = subscriptionService.hmacSha1("YOUR_SECRET", builder.toString().getBytes());
if (signature.startsWith("sha1=") && signature.substring(4).equals(signed)) {
// process the update
....
}
} catch (GeneralSecurityException ex) {
log.warn(ex.getMessage());
}
return null;
}
精彩评论