Java HMAC authentication verification.

Solved
nabeelhamad
Tourist
4 0 1

Hi,

 

I was asked to request this here in this forum by Shopify technical support team.

 

The problem:

Here in this article (https://help.shopify.com/en/api/getting-started/webhooks#creating-an-endpoint-for-webhooks), under the "Verifying Webhooks" heading it says that "To verify that the request came from Shopify, compute the HMAC digest according to the following algorithm and compare it to the value in the X-Shopify-Hmac-SHA256 header". The examples given are PHP and Ruby code; but since we are using Java, we are following sample code given here https://github.com/dworznik/calculate-hmac-sha256/tree/master/src/main/java/net/bzium/shopify . The problem is that the HMAC digest that we compute is never the same as the value that comes as the X-Shopify-Hmac-SHA256 in the header. While computing the HMAC, in the article mentioned above it is said that we should use the shared secret. I also tried with the API Key but that also doesn't match the value that come in the header.

 

I have already gone through the various Java & HMAC related posts in this forum including this (which, as mentioned in the replies, suggests a hex encryption instead of base64 encryption): https://community.shopify.com/c/Shopify-APIs-SDKs/Webhook-X-Shopify-Hmac-Sha256-calculation-issue-in...

 

Also the content length (Content-Length) that comes in the header never matches the length of the post request body content.

 

Any help in this regard would be highly appreciated.

 

Thanks,

Nabeel

0 Likes
vinothraj
Shopify Partner
2 1 1

This is an accepted solution.

Hi Nabeelhamad,

 

If you have created your webhook through admin setting under notification, then you have to use the key defined in the notification end of the page after the webhook listing. You can find something like this 'All your webhooks will be signed with 5596ab8fc9524xxxxxxxxxx so you can verify their integrity.'

 

I hope this helpls,

 

Thanks,

vinoth

nabeelhamad
Tourist
4 0 1

Hi Vinoth,

 

Thanks a ton for your input; it worked. So it looks like the documentation given in the article in help.shopify.com is incorrect; it is not the shared secret. Also I noticed that we should use character encoding UTF-8 while reading the data from the HTTP request object.

 

String jsonString = IOUtils.toString(request.getInputStream(),"UTF-8"); //IOUtils from apache commons

String hmac = request.getHeader("X-Shopify-Hmac-Sha256");

String secrete = "webhooks-signature-text"; //get this from shopify admin webhook configuration page

boolean validShopifyRequest = Hmac.checkHmac(message, hmac, secret); // used Hmac from the github I mentioned above in my ticket

 

And everything should work fine!!

 

Thanks,

Nabeel

0 Likes
Pedro_MV
New Member
1 0 0

Sorry but I got lost with your code, where did the "message" variable came from?

0 Likes
nabeelhamad
Tourist
4 0 1

hi,

 

Sorry - it should be the jsonString; copy/paste error; not able to edit the above one; so adding the corrected one here:

 

String jsonString = IOUtils.toString(request.getInputStream(),"UTF-8"); //IOUtils from apache commons

String hmac = request.getHeader("X-Shopify-Hmac-Sha256");

String secrete = "webhooks-signature-text"; //get this from shopify admin webhook configuration page

boolean validShopifyRequest = Hmac.checkHmac(jsonString, hmac, secret); // used Hmac from the github I mentioned above in my ticket

 

Anyways thanks for pointing it out.

 

Best Regards,

Nabeel

0 Likes
bhavya_dev
New Member
2 0 0

Hi 

I am wondering if this process is any different when we are using Oauth and not webhooks to verify the parameters passed in every request. We are currently using the client id as the secret to verify the parameters and it does not match the hmac . Just wondering if I am doing anything wrong. 

 

This is what we are trying to do : https://shopify.dev/tutorials/authenticate-with-oauth#verification 

0 Likes
billrobertson42
Tourist
5 1 0

Here's an example of verifying an application initialization request. Works in Java 11, may work in lower versions, and requires no third party libraries.

    public boolean verifySignature(String secretKey, String hmac, String queryString) {
        String reducedQueryString = queryString.replaceAll("hmac="+hmac+"&?", "");
        if(reducedQueryString.endsWith("&") && reducedQueryString.length() > 1) {
            reducedQueryString=reducedQueryString.substring(0, reducedQueryString.length()-1);
        }
        return verifyHmac(reducedQueryString, hmac, secretKey());
    }

    public boolean verifyHmac(String message, String hmac, String secretKey) {
        try {
            Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
            SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
            hmacSHA256.init(key);
            String fx = "%" + hmacSHA256.getMacLength()*2 + "x";
            String calculated = String.format(fx, new BigInteger(1, hmacSHA256.doFinal(message.getBytes())));
            return hmac.equals(calculated);
        } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
            LOG.error("Error verifying hmac", ex);
            throw new IllegalArgumentException(ex);
        }
    }
0 Likes
AmanL04
New Member
1 0 0

We are creating web-hooks using the Admin API and not the dashboard.

What is the "shared secret" in this case?

We have tried the "client_secret" of the app created but it has failed to work.  

This is our java code:

final String hmac = httpServletRequest.getHeader("X-Shopify-Hmac-SHA256");
final String body = IOUtils.toString(httpServletRequest.getInputStream(), StandardCharsets.UTF_8);

SecretKeySpec signingKey = new SecretKeySpec(SECRET.getBytes(), ALGORITHM);
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(signingKey);

String generatedHmacBase64 = Base64.getUrlEncoder().withoutPadding().encodeToString(mac.doFinal(message.getBytes()));

S.O.P(hmac.equals(generatedHmacBase64)); // ALWAYS FALSE
0 Likes