如何在node.js中获取字符串的sha1哈希值?

162

我正在尝试创建一个使用node.js编写的WebSocket服务器

为了使服务器正常工作,我需要获取字符串的SHA1哈希值。

具体操作请参考文件第35页第5.2.2节中的说明。

注意: 例如,如果客户端握手中的"Sec-WebSocket-Key"头的值为"dGhlIHNhbXBsZSBub25jZQ==",则服务器将附加字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"以形成字符串"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"。然后服务器将对此字符串进行SHA-1哈希运算,得到值为0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea。接着该值将进行base64编码,得到值为"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",并在"Sec-WebSocket-Accept"头中返回。


9
我强烈推荐使用出色的 http://socket.io/ 库,而不是自己编写。它经过了广泛测试和修复,并且通过各种方法支持大多数浏览器(甚至那些没有 WebSocket API 的)。 - Alex Turpin
1
未来的访问者可以参考这个不错的网址:https://dev59.com/mWox5IYBdhLWcg3wIQ_3 - Damodaran
6个回答


60

必要的糟糕解决方案:使用SHA1

SHA1已经被破解,你可以花费45000美元(甚至更少,因为这个答案是在写作时)来计算SHA1碰撞

为了回答你的问题并生成SHA1哈希:

const INSECURE_ALGORITHM = 'sha1'
const getInsecureSHA1ofJSON = function(input){
  return crypto.createHash(INSECURE_ALGORITHM).update(JSON.stringify(input)).digest('hex')
}

使用webcrypto是一个很好的解决方案(它是适用于浏览器和Node.js的JS标准)。
根据MDN的代码,我制作了boring-webcrypto-sha256
npm i boring-webcrypto-sha256

import { getSHA256Hash } from "boring-webcrypto-sha256";
const hash = await getSHA256Hash('someInput')

使用node.js自带的crypto模块的好解决方案(特定于node)

官方node文档关于crypto.createHash()的说明

你应该使用sha256

const getSHA256ofJSON = function(input){
  return crypto.createHash('sha256').update(JSON.stringify(input)).digest('hex')
}

然后:

getSHA256ofJSON('whatever')

或者

getSHA256ofJSON(['whatever'])

或者

getSHA256ofJSON({'this':'too'})

10
好主意。但请注意,除了数组和 null 之外的所有对象都将具有相同的 sha1sum 值,因为 Object.toString() 默认返回 [object Object]。因此,sha1sum({}) === sha1sum({"foo":"bar"}) === sha1sum({"a":1}) 等等。 - maerics
6
给定字符串的SHA1值应该在任何平台上都相同。您使用JSON.stringify实现的方式会更改原始字符串,sha1sum(“abcd”)产生了f805c8fb0d5c466362ce9f0dc798bd5b3b32d512,而任何人都会期望得到81fe8bfe87576c3ecb22426f8e57847382917acf。 - Pierre
2
@Pierre 非常好的观点。考虑到您所说的,我认为将函数命名为sha1sum是不准确的 - 这显然比普通的sha1做得更多。我已经在答案中将该函数重命名。 - mikemaccana
3
由于haveibeenpwned.com API需要SHA1哈希值,因此即使在现今仍有完全合理的理由使用它。但还是感谢您的回答! - NotX
2
SHA1仍然适用于差异检查吗?并非所有哈希应用都与安全有关。我认为发表像“SHA1已经破解,你应该使用sha256”这样的笼统声明是误导性的。SHA1非常快速,这是某些非安全相关应用程序的要求。 - Storm Muller
显示剩余8条评论

19

我发现NodeJS正在对字符串的UTF-8表示进行哈希处理。其他语言(如Python、PHP或PERL...)正在对字节串进行哈希处理。

获取与Python/PHP等相同的哈希值的提示:

我们可以添加binary参数来使用字节串(这种编码会增加碰撞的可能性,但将兼容其他语言)。

const crypto = require('crypto')

function sha1(data) {
    return crypto.createHash('sha1').update(data, 'binary').digest('hex')
}

text = 'Your text and symbol \xac'

console.log(text, ':', sha1(text))

你可以尝试使用:"\xac"、"\xd1"、"\xb9"、"\xe2"、"\xbb"、"\x93"等等...

其他编程语言(Python,PHP,...):

sha1('\xac') //39527c59247a39d18ad48b9947ea738396a3bc47

Nodejs:

sha1 = crypto.createHash('sha1').update('\xac', 'binary').digest('hex') //39527c59247a39d18ad48b9947ea738396a3bc47
//without:
sha1 = crypto.createHash('sha1').update('\xac').digest('hex') //f50eb35d94f1d75480496e54f4b4a472a9148752

2
'binary' - 别名为 'latin1' - Jossef Harush Kadouri
3
^^ @JossefHarush 的评论非常重要!如果您在哈希之前不需要将文本明确编码为 latin1(例如,与 PHP 兼容),并且有任何可能包含拉丁1范围之外的 Unicode 符号(例如表情符号!),请不要使用 binary!在编码中使用 binarylatin1 将会丢失信息并增加碰撞的可能性!例如,请尝试使用这两个片段: - cbr
所有哈希都是在二进制数据上完成的。你遇到的问题是,你提到的其他语言没有使用UTF-8,而不是相反。一旦你尝试对Latin1之外的东西进行哈希,这将变得非常明显。特别是在PHP的情况下,编码完全由源代码确定,例如硬编码文本的文本文件本身。Perl可能需要一些重活来使用UTF-8。 - Ryan Hanekamp
1
我们不能对被散列的模式的字符集做任何假设,它必须是二进制的。没有人关心冲突,所有实现都必须是确定性的。 - mckenzm
2
@mckenzm,没错,然而在nodejs中,字符串字面量的“正确”二进制表示是utf8。问题在于使用 "\xac" 并期望二进制表示为 [0xAC],就像其他具有不同本地编码的语言一样。Node使用utf8,因此utf8源代码中的 "\xac"(或 "¬")被解释为 [0xC2, 0xAC],并且以上代码生成了正确的sha1数组。 修复方法是首先以二进制形式读取数据,因为没有固有的跨平台字符串二进制表示。 - zapl

12

您可以使用:

  const sha1 = require('sha1');
  const crypt = sha1('Text');
  console.log(crypt);

安装说明:

  sudo npm install -g sha1
  npm install sha1 --save

1
嗨,用户944550,欢迎您。请考虑添加更多信息。 - Tiago Martins Peres

7

请在您的帖子评论中阅读并认真考虑我的建议。话虽如此,如果您仍有充分的理由这样做,请查看这个针对Node的加密模块列表。它包含用于处理sha1和base64的模块。


3

使用Node v15中新增的新型浏览器兼容、零依赖的SubtleCrypto API进行回答。

const crypto = this.crypto || require('crypto').webcrypto;

const sha1sum = async (message) => {
  const encoder = new TextEncoder()
  const data = encoder.encode(message)
  const hashBuffer = await crypto.subtle.digest('SHA-1', data)
  const hashArray = Array.from(new Uint8Array(hashBuffer));                     // convert buffer to byte array
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
  return hashHex;
}

sha1sum('foo')
  .then(digestHex => console.log(digestHex))

// "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"

Node沙箱:https://runkit.com/hesygolu/61564dbee2ec8600082a884d

来源:


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