Jump to content

PHP - Wrong base64 hmac sha256 encoding with single or double quotes


Guillaume

Recommended Posts

Hi,

That might be more of a question on a PHP developer forum, and apologise if it's irrelevant here. I am working on developping a plug-in for a payment gateway. As part the payment gateway API, I am sending several fields (e.g. amount, currency, etc. but also some plain-text fields like delivery address). I put all these in an array.The payment gateay requires to be passed a field called "signature", which is an encryption of the concatenation of all other fields. The encryption is as the PHP code below:

$signature = base64_encode(hash_hmac('sha256',$concatenated_string, $certificate, true));

As long as I don't have any single quotes (') or double quotes ("), everything works fine (even with special characters, such as "é"), and the gateway recognizes that the signature is correct. Now, whenever I input single or double quotes (and single quote is a valid character in a delivery address), I am getting something completely different (which means that I am not endoding the right expected string). For example:

Expected string (UTF-8) : [tqMQ6eliAfYpBWPUf5E6QDTlTRxER1GQvIllzZ652xo=]
String received (UTF-8) : [CYrNQ4ETAFfoN9xMAy+phHEopWZQbelEOJndkQJWz8k=]

What really puzzles me, is that when I checked online (https://www.devglan.com/online-tools/hmac-sha256-online), encoding my string with hmac sha256, with the correct certificate and output as base64, I get the correct expected string, even when quotes are present.

So I believe it's my PHP code shown above with does something wrong and wrongly interprets the quote. I cannot reverse the "received string" I believe. So in order to modify my code I would need to understand what exactly is wrongly interpreted in my code, and so far I haven't found it. I have tried adding a backslash before the quote ('), two or three backslashes before the quote (\', \'), I have tried stripping the string right before the quote, or right after the quote. But none of these gave me the same result as the "received string" above, so I'm still in the dark about what is wrong there.

All the plain-text fields come from a user form. I could forbid quotes in there (or remove them when contacting the gateway), but I think that wouldn't be exactly the expected behaviour.

Any hint would be highly appreciated! Cheers,

Guillaume

Link to comment
Share on other sites

What I am reading in your observation, is that adding a new character (a quote) into $concatenated_string, you are expecting the same sha256 result as the string without that newly added character? (I am ignoring the base64 encode for the moment.)

 

Link to comment
Share on other sites

No I completely appreciate that the encoding will be totally different with or without the quote.

What I am struggling to understand is that, I'm encoding a string, (say AB'CD), and the payment gateway tells me the string I shall encode is AB'CD, and:

The expected encryption is: [tqMQ6eliAfYpBWPUf5E6QDTlTRxER1GQvIllzZ652xo=]
While the received encryption is: [CYrNQ4ETAFfoN9xMAy+phHEopWZQbelEOJndkQJWz8k=]

Where I am struggling is, that when I try (on https://www.devglan.com/online-tools/hmac-sha256-online) to encrypt AB'CD, it says that the encryption is:

tqMQ6eliAfYpBWPUf5E6QDTlTRxER1GQvIllzZ652xo=

(I.e. the expected encryption)

And I'd like to find out what exact input string results in the following encryption (so that I can debug it), which is the one received by the gateway:

CYrNQ4ETAFfoN9xMAy+phHEopWZQbelEOJndkQJWz8k=

I have tried ABCD, AB\'CD, AB'\\CD, AB\\\'CD and none of them give this result.

I hope it clarifies

Link to comment
Share on other sites

We can be reasonably sure that we know what $concatenated_string and $certificate is. Those variables can be var_dumped in your experiments.

Next, we can do:

$hexits = hash_hmac('sha256',$concatenated_string, $certificate, false);
var_dump($hexits);

Note the false argument. This will give you a string of the hashed output expressed as hex bytes. With this, you may be able to compare a hexit result from the DevGlan site.

If the two hexit strings match, then we can look at the base64_encode function's output.

Link to comment
Share on other sites

Really appreciate your help. Unfortunately, I'm getting two different strings when trying what you mentioned:

Expected string : [egPOUxw8gjnOnBs2weJDngpxIwVBGvTStigqFGqEknQ=]

Received string : [FkGkaCo8foKTG2y/b4F/t22GrYkQjj5nmRU/a5zdWVk=]

Can it be a subtlety of the hash_hmac function which we are missing?

Link to comment
Share on other sites

The strings you are comparing are not hexits. The following is an example of a hexit from hash_hmac (using false for the third parameter):

b8e7ae12510bdfb1812e463a7f086122cf37e4f7

We can make tests on the base64_encode function later.

 

Link to comment
Share on other sites

I have actually just tried applying the url_encode function to the address containing the quote, and the payment provider system seems to be happy because the payment goes through. Next, I will url_encode all the variables in the array.

Now one thing I am wondering is that if I send either of those, it works:

$signature = hash_hmac('sha256', $concatenated_string, $certificate, true);

$signature = hash_hmac('sha256', $concatenated_string, $certificate, false);

$signature = base64_encode(hash_hmac('sha256', $concatenated_string, $certificate, true));

Link to comment
Share on other sites

  • 2 weeks later...

Thanks. I haven't yet come to a solution which gives me full satisfaction. Nevertheless, the below comes in handy:

 

            #URL encoding of all values except email and URL
            foreach ($hidden as $key => $val){
                if ($key != "vads_cust_email" and substr($key, 0, 8) != "vads_url") $hidden[$key] = urlencode($val);
            }

 

In other words, I encode with urlencode all the values of the form, except URLs and emails.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...