I am trying to parse the facebook signed_request in 32 bit operating system.
The java code for parsing signed_request in 64 bit operating system works fine.
When I am using the modified code in 32 bit operating system,
it gives the following error 'Unexpected character (£) at position 0'.
Please help me to identify the issue.
if (request.getParameter("signed_request") != null)
{
signed_request = request.getParameter("signed_request");
//out.print("<br>signed_request: "+signed_request);
try
{
System.out.println("signed_request"+signed_request);
System.out.println("signed_request1");
fb_data = parse_signed_request(signed_request, secretKey);
}
catch(Exception e)
{
System.out.println("error"+e);
}
}
public static Map parse_signed_request(String input, String secret) throws Exception {
return parse_signed_request(input, secret, 3600);
}
public static Map parse_signed_request(String input, String secret, int max_age) throws Exception {
String[] split = input.split("[.]", 2);
String encoded_sig = split[0];
String encoded_envelope = split[1];
JSONParser parser = new JSONParser();
Map envelope = (Map) parser.parse(new String(base32_url_decode(encoded_envelope)));
String algorithm = (String) envelope.get("algorithm");
if (!algorithm.equals("HMAC-SHA256")) {
throw new Exception("Invalid request. (Unsupported algorithm.)");
}
if (((Long) envelope.get("issued_at")) < System.currentTimeMillis() / 1000 - max_age) {
throw new Exception("Invalid request. (Too old.)");
}
byte[] key = secret.getBytes();
SecretKey hmacKey = new SecretKeySpec(key, "HMACSHA256");
Mac mac = Mac.getInstance("HMACSHA256");
mac.init(hmacKey);
byte[] digest = mac.doFinal(encoded_envelope.getBytes());
if (!Arrays.equals(base32_url_decode(encoded_sig), digest)) {
throw new Exception("Invalid request. (Invalid signature.)");
}
return envelope;
}
Use base64 method to decode the JSON object. No need to modify it to base32 as it has no co-relation with the O.S. architecture.
Correcting the flaw:
Map envelope = (Map) parser.parse(new String(base64_url_decode(encoded_envelope)));
Related
so learning still but i'm getting an Error 401 unauthorised back from the code below. I know that the OAuth header works as it works in postman so i'm assuming there is a problem with the POST request / Auth header? Any ideas?
//set timestamp
Long timestamp = System.currentTimeMillis()/1000;
//set nonce ***** call from main system*************************************************************
String aString = randomAlphaNumeric(11);
// other stuff
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders headers = new HttpHeaders();
String url = "aURL";
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// String auth = Base64.getEncoder().encodeToString(credentials.getBytes());
List<NameValuePair> oauthHeaders = new ArrayList<>(9);
oauthHeaders.add(new BasicNameValuePair("oauth_consumer_key", "aKey"));
oauthHeaders.add(new BasicNameValuePair("oauth_nonce", aString));
oauthHeaders.add(new BasicNameValuePair("oauth_timestamp", String.valueOf(timestamp)));
oauthHeaders.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
oauthHeaders.add(new BasicNameValuePair("oauth_version", "1.0"));
//generate signature
//encode
String encodedURL = encode(oauthHeaders.toString());
System.out.println("encoded URL:" +encodedURL);
//form base string
String baseString = "POST&"+encode(url).toString()+encodedURL;
System.out.println("Base String: "+baseString);
//form signature
byte[] byteHMAC = null;
try {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec spec;
if (null == secretKey) {
String signingKey = encode(secretKey) + '&';
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
} else {
String signingKey = encode(secretKey) + '&' + encode(secretKey);
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
}
mac.init(spec);
byteHMAC = mac.doFinal(baseString.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
String signature = new BASE64Encoder().encode(byteHMAC);
System.out.println("oauth signature: "+signature);
//set signature to params
oauthHeaders.add(new BasicNameValuePair("oauth_signature", signature));
String test = "OAuth "+oauthHeaders.toString();
headers.set("Authorization", test);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("Name",name.toString());
map.add("Region",region.toString());
HttpEntity<MultiValueMap<String, String>> requestEntity= new HttpEntity<MultiValueMap<String, String>>(headers, map);
System.out.println(requestEntity);
ResponseEntity<String> response= restTemplate.exchange(url ,HttpMethod.POST, requestEntity, String.class);
System.out.println(response.toString());
HttpStatus status = response.getStatusCode();
status.toString();
if(status.equals("200")){
Notification.show("Employer" + name +" added successfully");
}
else{
Notification.show("Unsuccessful, error: "+status);
}
}
removed the URL and consumer key / signature for obvious reasons.
The following System out prints might help as well:
encoded params:
%5Boauth_consumer_key%3aKey%2C%20oauth_nonce%3DWZU8H1B5JA6%2C%20oauth_timestamp%3D1511621759%2C%20oauth_signature_method%3DHMAC-SHA1%2C%20oauth_version%3D1.0%5D
Base String: POST&https%3A%2F%2Fapi.test.payrun.io%2FEmployer%5Boauth_consumer_key%3aKey%2C%20oauth_nonce%3DWZU8H1B5JA6%2C%20oauth_timestamp%3D1511621759%2C%20oauth_signature_method%3DHMAC-SHA1%2C%20oauth_version%3D1.0%5D
oauth signature: DlRJGSzgRIItzz+LzMbgnIfbOqU=
The value of oauth_signature is wrong. You are using asignature as the value of oauth_signature, but you have to compute the correct value for your request and set it to oauth_signature. If the value of oauth_signature is wrong, the server will reject your request. See "3.4. Signature" in RFC 5849 (The OAuth 1.0 Protocol) for details.
For anyone who wants to make this work then please see below for a complete OAuth Generator example :):
public class oAuthGenerator {
private String httpMethod;
private String params;
private String url;
//Required for percent encoding
private static final String ENC = "ASCII";
//Required for nonce
private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
//As provided by Payrun.io
private static final String secretKey = "aSecretKey";
//As provided by Payrun.io
private static final String consumerKey ="aConsumerKey";
private List veriList;
//Constructor for setting signature base string values.
//#Param url needs to be percent encoded
//#Param params needs to be percent encoded, this is done #Method generateOAuth()
public oAuthGenerator(String httpMethod,String url )throws Exception {
veriList= new ArrayList<String>();
veriList.add("POST");
veriList.add("GET");
veriList.add("PUT");
veriList.add("DELETE");
if (veriList.contains(httpMethod)){
this.httpMethod = httpMethod+"&";}
this.url = URLEncoder.encode(url,ENC)+"&";
}
//A method used to generate the OAuth Authorization header
//#Method randomAlphaNumeric() calls internal method using instance variable ALPHA_NUMERIC_STRING
//#Method getSignature() returns String HMACSHA1 > Base64 encoded value of httpMethod,url,params
public String generateOAuth()throws Exception{
//Set timestamp as seconds from 01-01-1970
Timestamp timestamp = new Timestamp(System.currentTimeMillis()/1000);
Long aTimestamp = timestamp.getTime();
//Set nonce which is a 10 digit random, non repeating alpha-numeric value
String aNonce = randomAlphaNumeric(10);
//Normalize and form param string
String normalizedParams = "oauth_consumer_key="+consumerKey+"&"+"oauth_nonce="+aNonce+"&"+"oauth_signature_method="+"HMAC-SHA1"+"&"+"oauth_timestamp="+ aTimestamp.toString()+"&"+"oauth_version="+"1.0";
//Percent encoded params
params = URLEncoder.encode(normalizedParams,ENC);
//Set signature variable
String signature = getSignature();
//place into required format
String oAuthResult = "OAuth "+"oauth_version="+"\"1.0\""+","+"oauth_consumer_key="+"\"" + consumerKey + "\""+","+"oauth_signature_method="+"\"HMAC-SHA1\""+","+"oauth_timestamp="+"\""+aTimestamp+"\""+","+"oauth_nonce="+"\""+aNonce+"\""+","+"oauth_signature="+"\""+signature+"\"";
return oAuthResult;
}
// A method designed to return a hashed and base64 encoded value.
//#Param aString holds HMAC-SHA1 and Base 64 encoded value of variables httpMethod,url,params
//#Param result holds percent encoded value of aString
private String getSignature()
throws Exception {
//form base string
StringBuilder base = new StringBuilder();
base.append(httpMethod);
base.append(url);
base.append(params);
//Set SecretKey of variable secretKey using HMAC-SHA1 algorithm
SecretKey signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// Compute the hmac on input data bytes, then encode to Base64
String aString = Base64.getEncoder().encodeToString(mac.doFinal(base.toString().getBytes(ENC))).trim();
//Percent encoded the Base64 value
String result = URLEncoder.encode(aString, ENC);
return new String(result);
}
//Required for nonce, returns a random alpha numeric value by using variable ALPHA_NUMERIC_STRING
private static String randomAlphaNumeric(int count) {
StringBuilder builder = new StringBuilder();
while (count-- != 0) {
int character = (int)(Math.random()*ALPHA_NUMERIC_STRING.length());
builder.append(ALPHA_NUMERIC_STRING.charAt(character));
}
return builder.toString();
}
}
Here is the code that calls three functions from my custom java class:
QAndroidJniObject datafile = QAndroidJniObject::fromString(path);
QAndroidJniObject password = QAndroidJniObject::fromString("asimpletest");
QAndroidJniObject::callStaticObjectMethod("org/qcolocrypt/AESCrypt",
"AESCryptInit",
"(Ljava/lang/String;Ljava/lang/String;)V;",
password.object<jstring>(),
datafile.object<jstring>());
QAndroidJniObject decrypted_data = QAndroidJniObject::callStaticObjectMethod("org/qcolocrypt/AESCrypt",
"decrypt",
"()Ljava/lang/String;");
QAndroidJniObject fname = QAndroidJniObject::callStaticObjectMethod("org/qcolocrypt/AESCrypt",
"getFilename",
"()Ljava/lang/String;");
QAndroidJniObject status = QAndroidJniObject::callStaticObjectMethod("org/qcolocrypt/AESCrypt",
"getStatus",
"()Ljava/lang/String;");
Here is the java code for three of these functions:
The non working one:
public static void AESCryptInit (String passwd, String datafile){
// Initializing variables.
rawdata = null;
status = "";
fileName = datafile;
Log.i("[QCOLOCRYPT]","The filename is " + datafile);
// Transforming the passwd to 16 bytes.
try {
MessageDigest digester = MessageDigest.getInstance("MD5");
InputStream in = new ByteArrayInputStream(Charset.forName(encoding).encode(passwd).array());
byte[] buffer = new byte[NCHARS];
int byteCount;
while ((byteCount = in.read(buffer)) > 0) {
digester.update(buffer, 0, byteCount);
}
keyBytes = digester.digest();
}
catch(Exception e){
status = "Error in key generation: " + e.toString();
}
// Initilizing the crypto engine
try {
cipher = Cipher.getInstance(algorithm);
}
catch(Exception e){
status = "Error in intialization: " + e.toString();
}
secretKeySpec = new SecretKeySpec(keyBytes, "AES");
ivParameterSpec = new IvParameterSpec(keyBytes);
}
And two that work
// Getting status
public static String getStatus(){return status;}
public static String getFilename() {
Log.i("[QCOLOCRYPT]","Getting the file name");
return "The Filename is: " + fileName;
}
The non-working function is not being called because its debug message is NOT being printed, while I get the log message AND the return values of the other two. Logcat does not seem to show any errors, so I'm at a loss. Am I calling it wrong?
Ok, so this isn't exactly an answer. It's more of a workaround, I got the function to work, but the only way I could do it is by returning a String. I've tried with an Integer using (Arguments)I; as the signature but I had the same problem. I modified the Java function to return a status string, changed the signature to reflect that, and everything worked perfectly. Though its weird.
I was trying to load the keystore from a p12 file , the behaviour is highly inconsistent where in the keystore.aliases().nextElement() gives proper alias once and displaying CN in other cases. In the later case I am not able to store the keystore (using keystore.store) and the output stream is empty .
Below is the code snippet. Let me know if I overlooked anything.
// the main code where i am facing issue
private byte[] generateKeyStoreData(String appName, Map<String, String> credentials)
throws ApplicationException {
try {
if (!credentials.containsKey(KEYSTORE)) {
throw new NullPointerException("No keystore provided");
}
if (!credentials.containsKey(KEYSTORE_PASSWORD)) {
throw new NullPointerException("No keystore password provided");
}
String keystoreStr = credentials.get(KEYSTORE);
char[] keystorePass = credentials.get(KEYSTORE_PASSWORD).toCharArray();
// I have printed the base64 string here and tried loading inside a standalone code
and it is working. The method is below
InputStream keystoreIs = base64stringToInputStream(keystoreStr);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(keystoreIs, keystorePass);
// I printed the keyStore.aliases().nextElement() which returns correct alias "omss"
// but returns CN in cases where it fails.
ByteArrayOutputStream keyStoreOut = new ByteArrayOutputStream();
keyStore.store(keyStoreOut, keystorePass);
// I printed the keystoreOut.toByteArray() and it is empty in failing cases
return keyStoreOut.toByteArray();
} catch (Exception e) {
// exception
}
}
// the conversion code from base64string to bytearrayinputstream
private InputStream base64stringToInputStream(String str) {
byte[] ba = DatatypeConverter.parseBase64Binary(str);
return new ByteArrayInputStream(ba);
}
//--------------------------------------------------------------------
// Below is api which calls the generateKeystore
//-------------------------------------------------------------------
// We get the inputstream from the uploaded p12 file and the below api is called
public void createKeystore(InputStream certFile,
char[] password) {
Util.nullCheck(certFile,
"Certificate File cannot be null or empty");
Util.nullCheck(password, "Password Cannot be null");
try {
// the method is below
byte[] raw = toByteArray(certFile);
// converting to base64 string
String base64encodedString = DatatypeConverter
.printBase64Binary(raw);
//....... we create a map of keystore string and password
// and the call is made to generateKeystore method above
}
catch(Exception e){
}
// the inputstream is converted to bytearray inputstream
private static byte[] toByteArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int reads = is.read();
while (reads != -1) {
baos.write(reads);
reads = is.read();
}
return baos.toByteArray();
}
Looks like the keystore.load() is not using "SunJSSE" as the default keystore provider in my j2ee environment rather it was using oraclepki provider. Now that i am loading keystore.load(Is,"SunJSSE") it is able to load properly.
I am not an expert with REST APIs and I am running into issues when trying to makes calls to the Duo 2 factor authentication API: https://www.duosecurity.com/docs/authapi#api-details
It looks really straight forward, but I think I am missing something, I have been working on this for 2 days without success.
I am using Jersey, which gives me the following error:
java.lang.IllegalArgumentException: Illegal character(s) in message header value: Basic RElGUE1MSVQyMU40OUhORURL[...]YTYzOQ==
(I have shortened the key in the above line)
The API uses HTTP Basic Authentication to authenticate requests.
I did follow the instructions in the doc for the authentication. I did generate the HTTP Password as an HMAC signature of the request. I also did build the signature as explained in the doc, by first building an ASCII string from my request, then by concatenating the components with line feeds and computing the HMAC-SHA1 of the canonical representation, then encoding username:hmac in Base64.
I think I might be misunderstanding something on the encoding part or not doing something properly.
Here is part of my code:
public Enroll enroll(String username){
HashMap<String, String> formData = new HashMap<String, String>();
formData.put("username", username);
String date = generateDate();
String signature = constructSignature("POST", "/auth/v2/enroll", formData);
String authValue = generateAuthValue(secretKey, signature);
Enroll response = service.path("auth").path("v2").path("enroll").header("Date", date)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", authValue).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
.accept(MediaType.APPLICATION_JSON_TYPE).post(Enroll.class, formData);
return response;
}
public String generateAuthValue(String secretKey, String signature){
String hmacValue = calcShaHash(signature, integrationKey, secretKey);
return hmacValue;
}
private String constructSignature(String method, String path, HashMap<String, String> params){
String date = generateDate();
String lineFeed = "\n";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(date);
stringBuilder.append(lineFeed);
stringBuilder.append(method);
stringBuilder.append(lineFeed);
stringBuilder.append(apiHostName);
stringBuilder.append(lineFeed);
stringBuilder.append(path);
stringBuilder.append(lineFeed);
stringBuilder = urlEncodeParameters(params, stringBuilder);
return stringBuilder.toString();
}
private StringBuilder urlEncodeParameters(HashMap<String, String> params, StringBuilder stringBuilder){
try{
for (Map.Entry<String, String> entry : params.entrySet()){
stringBuilder.append(URLEncoder.encode(entry.getKey().toString(), "UTF-8"));
stringBuilder.append("=");
stringBuilder.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
stringBuilder.append("\n");
//signature.concat(encoded);
}
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
return stringBuilder;
}
public static String calcShaHash(String data, String integrationKey, String secretKey){
String HMAC_SHA1_ALGORITHM = "HmacSHA1";
StringBuilder result = new StringBuilder();
try{
Key signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(integrationKey);
stringBuilder.append(":");
stringBuilder.append(Hex.encodeHexString(rawHmac).toString());
byte[] byteAuthorizationValue = stringBuilder.toString().getBytes("UTF-8");
result.append("Basic ");
result.append(Base64.encode(byteAuthorizationValue).toString());
}catch (Exception e){
e.printStackTrace();
}
return result.toString();
}
private String generateDate(){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZZ");
String formattedDate = sdf.format(date);
return formattedDate;
}
Make sure that your requestXML or any header value does not have any illegal characters and replace it like...
aXmlRequest=aXmlRequest.replaceAll("\n", "");
I guess it is the same problem I am facing at the moment.
The illegal character is a new-line-character at the end of the auth header.
Take a look at Java: fetch URL with HTTPBasic Authentication for more information.
I use the org.apache.commons.codec.binary.Base64 class from the commons-codec library to get Base64 encryption. I'm not sure but maybe it solves your problem.
Best regards,
Felix
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:
#BodyParser.Of(BodyParser.Raw.class)
public static Result receiveRawInstaData(){
Map<String, String[]> headers = request().headers();
RawBuffer jsonRaw = request().body().asRaw();
if(jsonRaw == null){
logger.warn("jsonRaw is null. Something is wrong with the payload");
return badRequest("Expecting serializable raw data");
}
String[] xHubSignature = headers.get(InstaSubscriptionUtils.HTTP_HEADER_X_HUB_SIGNATURE);
if(xHubSignature == null){
logger.error("Invalid POST. It does not contain {} in its header", InstaSubscriptionUtils.HTTP_HEADER_X_HUB_SIGNATURE);
return badRequest("You are not Instagram!\n");
}
String json;
byte[] jsonRawBytes;
jsonRawBytes = jsonRaw.asBytes();
json = new String(jsonRawBytes, StandardCharsets.UTF_8);
try {
String clientSecret = InstaSubscriptionUtils.getClientSecret(1);
VerificationResult verificationResult = SubscriptionUtil.verifySubscriptionPostRequestSignature
(clientSecret,jsonRawBytes, xHubSignature[0]);
if(verificationResult.isSuccess()){
logger.debug("Signature matches!. Received signature: {}, calculated signature: {}", xHubSignature[0], verificationResult.getCalculatedSignature());
}else{
logger.error("Signature doesn't match. Received signature: {}, calculated signature: {}", xHubSignature[0], verificationResult.getCalculatedSignature());
return badRequest("Signature does not match!\n");
}
} catch (InstagramException e) {
logger.error("Instagram exception.", e);
return internalServerError("Internal server error. We will attend to this problem ASAP!");
}
logger.debug("Received xHubSignature: {}", xHubSignature[0]);
logger.info("Sucessfully received json data: {}", json);
return ok("OK!");
}
And for the code for method verifySubscriptionPostRequestSignature in SubscriptionUtil
public static VerificationResult verifySubscriptionPostRequestSignature(String clientSecret, byte[] rawJsonData, String xHubSignature) throws InstagramException{
SecretKeySpec keySpec;
keySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8), HMAC_SHA1);
Mac mac;
try {
mac = Mac.getInstance(HMAC_SHA1);
mac.init(keySpec);
byte[] result = mac.doFinal(rawJsonData);
String encodedResult = Hex.encodeHexString(result);
return new VerificationResult(encodedResult.equals(xHubSignature), encodedResult);
} catch (NoSuchAlgorithmException e) {
throw new InstagramException("Invalid algorithm name!", e);
} catch (InvalidKeyException e){
throw new InstagramException("Invalid key: " + clientSecret, e);
}
}
I implemented this solution in jInstagram, here is the link to the source code: SubscriptionUtil