Unix md5sum 与 Powershell Get-hash

Unix md5sum vs Powershell Get-hash

我正在尝试从 Powershell 生成 md5 哈希。我安装了 Powershell Community Extension (Pscx) 来获取命令:Get-Hash

然而,当我使用 Get-Hash 生成 md5 散列时,它似乎与 Ubuntu 机器上使用 md5sum 生成的散列不匹配。

Powershell:

PS U:\> "hello world" | get-hash -Algorithm MD5

Path Algorithm HashString                       Hash
---- --------- ----------                       ----
     MD5       E42B054623B3799CB71F0883900F2764 {228, 43, 5, 70...}

Ubuntu:

root@LT-A03433:~# echo "hello world" | md5sum
6f5902ac237024bdd0c176cb93063dc4  -

我知道 Ubuntu 生成的那个是正确的,因为几个在线站点显示相同的结果。

Powershell Get-Hash 有什么问题?

区别不明显,但你不是在散列相同的数据。 MD5 是一种散列算法,它没有文本编码的概念——这就是为什么您可以像创建文本散列一样轻松地创建二进制数据的散列。考虑到这一点,我们可以找出 bytes(或八位字节;严格来说是每个 8 位的值流)MD5 正在计算的哈希值。为此,我们可以使用 xxd,或任何其他十六进制编辑器。

首先,您的 Ubuntu 示例:

$ echo "hello world" | xxd
0000000: 6865 6c6c 6f20 776f 726c 640a            hello world.

注意末尾的 0a,Unix 风格的换行符,在右视图中显示为 .echo 默认情况下会在其打印的内容后附加一个换行符,您可以使用 printf,但这会导致不同的散列。

$ echo "hello world" | md5
6f5902ac237024bdd0c176cb93063dc4

现在让我们考虑一下 PowerShell 在做什么。它将自己的字符串直接传递给 get-hash cmdlet。事实证明,许多 Windows 中字符串数据的自然表示与 Unix 不同——Windows 使用宽字符串,其中每个字符(在内存中)表示为两个字节。具体来说,我们可以打开一个文本编辑器,粘贴:

hello world

没有尾随换行符,并将其保存为 UTF-16,little-endian。如果我们检查它产生的实际字节数,我们会看到不同之处:

$ xxd < test.txt
0000000: 6800 6500 6c00 6c00 6f00 2000 7700 6f00  h.e.l.l.o. .w.o.
0000010: 7200 6c00 6400                           r.l.d.

每个字符现在占用两个字节,第二个字节是 00 – 这是正常的(例如,这也是为什么在 Internet 上使用 UTF-8 而不是 UTF-16 的原因),因为基本 ASCII 字符的 Unicode 代码点与其 ASCII 表示相同。现在让我们看看散列:

$ md5 < thefile.txt
e42b054623b3799cb71f0883900f2764

这与 PS 为您制作的相匹配。

所以,回答你的问题——你没有做错任何事。您只需要以相同的方式对字符串进行编码即可获得相同的哈希值。不幸的是,我无法访问 PS,但这应该是朝着正确方向迈出的一步:UTF8Encoding class.

这个问题肯定与How to get an MD5 checksum in PowerShell有关,但它是不同的并且提出了一个重要的观点。

Md5sums 是根据 字节计算的。 实际上,从某种意义上说,您的 Ubuntu 结果是错误的:

$ echo "hello world" | md5sum
6f5902ac237024bdd0c176cb93063dc4  -

$ echo -n "hello world" | md5sum
5eb63bbbe01eeed093cb22bb8f5acdc3  -

在第一种情况下,您将构成字符串的 ASCII 表示的 12 个字节加上最后一个回车 return。在第二种情况下,您不包括回车 return.

(顺便说一句,有趣的是 这里的字符串 包含一个回车符 return:)

$ md5sum <<<"hello world"
6f5902ac237024bdd0c176cb93063dc4 

在 Windows powershell 中,您的字符串以 UTF-16LE 表示,每个字符 2 个字节。要在 Ubuntu 和 Windows 中获得相同的结果,您必须使用重新编码程序。 Ubuntu 的一个不错的选择是 iconv:

$ echo -n "hello world" | iconv -f UTF-8 -t UTF-16LE | md5sum
e42b054623b3799cb71f0883900f2764  -

md5sum 是错误的,尽管其他人也同意它。它在 unixlf 上向输入字符串添加特定于平台的行尾字符,在 windows[= 上28=]一个cr-lf.

在具有 powershellbash 的机器上验证这一点,例如postgres 为比较安装:

'A string with no CR or LF at the end' | %{  psql -c "select md5('$_' || Chr(13) || Chr(10) )"   }
echo 'A string with no CR or LF at the end' | md5sum.exe
'A string with no CR or LF at the end' | %{  psql -c "select md5('$_' || Chr(10) )"   }
bash -c "echo 'A string with no CR or LF at the end' | md5sum.exe"

输出前两行:

PS> 'A string with no CR or LF at the end' | %{  psql -c "select md5('$_' || Chr(13) || Chr(10) )"   }
               md5
----------------------------------
 1b16276b75aba6ebb88512b957d2a198

PS> echo 'A string with no CR or LF at the end' | md5sum.exe

1b16276b75aba6ebb88512b957d2a198 *-

输出后两行:

PS> 'A string with no CR or LF at the end' | %{  psql -c "select md5('$_' || Chr(10) )"   }
               md5
----------------------------------
 68a1fcb16b4cc10bce98c5f48df427d4

PS> bash -c "echo 'A string with no CR or LF at the end' | md5sum.exe"

68a1fcb16b4cc10bce98c5f48df427d4 *-