【发布时间】:2021-08-25 00:50:17
【问题描述】:
问题是关于在 Java 中创建哈希的正确方法:
假设我有一个正的 BigInteger 值,我想从中创建一个哈希值。让我们假设messageDigest 的下面实例是(SHA-256) 的有效实例
public static final BigInteger B = new BigInteger("BD0C61512C692C0CB6D041FA01BB152D4916A1E77AF46AE105393011BAF38964DC46A0670DD125B95A981652236F99D9B681CBF87837EC996C6DA04453728610D0C6DDB58B318885D7D82C7F8DEB75CE7BD4FBAA37089E6F9C6059F388838E7A00030B331EB76840910440B1B27AAEAEEB4012B7D7665238A8E3FB004B117B58", 16);
byte[] byteArrayBBigInt = B.toByteArray();
this.printArray(byteArrayBBigInt);
messageDigest.reset();
messageDigest.update(byteArrayBBigInt);
byte[] outputBBigInt = messageDigest.digest();
现在我只假设下面的代码是正确的,因为根据测试,我生成的哈希值与以下代码相匹配:
但是我不确定我们为什么要执行以下步骤,即 因为在 digest() 调用之后返回的字节数组是有符号的,在这种情况下它是负数,我怀疑我们确实需要将它转换为正数,即我们可以使用这样的函数。
public static String byteArrayToHexString(byte[] b) {
String result = "";
for (int i=0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
因此:
String hex = byteArrayToHexString(outputBBigInt)
BigInteger unsignedBigInteger = new BigInteger(hex, 16);
当我从新的十六进制字符串构造 BigInteger 并将其转换回字节数组时,我看到符号位,即最高有效位,即最左边的位,设置为 0,这意味着数字是正的,而且整个字节由零构成( 00000000 )。
我的问题是:是否有任何 RFC 描述了为什么我们需要始终将哈希转换为“正”无符号字节数组。我的意思是即使摘要调用后产生的数字是负数,它仍然是有效的哈希,对吗?因此,我们为什么需要那个额外的程序。基本上,我正在寻找一篇论文:描述我们需要这样做的标准或 rfc。
【问题讨论】:
-
我的 2 美分: 1. 这很可能是因为通过简单地执行 hashCode % bucketSize; 可以更容易地找到这样一个 hashCode 的哈希桶。但我不是这方面的专家。 2. 你的代码风格不好,因为 hashCode() 的时间复杂度随着输入字符串的长度而增长,并且是 O(n) 实现。相信我,我们曾经在这样的实施上遇到过严重的问题。您可能希望将其更改为整个字符串的一部分的某些特征固定大小的数组。
-
你会如何改变它。大整数不是固定长度,字节数组长度也不是固定的。我能想象的一件事是验证 BigInteger 长度不大于某个 Max 长度。但除此之外,你有一个你提议的例子吗?
-
你的问题不是关于散列,而是关于将大整数转换为字节数组。
-
好吧,我不会那样说,因为 toByteArray() 方法将大整数转换为字节数组,但是为了获得相同的结果,即与 fileformat.info 中所示的哈希相同,然后我需要对该字节数组应用一些额外的逻辑。您可以称其为关于散列或将大整数转换为字节数组,最后问题非常精确,即为什么我们需要额外的最后一步,即在我的情况下是方法 byteArrayToHexString 并且是否有标准/RFI 我可以阅读更多关于它。
-
@Tito 通常您会选择整个字符串中最具代表性的部分(取决于您的业务场景)。例如,如果您知道前 24 个字符是关键,为什么不直接使用它呢?或者,您为字节长度设置一个上限,例如 128,然后截断任何内容。
标签: java hash cryptography sha256