Reputation: 304
I'm starting to code in Go and I tried to do an simple request validation, by checking the signature sent by the client. At the first look everything was fine, but after a few tests validating real requests I found that Go is generating an awkward hash.
To prove the inconsistency between the Go backend and the JavaScript signature, I developed an PHP version of the same sign method, and it gave me identical results of the JavaScript version, so my expectation was right.
I've developed one sample test to each language: Go, PHP and JavaScript.
So, to achieve the same results of PHP and JavaScript in Go, what should I do?
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
)
func main() {
data := "My name is Danniel"
mac := hmac.New(sha256.New, []byte("secret"))
mac.Write([]byte(data))
macSum := mac.Sum(nil)
data64 := base64.StdEncoding.EncodeToString(macSum)
fmt.Println(fmt.Sprintf("mac: %s | b64: %s", macSum, data64))
data64 = base64.StdEncoding.EncodeToString([]byte("My name is Danniel"))
fmt.Println(fmt.Sprintf("b64: %s", data64))
}
mac: 6��q��0��5�|��G�#0��ih | b64: NqzRcf/FMLPvo678NdC58JB8lgOFR+wjMNQDDwSkaWg=
b64: TXkgbmFtZSBpcyBEYW5uaWVs
$data = 'My name is Danniel';
$macSum = hash_hmac('sha256', $data, 'secret');
$data64 = base64_encode($macSum);
echo sprintf('mac: %s | b64: %s', $macSum, $data64) . "\n";
$data64 = base64_encode('My name is Danniel');
echo sprintf('b64: %s', $data64) . "\n";
mac: 36acd171ffc530b3efa3aefc35d0b9f0907c96038547ec2330d4030f04a46968 | b64: MzZhY2QxNzFmZmM1MzBiM2VmYTNhZWZjMzVkMGI5ZjA5MDdjOTYwMzg1NDdlYzIzMzBkNDAzMGYwNGE0Njk2OA==
b64: TXkgbmFtZSBpcyBEYW5uaWVs
var data = 'My name is Danniel';
var mac = CryptoJS.HmacSHA256(data, 'secret');
var macSum = mac.toString();
var data64 = btoa(macSum)
console.log('mac: ' + macSum + ' | b64: ' + data64);
var data64 = btoa('My name is Danniel')
console.log('b64: ' + data64);
"mac: 36acd171ffc530b3efa3aefc35d0b9f0907c96038547ec2330d4030f04a46968 | b64: MzZhY2QxNzFmZmM1MzBiM2VmYTNhZWZjMzVkMGI5ZjA5MDdjOTYwMzg1NDdlYzIzMzBkNDAzMGYwNGE0Njk2OA=="
"b64: TXkgbmFtZSBpcyBEYW5uaWVs"
Upvotes: 1
Views: 2022
Reputation: 61952
The problem is that both your PHP and JavaScript codes do this: Base64(Hex(Hmac(key, msg)))
. You really don't need the double encoding.
In PHP you can simply request raw_encoding
instead of hex:
$macSum = hash_hmac('sha256', $data, 'secret', true);
and in CryptoJS, you have to encode directly to Base64 instead of encoding to hex and then using btoa()
with:
var data64 = mac.toString(CryptoJS.enc.Base64);
For that, you will need to include the enc-base64.js component.
var data = 'My name is Danniel';
var mac = CryptoJS.HmacSHA256(data, 'secret');
var macSum = mac.toString();
var data64 = mac.toString(CryptoJS.enc.Base64)
document.write('mac: ' + macSum + ' | b64: ' + data64);
var data64 = btoa('My name is Danniel')
document.write('<br>b64: ' + data64);
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/hmac-sha256.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/enc-base64.js"></script>
Upvotes: 5
Reputation: 246
Your PHP function works differently than the Go function.
From the PHP documentation:
Returns a string containing the calculated message digest as lowercase hexits unless raw_output is set to true in which case the raw binary representation of the message digest is returned. Returns FALSE when algo is unknown.
So in this case, Go is encoding the raw bytes returned from the HMAC sum. In PHP (and I assume JS), you are b64 encoding a hex string returned from the hmac.
To achieve the same results in go, encode the []byte in hex, then base64 encode it.
Generally, however, it is best practice to operate on bytes, not an encoding of bytes. If you have to conform to a previous design, go ahead, but otherwise you should be using the raw_output option allowed in the PHP function and you should end up with the same results.
Upvotes: 2