Nodejs crypto md5 hash 给了我 24 字节的摘要,而不是预期的 16

Nodejs crypto md5 hash gives me digest of size 24 bytes, instead of expected 16

var crypto = require('crypto');
var key1 = crypto.createHash('md5').update('abcdefgh').digest();
var key2 = crypto.createHash('md5').update('abcdefgh').digest('base64');
var key3 = crypto.createHash('md5').update('abcdefgh').digest('hex');
var key4 = crypto.createHash('md5').update('abcdefgh').digest('latin1');
console.log('Key sizes :', key1.toString().length, key2.toString().length, key3.toString().length, key4.toString().length ); 
//Key sizes : 16 24 32 16

md5 不应该总是 return 16 字节摘要吗?

hash.digest(encoding) docs

Calculates the digest of all of the data passed to be hashed (using the hash.update() method). The encoding can be 'hex', 'latin1' or 'base64'. If encoding is provided a string will be returned; otherwise a Buffer is returned.

'binary' 不是 digest 的可识别编码类型。默认情况下使用 'buffer' 类型的编码。

而是使用 'hex''base64'

var key1 = crypto.createHash('md5').update('abcdefgh').digest('hex');

key1.length
// => 16

key1.toString()
// => 'e8dc4081b13434b45189a720b77b6818'

注意输出是 32 个字符,其中每 2 个字符代表一个十六进制格式的字节。

所以哈希大小实际上是 (32/2=16) 16 字节.


binary output is self explanatory, no encoding

嗯,不,这不是不言自明的。这是一个未记录的参数值。但是,请注意 encoding 是一个 可选的 参数。

如果在调用 digest 时不使用参数,您仍然会得到 16 字节的结果

var key1 = crypto.createHash('md5').update('abcdefgh').digest()

key1.length
// => 16

key1.toString()
// => '��@��44�Q�� �{h\u0018'

key1.toString().length
// => 16

请注意,当使用 'buffer' 类型时,Buffer 的默认编码是 'utf8'。所以当我们调用 buffer.toString 时,我们得到了这个讨厌的输出。你看到里面的 \u 了吗?那是统一码。如果您没有使用正确的 digest 参数,您可以轻松地将缓冲区转换为 hex 字符串(或 base64),

// digest defaults to 'buffer' for unrecognized type
var key1 = crypto.createHash('md5').update('abcdefgh').digest('')

key1.length
// => 16

key1.toString()
// => '��@��44�Q�� �{h\u0018'

key1.toString('hex')
// => 'e8dc4081b13434b45189a720b77b6818'

无论传递给摘要的参数如何,缓冲区中的结果字节都是相同的。编码只是传递给缓冲区,只要您选择将缓冲区转换为字符串,就可以更改编码。

Buffer 构造函数(已弃用)需要第二个编码参数。如果您不指定,它将把输入解释为 UTF-8。

您的 md5 散列不是有效的 UTF-8,但 Buffer 构造函数试图从中构造一个有效的 UTF-8 字符串。在我看来,它应该用 replacement characters 代替无效字节,但它似乎最终做的是以下内容:对于每个以 0 作为第一位的字节,没有任何改变(它们已经有效),对于以 1 作为第一位的每个字节,它将字节编码为以下两个字节:

110000xx 10xxxxxx

其中 x 是它正在编码的字节的位。在您的情况下,这种情况发生了八次,总是导致 c2c3 后跟另一个字节。所有在第一个位置有 0 的字节都被正常编码。你最终得到了八个额外的字节,并且 16 + 8 = 24:

您应该指定您希望缓冲区接受二进制:

console.log(new Buffer(key1, 'binary'));

并且,由于 Buffer 构造函数已弃用,您应该切换到 Buffer.from:

console.log(Buffer.from(key1, 'binary'));