在Node.js和Python中进行单向sha256哈希的比较

3

I have the following version of code for Python:

import hashlib
msg = 'abc'
print msg
sha256_hash = hashlib.sha256()
sha256_hash.update(msg)
hash_digest = sha256_hash.digest()
print hash_digest

相应的 Node js 版本:

var crypto= require('crypto');
var msg = 'abc';
var shasum = crypto.createHash('sha256').update(msg);
var hashDigest = shasum.digest();
console.log(hashDigest);

然而,两个输出的二进制结果都略微有所偏差:

  • Node: �x����AA@�]�"#�a��z���a��
  • Python: �x���AA@�]�"#�a��z���a��

尽管在两个库之间十六进制表示是正确的。我在这里做错了什么吗?


你使用的是哪个版本的Node?我感到困惑的原因主要是,至少对于我来说,hashDigest是一个Buffer,所以你的示例输出肯定不正确。对于Python,如果你实际上打印出hash_digest的字节,你会得到什么?在我的测试中,当运行此代码时,两者都会得到相同的二进制内容。 - loganfsmyth
节点版本0.10.25。对于节点,只需console.log('OP:'+hashDigest)。我仍然不能确定我做错了什么。你能把你的输出粘贴过来吗? - wizgot
我也在Node 0.10.32上尝试过了,输出结果和上面一样。 - wizgot
你为什么要比较字符串值而不是十六进制值呢? - loganfsmyth
3个回答

5

简短概述

你的节点代码尝试解析哈希结果为utf8并失败了。


两种语言处理其二进制数据和字符串类型的方式不同。考虑最终的二进制输出,你的两个示例都输出相同的值。因此让我们来看一下你两个示例的十六进制输出:

ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

在Python中:

'\xbax\x16\xbf\x8f\x01\xcf\xeaAA@\xde]\xae"#\xb0\x03a\xa3\x96\x17z\x9c\xb4\x10\xffa\xf2\x00\x15\xad'

在Node中:

<SlowBuffer ba 78 16 bf 8f 01 cf ea 41 41 40 de 5d ae 22 23 b0 03 61 a3 96 17 7a 9c b4 10 ff 61 f2 00 15 ad>

在这种情况下,需要注意的核心问题是Python中返回的结果是字符串。在Python中,字符串只是char(0-255)值的数组。然而,在Node中,该值存储为缓冲区(Buffer),实际上表示了一个值(0-255)的数组。 这就是关键的不同之处。 Node不返回字符串,因为Node中的字符串不是单字节字符的数组,而是UTF-16代码单元的数组。 Python使用u''指定的单独字符串类来支持Unicode。
所以比较您打印输出的示例,为了便于阅读进行了简化。
print '\xbax\x16\xbf\x8f\x01\xcf\xeaAA'

vs

console.log('' + 
    new Buffer([0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41]))

Python代码的意思是将这个字节数组写入终端。然而第二个代码块则表示了完全不同的意思:将这个字节数组转换成字符串,然后将该字符串写入终端。但是缓冲区是二进制数据,不是UTF-8编码的数据,因此它将无法将您的数据解码为字符串,导致出现乱码结果。如果您希望直接在终端中比较二进制值作为实际解码值,则需要在两种语言中都提供相应的指令。

print '\xbax\x16\xbf\x8f\x01\xcf\xeaAA'

vs

process.stdout.write(
    new Buffer([0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41]))

在这种情况下,process.stdout.write 是一种将二进制值写入终端的方式,而不是字符串。但实际上,你应该将哈希值作为十六进制进行比较,因为它已经是一个二进制值的字符串表示形式,而且比错误解码的Unicode字符更容易阅读。

感谢您的清晰解释和帮助。问题是,我正在使用它来验证请求,而开发平台的开发人员创建了hmacs sigs,但没有进行十六进制转换。 - wizgot
明白了。我会专注于将其作为单字节字符串(Python)和缓冲区(Node)进行处理,但如果您要打印它,您可以在那时将其转换为十六进制,这样您就不必直接将其转储到控制台。 - loganfsmyth

4

对我来说是匹配的。

Python 2.7.3:

Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import hashlib
>>> msg = 'abc'
>>> sha256_hash = hashlib.sha256()
>>> sha256_hash.update(msg)
>>> hash_digest = sha256_hash.hexdigest()
>>> print hash_digest
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
>>>

Node v0.10.30:

> crypto.createHash('sha256').update('abc').digest('hex')
'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'

两个16进制字符串匹配。


是的,这就是我提到的,十六进制代码可以,但二进制代码不同,这没有任何意义(因此有问题),我想我可能做了一些傻事,已经折腾了一天多了。 - wizgot

0

我遇到了类似的情况,需要将下面的Python hmac256函数转换为Node.js等效函数

 def HmacSha256(key, sign):
        return hmac.new(key, sign, hashlib.sha256).digest()

 hash = HmacSha256("\0"*32, rawMsg)
 print hash

以上片段的示例输出。
python test.py sasa
_��"/���q���h�u$�k�w�)R]n�mf�

这是哈希后得到的字节的字符串表示形式。 它在Nodejs中的等效物就像这样简单。

function HmacSha256(key, sign){
    return crypto
      .createHmac("sha256", key)
      .update(sign)
      .digest()
  }

const hash = HmacSha256("\0".repeat(32), rawMsg).toString()
    console.log(hash)

上面的Node.js代码片段的示例输出

node test.js sasa
_��"/���q���h�u$�k�w�)R]n�mf�

请注意,输出结果是相同的。我所要做的就是将在 HmacSha256("\0".repeat(32), rawMsg) 返回的缓冲区数组转换为字符串。 我正在使用 Node v8.11.2Python 2.7.15rc1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接