【问题标题】:MD5 Hashcode in C# and Kotlin(Java)C# 和 Kotlin(Java) 中的 MD5 哈希码
【发布时间】:2019-10-21 14:16:18
【问题描述】:

我正在努力解决 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 变量)。我想念什么?

谢谢。

【问题讨论】:

    标签: java c# kotlin md5


    【解决方案1】:

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

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

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

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

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

    对于 C#,使用 Convert.ToBase64String(data) 获取 base64 编码的字符串,使用 BitConverter.ToString(data).Replace("-","") 获取十六进制编码的字符串(尽管有 are many way to do this)。

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

    【讨论】:

    • "你不能把任何旧的字节序列变成一个字符串。" - 是的,您可以,只是不能将其视为任意编码的字符串。将其转换为base64仍然是“将其转换为字符串”。没有编码的概念,就没有“有效字符串”的概念。 (实际上,任何字节序列 is 都是有效的 ISO-8859-1 字符串。)我完全同意答案的主旨,但在对第一段进行排序之前,我不能凭良心对其进行投票: )
    • @JonSkeet 希望这更正确,感谢您的反馈
    猜你喜欢
    • 2020-12-08
    • 2014-05-19
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    • 2011-02-24
    • 1970-01-01
    相关资源
    最近更新 更多