2 回答
TA贡献1806条经验 获得超8个赞
问题由 Erwin Bolwidt 在评论中回答:
删除
digest
是破坏性的,您调用了两次 - 您从该方法返回的值不正确
在此之上:
没有必要调用
reset()
一个新的MessageDigest
调用
update(input)
而不是update(input, 0, input.length)
调用
digest(input)
而不是update(input)
+digest()
综合起来,以上所有意味着您的代码应该是:
private static byte[] getSHA1(byte[] input) throws NoSuchAlgorithmException {
return MessageDigest.getInstance("SHA-1").digest(input);
}
测试
byte[] input = "$&).6CXzPHw=2N_+isZK2908069825".getBytes("US-ASCII");
System.out.println(Arrays.toString(getSHA1(input)));
输出
[-96, -1, 78, 94, -67, -96, -113, 12, -31, 93, -10, -55, -5, 72, -2, -57, 52, -84, -117, 40]
这与Python 输出相同,除了打印为有符号字节和无符号字节:
[160, 255, 78, 94, 189, 160, 143, 12, 225, 93, 246, 201, 251, 72, 254, 199, 52, 172, 139, 40]
TA贡献2021条经验 获得超8个赞
在Java密码体系结构代表的算法类型,如对象MessageDigest,但也Cipher并Mac全部实施方案,使得它可以对大量的数据进行的零碎操作。他们通过更新内部状态和最终操作的方法来做到这一点,例如签名/验证或 - 对于MessageDigest-digest使用各种重载调用的单个操作。
这些算法还有一个共同点,即在调用“最终”操作时,对象的状态会直接重置为初始化后的状态。这种状态通常在调用init方法后达到。
SHA-1 等哈希算法不需要显式初始化,因此它们在实例化后直接返回到状态:它们尚未处理任何数据的状态。通过这种方式,可以重用散列算法来散列另一个值。这比实例化一个新对象稍微更有效。
来自JCA 开发人员指南:
计算完消息摘要后,消息摘要对象会自动重置并准备好接收新数据并计算其摘要。所有以前的状态(即提供给更新调用的数据)都将丢失。
因此,在您调用该digest()对象后,该对象将重置为尚未收到任何数据的状态。因此,第二次调用返回空八位字节字符串/字节数组的哈希值。
所以引用自维基百科:
SHA1("")
gives hexadecimal: da39a3ee5e6b4b0d3255bfef95601890afd80709
这与有符号字节相同[-38, 57, -93, -18, 94, 107, 75, 13, 50, 85, -65, -17, -107, 96, 24, -112, -81, -40, 7, 9]:您在注释中输入的值。
打印的正确哈希确实是
A0FF4E5EBDA08F0CE15DF6C9FB48FEC734AC8B28
大写十六进制或
[160, 255, 78, 94, 189, 160, 143, 12, 225, 93, 246, 201, 251, 72, 254, 199, 52, 172, 139, 40]
作为Python 中的无符号字节数组。请注意,Java改为使用有符号字节,因此这将等于
[-96, -1, 78, 94, -67, -96, -113, 12, -31, 93, -10, -55, -5, 72, -2, -57, 52, -84, -117, 40]
要计算这个,单个调用MessageDigest#digest(byte[] input): byte[]就足够了,在您的情况下,byte[] digest = msdDigest.digest(input)您可以先打印出来digest 并稍后返回保存字节数组的变量。
请注意,仍然不应在多个线程上同时使用散列;一次只能计算一个哈希值;这些类不是线程安全的。
添加回答
举报