3
votes

I'm trying to validate the Shopify HMAC during an OAUTH request and the hash I generate does not match the one provided as part of the request.

I've found some other threads but they are either outdated, as the documentation now states it uses a GET request instead of POST, or unanswered in java.

My C# code is as follows:

string key = "mysecretkey";

string message = string.Format("shop={0}&timestamp={1}", shop, timestamp);

System.Text.ASCIIEncoding encoding = new ASCIIEncoding();

byte[] keyBytes = encoding.GetBytes(key);

byte[] messageBytes = encoding.GetBytes(message);

System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);

byte[] bytes = cryptographer.ComputeHash(messageBytes);

string digest = BitConverter.ToString(bytes).Replace("-", "");

bool valid = digest == hmac.ToUpper();

I'm guessing the message is being built incorrectly but I've followed the official documentation with no luck.

Can someone help please?

3
You are calculating a HMAC without using your key. The documentation states that you should generate the HMAC digest using the shared key. Try cryptographer.Key = keyBytes; before computing the HashOguz Ozgul
Try using this string: code=code&shop=shop&state=state&timestamp=timestamp; It worked for meParag Jadhav
Thanks Parag. I suspected this might be the case but no matter what combination of parameters I use it still does not work. I've asked the developers at Shopify for help so we'll see what they say.Guy Lowe
@Parag is correct. As per doc you have to just remove the fields hmac and signature and compute the hmac using remaining variables. In future if shopify decides to add another variable in the response the hmac computation should also include that variable. Thanks parag.Sasi varna kumar

3 Answers

5
votes

OK the devs at Shopify got back to me with the answer. It seems you need to hash the entire contents of the querystring in alphabetical order except the signature and hmac. I had my own parameter (rlr) I was appending as well as one not mentioned in the docs (state).

 string message = "";// "code=7af66fd73427a1634cee3103297230b8&rlr=9DFD5EA9-7747-4142-97D9-2D44BBA442F1&shop=appswiz.myshopify.com&state=fa992b8f-762e-4813-b707-6044e71ad3b5&timestamp=1448856806";
        message = "code=xxxxxxxx";
        message += "&rlr=xxxxx";
        message += "&shop=xxx.myshopify.com";
        message += "&state=xxxxxxxx";
        message += "&timestamp=1449111190";
        hmac = "xxxxxxx";
        System.Text.ASCIIEncoding encoding = new ASCIIEncoding();
        byte[] keyBytes = encoding.GetBytes(key);
        byte[] messageBytes = encoding.GetBytes(message);
        System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);

        byte[] bytes = cryptographer.ComputeHash(messageBytes);

        string digest = BitConverter.ToString(bytes).Replace("-", "");
        return digest == hmac.ToUpper();

This now works.

1
votes

You are calculating a HMAC without using your key.

The documentation states that you should generate the HMAC digest using the shared key. What is the meaning of an HMAC value without the key anyways? Anybody could mimic the shopify server if Shopify had not use the pre-shared key between you and them for calculating the HMAC.

The following code block is from the documentation:

digest = OpenSSL::Digest.new('sha256')
secret = "hush"
message = "shop=some-shop.myshopify.com&timestamp=1337178173"

digest = OpenSSL::HMAC.hexdigest(digest, secret, message)
digest == "2cb1a277650a659f1b11e92a4a64275b128e037f2c3390e3c8fd2d8721dac9e2"

Therefore, try cryptographer.Key = keyBytes; before computing the Hash

byte[] keyBytes = encoding.GetBytes(key);

byte[] messageBytes = encoding.GetBytes(message);

System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);

cryptographer.Key = keyBytes;

byte[] bytes = cryptographer.ComputeHash(messageBytes);
0
votes

Using Guy Lowe's answer I recently got this working:

    public bool ValidateShopifyHmac(string hmacHeader, string localData, string apiSecret) {
        var ascii = new ASCIIEncoding();
        var secretBytes = ascii.GetBytes(apiSecret);
        var cryptographer = new System.Security.Cryptography.HMACSHA256(secretBytes);
        var messageBytes = ascii.GetBytes(localData);
        var hashedMessage = cryptographer.ComputeHash(messageBytes);
        var digest = BitConverter.ToString(hashedMessage).Replace("-", "");
        return digest == hmacHeader.ToUpper();
    }