Python与JavaScript中的HMAC SHA256

2024-05-12 23:41:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用JavaScript重新实现一个特定的API客户机,它是用Python编写的。我无法复制HMAC SHA256签名函数。对于某些键,输出相同,但对于某些键,输出不同。解码其Base64表示后,如果密钥由可打印字符组成,则输出似乎是相同的

Python

#!/usr/bin/env python3

import base64
import hashlib
import hmac

def sign_string(key_b64, to_sign):
    key = base64.b64decode(key_b64)
    signed_hmac_sha256 = hmac.HMAC(key, to_sign.encode(), hashlib.sha256)
    digest = signed_hmac_sha256.digest()
    return base64.b64encode(digest).decode()

print(sign_string('VGhpcyBpcyBhIHByaW50YWJsZSBzdHJpbmcuCg==', "my message"))
print(sign_string('dGhlIHdpbmQgb2YgTXQuIEZ1amkK', "my message"))
print(sign_string('pkmNNJw3alrpIBi5t5Pxuym00M211oN86IhLZVT8', "my message"))

JavaScript

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/hmac-sha256.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/enc-base64.min.js"></script>

<script>
    function sign_string(key_b64, to_sign) {
        key = atob(key_b64)
        var hash = CryptoJS.HmacSHA256(to_sign, key);
        var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
        document.write(hashInBase64 + '<br>');
    }
    sign_string('VGhpcyBpcyBhIHByaW50YWJsZSBzdHJpbmcuCg==', "my message")
    sign_string('dGhlIHdpbmQgb2YgTXQuIEZ1amkK', "my message")
    sign_string('pkmNNJw3alrpIBi5t5Pxuym00M211oN86IhLZVT8', "my message")
</script>

输出

Python

TdhfUQfym16HyWQ8wxQeNVvJKr/tp5rLKHYQSpURLpw=
pQ5NzK3KIWjqc75AXBvWgLK8X0kZvjRHXrLAdxIN+Bk=
8srAvMucCd91CWI7DeCFjxJrEYllaaH63wmVlMk0W+I=

JavaScript

TdhfUQfym16HyWQ8wxQeNVvJKr/tp5rLKHYQSpURLpw=
pQ5NzK3KIWjqc75AXBvWgLK8X0kZvjRHXrLAdxIN+Bk=
31QxOpifnpFUpx/sn336ZmmjkYbLlNrs8NP9om6nPeY=

正如你所看到的,前两个是相同的,而后一个是不同的

如何更改JavaScript代码,使其行为与python代码相同?


Tags: tokeyimportmessagestringmyjsscript
2条回答

您试图提供给CryptoJs的base64编码秘密不代表CryptoJs需要的有效UTF-8字符串。您可以使用this tool检查有效性atob()编码不可知,只是逐字节转换,不检查它是否有效UTF-8

在这里,我使用CryptoJS自己的解码器对base64机密进行解码,它抛出一个错误,说它是无效的UTF-8:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/hmac-sha256.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/enc-base64.min.js"></script>

<script>
    function sign_string(key_b64, to_sign) {
        var key = CryptoJS.enc.Base64.parse(key_b64).toString(CryptoJS.enc.Utf8);
        var hash = CryptoJS.HmacSHA256(to_sign, key);
        var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
        document.write(hashInBase64 + '<br>');
    }
    sign_string('VGhpcyBpcyBhIHByaW50YWJsZSBzdHJpbmcuCg==', "my message")
    sign_string('dGhlIHdpbmQgb2YgTXQuIEZ1amkK', "my message")
    sign_string('pkmNNJw3alrpIBi5t5Pxuym00M211oN86IhLZVT8', "my message")
</script>

我还发现了一种可以使用原始字节作为密钥的方法。这适用于最后一个关键点,但不适用于前两个关键点

var key = CryptoJS.enc.Hex.parse(toHex(atob(key_b64)));

现在,如果你结合这两种方法,你可以得到一个真正的解决方案。最后一段代码给出了与python相同的输出:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/hmac-sha256.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/enc-base64.min.js"></script>

<script>
    function sign_string(key_b64, to_sign) {
        try {
            var key = CryptoJS.enc.Base64.parse(key_b64).toString(CryptoJS.enc.Utf8);
        }
        catch {
            var key = CryptoJS.enc.Hex.parse(toHex(atob(key_b64)));
        }
        var hash = CryptoJS.HmacSHA256(to_sign, key);
        var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
        document.write(hashInBase64 + '<br>');
    }
    
    function toHex(str) {
        var result = '';
        for (var i=0; i<str.length; i++) {
          if (str.charCodeAt(i).toString(16).length === 1) {
            result += '0' + str.charCodeAt(i).toString(16);
          } else {
            result += str.charCodeAt(i).toString(16);
          }
        }
        return result;
    }

    sign_string('VGhpcyBpcyBhIHByaW50YWJsZSBzdHJpbmcuCg==', "my message")
    sign_string('dGhlIHdpbmQgb2YgTXQuIEZ1amkK', "my message")
    sign_string('pkmNNJw3alrpIBi5t5Pxuym00M211oN86IhLZVT8', "my message")
    sign_string('xTsHZGfWUmnIpSu+TaVraECU88O3j9qVjlwTWGb/C8k=', "my message")
</script>

在第三个示例中,您在python和JavaScript版本中使用了不同的参数。 在python中: 符号字符串('xTsHZGfWUmnIpSu+TaVraECU88O3j9qVjlwTWGb/C8k=',“我的消息”) 在JavaScript中: 符号字符串('pkmNNJw3alrpIBi5t5Pxuym00M211oN86IhLZVT8',“我的消息”)

相关问题 更多 >