C# 和 Kotlin 中的 MD5 哈希码 (Java)

MD5 Hashcode in C# and Kotlin(Java)

我正在努力解决 Kotlin (Java) 和 C# 中 MD5 结果消耗的差异。我发现这篇文章提出了解决方案:

How can you generate the same MD5 Hashcode in C# and Java?

但我想了解这背后的逻辑。我做了几个测试。 C#:

var data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("123456"));
var s = Encoding.UTF8.GetString(data, 0, data.Length);

产生以下字节序列(数据变量):

 225, 10, 220, 57, 73, 186, 89, 171, 190, 86, 224, 87, 242, 15, 136, 62

如果我使用 Kotlin (Java):

val md = MessageDigest.getInstance("MD5")
val data = md.digest("123456".toByteArray())

val s = String(data)

val ls2 = data.map { x-> x.toUByte() }

所以 Java 有带符号的字节和 c# 无符号字节(ls2 - 包含与 c# 示例相同的无符号字节)。美好的。我想获得字符串值——我将两个字节数组都转换为字符串,并且得到了不同的字符串(s 变量)。我想念什么?

谢谢。

在 C# 中,您尝试使用 UTF-8 编码将字节转换为字符串。然而,这是一个非常糟糕的主意——有许多字节序列在 UTF-8 编码的字符串中无效,并且进一步的序列将导致不可打印的字符。如果编码器遇到不构成有效 UTF-8 编码字符的字节序列(它会这样做,因为您没有做任何事情来确保您的字节序列是有效的 UTF-8 编码字符串), 它将插入一个替换字符。

在 Kotlin 中,您使用 new String(byte[]),它使用您系统的编码。您在这里遇到了类似的问题:虽然大多数字节会产生有效字符,但其中一些字符将无法打印。

所以您对 C# 和 Kotlin 使用了两种不同的编码(因此结果不同),但是您做了一些可能会给您无法打印的字符,或者可能用替换字符替换字节序列(因此不同的 MD5 哈希值看起来相同)。

(请注意,"unprintable characters" 可能只是不可见,但它们可能会做一些奇怪的事情,例如反转该页面上的文本方向,或者开始将它们周围的字符连接在一起!)

最好将字节转换为 base64 字符串或十六进制字符序列。这两者都确保每个可能的字节序列都以一种在不同语言中保持一致的方式变成可打印的字符。

对于 C#,使用 Convert.ToBase64String(data) to get a base64-encoded string, and BitConverter.ToString(data).Replace("-","") to get a hex-encoded string (although there are many way to do this)。

对于 Kotlin,使用 Base64.getEncoder().encodeToString(data) 获取 base64 编码的字符串,使用 data.joinToString("") { "%02x".format(it) } 获取十六进制编码的字符串。