Chaosphere2112
Chaosphere2112

Reputation: 674

HMAC SHA-256 and Facebook signed_request

So, I'm working on validating Facebook's signed_request using Java. Unfortunately, I keep running into issues with the validation process. I've looked at this documentation, and emulated their algorithm, to no success. I've also followed this tutorial, and continued to come up with my calculated signature being different from the one that Facebook sent.

Or at least, that was what String.equals() was telling me.

So I decided to poke at it some more.

I set it up to iterate over the bytes in my calculated signature and the provided ones. Low and behold, the first 32 bytes of my signature matched theirs exactly. It was just missing another 400+ bytes of data.

At that point, I decided I should probably get a better notion of what the heck was going on. I looked up SHA-256, and found that, indeed, it only creates 32 bytes of information. So then I'm left with over 400 bytes of data which Facebook claims was generated using the HMAC SHA-256 algorithm. I thought I should compare the maximum length of SHA-256 with the length of the data I'm hashing, but that just showed that there was way, way, way a lot of room to spare (Message size: 575 bytes; Maximum Size: 2.305843009213694 x 10^18 bytes).

Is Facebook making shit up? Or am I missing something?

edit

This is the function I use to hash the data. I pass in my facebook secret code (for key), and the base64url encoded JSON object (for data). It consistently returns a byte array of length 32 with data that matches the first 32 bytes of the signature provided by facebook.

private byte[] hmacSHA256(String data, String key) throws Exception {
    SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(secretKey);
    mac.update(data.getBytes("UTF-8"));
    byte[] hmacData = mac.doFinal();
    return hmacData;
}

Upvotes: 2

Views: 5833

Answers (2)

Luciano
Luciano

Reputation: 96

Since I also came in here looking for an answer to a similar problem. This is the code that works for me:

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

private JSONObject parseFBSignedRequest(String signedRequest, String secret) throws UnsupportedEncodingException, Exception {
    //split request into signature and data
    String[] signedRequests = signedRequest.split("\\.", 2);
    //parse signature
    String sig = signedRequests[0];

    //parse data and convert to json object
    String data = signedRequests[1];

    //I assumed it is UTF8
    JSONObject jsonData = new JSONObject(new String(Base64.decodeBase64(data), "UTF-8"));
    //check signature algorithm
    if(!jsonData.getString("algorithm").equals("HMAC-SHA256")) {
        //unknown algorithm is used
        return null;
    }

    //check if data is signed correctly
    if(!hmacSHA256(signedRequests[1], secret).equals(sig)) {
        //signature is not correct, possibly the data was tampered with
        return null;
    }
    return jsonData;
}

//HmacSHA256 implementation 
private String hmacSHA256(String data, String key) throws Exception {
    SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(secretKey);
    byte[] hmacData = mac.doFinal(data.getBytes("UTF-8"));
    return new String(Base64.encodeBase64URLSafe(hmacData), "UTF-8");
}

Upvotes: 6

C3roe
C3roe

Reputation: 96407

I looked up SHA-256, and found that, indeed, it only creates 32 bytes of information. So then I'm left with over 400 bytes of data which Facebook claims was generated using the HMAC SHA-256 algorithm.

The data of the signed request is not „created” using HMAC SHA-256 – it’s signed with it.

The first part of the signed request, before the dot, is the signature – the rest is the data payload. You have to hash that payload data and compare the hash your getting with the signature – these two should match, to prove the signed request is genuine.

If that doesn’t help you see clearer, show us some code please.

Upvotes: 0

Related Questions