Crypt.crypt() 和 DigestUtils.md5() 在 apache.commons.Codec 中的区别

Differences between Crypt.crypt() and DigestUtils.md5() in apache.commons.Codec

我正在为 Linux /etc/shadow file 的 MD5 散列方案编写基本密码破解程序。当我使用 commons.codecDigestUtilsCrypt 库时,它们的哈希长度不同(除其他外)。

当我使用 Crypt.crypt(passwordToHash, "$Jhe937$") 时,输出是一个 22 个字符的字符串。当我使用 DigestUtils.md5[Hex](passwordToHash + "Jhe937")(或 Java MessageDigest class)时,输出是一个 32 个字符的字符串(转换后)。这对我来说毫无意义。


旁白:是否没有简单的方法将 DigestUtils.md5(passwordToHash)byte[] 转换为字符串。我已经尝试了所有*方法,但我得到了所有无效输出:Nz_èJÓ_µù[î¬y

*全部为:new String(byte[], "UTF-8") 并转换为 char,然后转换为 String

执行摘要是,虽然它们将执行相同的散列,但两者的输出格式不同,因此长度会不同。继续阅读了解详情。

MD5 是一种生成 16 字节散列值的消息摘要算法,始终(假设输入有效等)这些字节 并非 所有可打印字符,它们可以采用任何字节的任何值从 0-255,而 ASCII 中的可打印字符在 32-126 范围内。

DigestUtils.md5(String) 生成字符串的 MD5 和 returns 一个 16 元素字节数组。 DigestUtils.md5Hex(String) 是一个方便的包装器(我假设,我没有看过源代码,但我就是这样写的:-))围绕 DigestUtils.md5 使用 16 元素字节数组 md5 产生并对其进行 base16 编码(也称为十六进制编码)。这会将每个字节替换为等效的两个十六进制字符,这就是为什么您会从中得到一个 32 个字符的字符串。

Crypt.crypt 使用一种特殊的格式,可以追溯到原始的 Unix 密码存储方法。多年来,它一直在扩展,以使用不同的 hash/encryption 算法、更长的盐和其他功能。它还将其输出编码为可打印文本,这是长度差异的来源。通过使用“$1$...”的盐,您说的是使用 MD5,因此密码加上盐将使用 MD5 进行哈希处理,结果如预期的那样产生 16 个字节,但因为这些字节不一定可打印,哈希是 base64 编码的(使用与标准 base64 编码略有不同的字母表),它将 3 个字节替换为 4 个可打印字符。所以 16 个字节变成 16 / 3 * 4 = 21-1/3 个字符,四舍五入为 22.

在你这边,DigestUtils.md5 产生 16 个字节,但这些字节可以有 0 到 255 之间的任何值,并且(实际上)是随机的。 new String(byte[], "UTF-8") 表示字节数组中的字节是UTF-8编码,这是一种非常特殊的格式。 new String 最好将字节视为 UTF-8 编码的字符串,但因为它们实际上不是,所以通常会出现乱码。如果您想要可打印的内容,则必须使用采用随机字节的内容,而不是特定格式(如 UTF-8)的字节。两个流行的选项是 base16/hex 编码,您可以使用 DigestUtils.md5Hex 或 base64,您可以使用 Base64.encodeBase64String(DigestUtils.md5(pwd + salt)).