SHA 256不同的结果

20

如果我从 Mac 上调用命令

echo hello | shasum -a 256

或者从Ubuntu获取

echo hello | sha256sum

那么我得到了以下结果

5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03  -

我注意到末尾有一个破折号。

但是当我使用Python的hashlib或Java的java.security.MessageDigest时,它们会给我相同的结果,如下所示:

2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

那么,请问有人可以指出我的错误吗?

谢谢。


Python:

>>> import hashlib
>>> hashlib.sha256("hello").hexdigest()

Java:

Java
MessageDigest md = MessageDigest.getInstance("SHA-256");
String text = "hello";
md.update(text.getBytes("UTF-8"));
byte[] digest = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < digest.length; i++) {
    sb.append(String.format("%02x", digest[i] & 0xFF))
}
System.out.println(sb.toString());

2
当你停止调查时,你犯了错误。接下来要做的逻辑事情是在两个系统上执行类似于 echo hello | od -a 的命令,以确保 sha256sum 获取到相同的输入。 - David Schwartz
@DavidSchwartz 或者现在我们已经在处理十六进制了,可以使用od -A n -t x1。SHA256 的输入和输出都是二进制的。 - Maarten Bodewes
2
echo -n hello | sha256sum 是 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824。 - axiopisty
这个回答解决了你的问题吗?从Linux命令行生成SHA-256哈希 - uak
2个回答

32
< p > echo 命令会在你的字符串后面添加一个换行符。尝试使用:

hashlib.sha256("hello\n").hexdigest()

1
谢谢!我完全没有意识到这一点。 - 4af2e9eb6
好的回答,但没有解释破折号。 - Maarten Bodewes
1
@MaartenBodewes 这个破折号是文件名。由于它是标准输入,因此打印出一个破折号。 - Art
我同意@MaartenBodewes的观点。不过你认为那是问题的关键吗?我认为OP只是在暗示破折号可能是导致他们得到不同结果的线索。 - Turn
可能不是关键点,但我也不认为Aquacava想每次都手动比较。但是没错,你说得对。 - Maarten Bodewes
显示剩余2条评论

12

TL;DR 这是一篇关于字符和十六进制编码的详细解释,如果你只想看代码,请直接跳到下面

sha256sum 和相关命令会在输出中添加破折号:-。这些命令旨在显示 *文件 的哈希值。单个破折号表示输入来自标准输入流(即没有文件名)。不幸的是,我没有看到抑制输出的选项,所以您必须自己删除破折号才能得到实际的哈希值。

因此,哈希工具不仅返回哈希值。SHA-256 哈希值仅由 32 字节组成。由于人类无法阅读二进制数,因此使用十六进制显示二进制值,但实际值仍应视为字节。十六进制字符只是这些字节的一种表示

哈希函数的输入也由位或字节组成。这意味着对文本进行任何编码差异都将导致哈希值不同。当涉及空格和行尾编码时,这尤其棘手。与其添加尾随换行符,不如在"hello"的情况下使用echo 命令的-n 命令行选项来抑制它。

请注意,十六进制数本身也可以以不同的方式显示;您需要确保不存在空格,并且比较不区分大小写,或者字节的表示始终使用相同的大小写。

Shell 代码

使用 sha256sum

echo -n "hello" | sha256sum | tr -d "[:space:]-"

使用 OpenSSL 命令行:

echo -n hello | openssl sha256 -binary | od -An -tx1 | tr -d "[:space:]"

这里 od -An -tx1 将每个字节分开显示,而不是将它们分组,这可能会导致大小端问题。

tr -d "[:space:] 不仅会从十六进制中删除空格,还会删除尾随的换行符。对于 sha256sum,也会删除破折号文件指示符(请注意末尾的 -)。这样就可以执行文本比较(不区分大小写)。

Python 代码

在 Python 中没有尾随的行结束符:

print(hashlib.sha256("hello").hexdigest(), end="")

Java代码

在Java中,您还应确保文本编码与系统默认编码相匹配,否则可能会遇到问题。因此,您应该更改:

md.update(text.getBytes("UTF-8"));

md.update(text.getBytes());

要获取平台字符编码。如果您不这样做,则如果平台的编码与您想要比较的字符串的UTF-8不兼容,则比较将失败。


有趣的是,这是我第一次觉得从Java程序中剥离字符编码实际上是有意义的。通常情况下,我必须提醒开发人员添加字符编码... - Maarten Bodewes
也许是我有问题...我看到“TL;DR ...你可以跳过这部分,直接看下面的代码”时微笑了。我知道,因为我已经写过这样的免责声明很多次了,所以我必须继续阅读,而大多数人不会。感谢您详细的回答。 - CodeShane
最近我在解决为什么 AWS S3 拒绝我的校验和时遇到了困难。通过对输出进行 base64 编码,我完全忽略了 sha256sum 以十六进制编码哈希值,而 OpenSSL 则将其保留为二进制。 - myrsnipe

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