I'm using Play framework to develop consumer for Instagram real-time API. But still could not perform x-hub-signature verification properly. So, how can we perform Instagram x-hub-signature verification using Java and Play framework?
Here is my current code:
From the Play framework, I obtain the JSON payload using this method:
public static Result receiveInstaData(){ JsonNode json = request().body().asJson(); //obtain the x-hub-signature from the header //obtain the corresponding client secret VerificationResult verificationResult = SubscriptionUtil.verifySubscriptionPostSignature( clientSecret, json.toString(), xHubSignature); if(verificationResult.isSuccess()){ //do something } }
Then inside the
SubscriptionUtil
, I perform verification using this following code:public static VerificationResult verifySubscriptionPostSignature(String clientSecret, String rawJsonData, String xHubSignature) { SecretKeySpec keySpec; keySpec = new SecretKeySpec(clientSecret.getBytes("UTF-8"), HMAC_SHA1); Mac mac; mac = Mac.getInstance(HMAC_SHA1); mac.init(keySpec); byte[] result; result = mac.doFinal(rawJsonData.getBytes("UTF-8")); String encodedResult = Hex.encodeHexString(result); return new VerificationResult(encodedResult.equals(xHubSignature), encodedResult); }
I created a standalone Python script that copies the instagram-python implementation and both of them produce the same results for the same clientSecret
and jsonString
. Maybe I should provide with raw binary data instead of String.
If let's say we need a raw binary data for JSON request, then I need to create my custom BodyParser to parse the JSON request to raw binary data[5]
References:
[1-4]http://pastebin.com/g4uuDwzn (SO doesn't allow me to post more than 2 links, so I put all the references here. The links contain the signature verification in Ruby, Python and PHP)
[5]https://groups.google.com/forum/#!msg/play-framework/YMQb6yeDH5o/jU8FD--yVPYJ
[6]My standalone python script: #! /usr/bin/env python
import sys
import hmac
import hashlib
hc_client_secret = "myclientsecret"
hc_raw_response = "[{\"subscription_id\":\"1\",\"object\":\"user\",\"object_id\":\"1234\",\"changed_aspect\":\"media\",\"time\":1297286541},{\"subscription_id\":\"2\",\"object\":\"tag\",\"object_id\":\"nofilter\",\"changed_aspect\":\"media\",\"time\":1297286541}]"
client_secret = hc_client_secret
raw_response = hc_raw_response
if len(sys.argv) != 3:
print 'Usage verify_signature <client_secret> <raw_response>.\nSince the inputs are invalid, use the hardcoded value instead!'
else:
client_secret = sys.argv[1]
raw_response = sys.argv[2]
print "client_secret = " + client_secret
print "raw_response = " + raw_response
digest = hmac.new(client_secret.encode('utf-8'), msg=raw_response.encode('utf-8'), digestmod=hashlib.sha1).hexdigest()
print digest
Finally I managed to find the solution. For the Controller in Play Framework, we need to use
BodyParser.Raw
so the we can extract the payload request as raw data, i.e. array of bytes.Here's the code for the controller in Play Framework:
And for the code for method
verifySubscriptionPostRequestSignature
inSubscriptionUtil
I implemented this solution in jInstagram, here is the link to the source code: SubscriptionUtil