4

我有计算字符串的 SHA-256 哈希的代码,并注意到我从 Android 和 Oracle Java 7 获得了相同字符串的不同哈希。我的哈希代码将其String转换byte[]为:

byte[] data = stringData.getBytes("UTF-16");

使用 UTF-16 编码,我从 Oracle Java 和 Android Java 得到不同的结果。这是我正在散列的字符串:

// Test Code:
String toHash = "testdata";
System.out.println("Hash: " +DataHash.getHashString(toHash));

并使用 UTF-16 获取这些哈希:

Hash: a1112a0363a59097a701e38398e1fdfef3049358aee81b77ecaad2924a426bc5 [Oracle Java 7]
Hash: 811b723aee07c7a52456fc57a5683e73649075a373d341f7257bf73575111ba3 [Android 2.2]

但是,使用 UTF-8,我得到两个 JRE 的相同哈希值:

Hash: 810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50 [Oracle Java 7]
Hash: 810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50 [Android 2.2]

是否存在某种字节序问题,导致不同平台上的结果不同?我应该如何真正准备一个字符串以独立于平台的方式进行散列?

编辑: 哎呀,一旦您阅读更多有关 UTF-16 的信息,答案就很明显了。UTF-16 有两个版本(大端和小端)。您只需要指定 getBytes() 应该使用哪个版本,并且哈希值是相同的。选择以下之一:

  • UTF-16LE
  • UTF-16BE
4

2 回答 2

1

根据Orcale Java 的文档

解码时,UTF-16 字符集解释一个字节序标记来指示流的字节序,但如果没有字节序标记,则默认为大端;编码时,它使用大端字节序并写入一个大端字节序标记。

这意味着 plainUTF-16在 Oracle Java 中应该始终编码为 Big Endian。

然后从Android Java 文档

Charset            Encoder writes
UTF-16BE           BE, no BOM
UTF-16LE           LE, no BOM
UTF-16             BE, with BE BOM

因此,其中一个或文档中都存在错误。两者都必须是Big Endian,并且写BOM,所以应该没有任何区别。

一般来说,您应该更喜欢UTF-16BE/LEUTF-16但在这种情况下,它似乎是一个错误。

于 2012-12-18T10:21:01.263 回答
0

显示您的哈希代码,但它可能做错了什么。散列的结果是 abyte[]所以不需要首先从字符串转换为byte[]。用于将二进制哈希值转换为String使用 Base64 或十六进制编码。

于 2012-12-18T05:37:37.477 回答