为什么Go生成的hmac哈希值与PHP和JavaScript不同?

时间:2016-02-23 18:01:20

标签: go cryptography hmac

我开始在Go中编码,我尝试通过检查客户端发送的签名来进行简单的请求验证。初看起来一切都很好,但经过几次测试验证真实请求后,我发现Go产生了一个尴尬的哈希值。

为了证明Go后端和JavaScript签名之间的不一致,我开发了相同签名方法的PHP版本,它给了我相同的JavaScript版本结果,所以我的期望是正确的。

我为每种语言开发了一个示例测试:GoPHPJavaScript

因此,要在Go中实现与PHP和JavaScript相同的结果,我该怎么办?

开始

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

PHP

$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

的JavaScript

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"

2 个答案:

答案 0 :(得分:4)

问题是您的PHP和JavaScript代码都执行此操作:Base64(Hex(Hmac(key, msg)))。你真的不需要双重编码。

在PHP中,您只需要raw_encoding而不是hex:

$macSum = hash_hmac('sha256', $data, 'secret', true);

在CryptoJS中,您必须直接编码为Base64而不是编码为十六进制,然后使用btoa()编码:

var data64 = mac.toString(CryptoJS.enc.Base64);

为此,您需要包含enc-base64.js组件。



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);
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

您的PHP功能与Go功能的工作方式不同。

从PHP文档:

  

以小写形式返回包含计算的消息摘要的字符串   hexits除非raw_output设置为true,在这种情况下是原始二进制文件   返回消息摘要的表示。返回时为FALSE   算法未知。

所以在这种情况下,Go正在编码从HMAC总和返回的原始字节。在PHP中(我假设是JS),你是b64编码从hmac返回的十六进制字符串。

要在go中获得相同的结果,请以十六进制编码[]字节,然后对其进行base64编码。

但是,通常,最佳做法是对字节进行操作,而不是对字节进行编码。如果 符合以前的设计,请继续,否则你应该使用PHP函数中允许的raw_output选项,你应该得到相同的结果。