迭代散列在Python和Java中返回不同的值

我正在尝试将python(2.7)脚本移植到Java。 它多次迭代sha256哈希,但最终会得到不同的结果。 我注意到他们第一次返回相同的结果,但从那里它就不同了。

这是Python实现:

import hashlib def to_hex(s): print " ".join(hex(ord(i)) for i in s) d = hashlib.sha256() print "Entry:" r = chr(1) to_hex(r) for i in range(2): print "Loop", i d.update(r) r = d.digest() to_hex(r) 

在Java中:

 import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class LoopTest { public static void main(String[] args) { MessageDigest d; try { d = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { System.out.println("NoSuchAlgorithmException"); return; } System.out.println("Entry:"); byte[] r = new byte[] {1}; System.out.println(toHex(r)); for(int i = 0; i < 2; i++) { System.out.printf("Loop %d\n", i); d.update(r); r = d.digest(); System.out.println(toHex(r)); } } private static String toHex(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length); for (byte b: bytes) { sb.append(String.format("0x%02X ", b)); } return sb.toString(); } } 

对于python,输出是:

 $ python looptest.py Entry: 0x1 Loop 0 0x4b 0xf5 0x12 0x2f 0x34 0x45 0x54 0xc5 0x3b 0xde 0x2e 0xbb 0x8c 0xd2 0xb7 0xe3 0xd1 0x60 0xa 0xd6 0x31 0xc3 0x85 0xa5 0xd7 0xcc 0xe2 0x3c 0x77 0x85 0x45 0x9a Loop 1 0x98 0x1f 0xc8 0xd4 0x71 0xa8 0xb0 0x19 0x32 0xe3 0x84 0xac 0x1c 0xd0 0xa0 0x62 0xc4 0xdb 0x2c 0xe 0x13 0x58 0x61 0x9a 0x83 0xd1 0x67 0xf5 0xe8 0x4e 0x6a 0x17 

对于java:

 $ java LoopTest Entry: 0x01 Loop 0 0x4B 0xF5 0x12 0x2F 0x34 0x45 0x54 0xC5 0x3B 0xDE 0x2E 0xBB 0x8C 0xD2 0xB7 0xE3 0xD1 0x60 0x0A 0xD6 0x31 0xC3 0x85 0xA5 0xD7 0xCC 0xE2 0x3C 0x77 0x85 0x45 0x9A Loop 1 0x9C 0x12 0xCF 0xDC 0x04 0xC7 0x45 0x84 0xD7 0x87 0xAC 0x3D 0x23 0x77 0x21 0x32 0xC1 0x85 0x24 0xBC 0x7A 0xB2 0x8D 0xEC 0x42 0x19 0xB8 0xFC 0x5B 0x42 0x5F 0x70 

造成这种差异的原因是什么?

编辑:

感谢@dcsohl和@Alik的答案我现在明白了原因。 由于我将Python脚本移植到Java,我必须保持Python的原样,因此我修改了Java程序,如下所示:

 byte[] r2 = new byte[]{}; for(int i = 0; i < 2; i++) { System.out.printf("Loop %d\n", i); d.update(r); r2 = d.digest(); System.out.println(toHex(r2)); byte[] c = new byte[r.length + r2.length]; System.arraycopy(r, 0, c, 0, r.length); System.arraycopy(r2, 0, c, r.length, r2.length); r = c; } 

这两种语言以不同方式运行update()digest()

update()的python文档说

使用字符串arg更新哈希对象。 重复调用相当于一次调用,并连接所有参数: m.update(a); m.update(b) m.update(a); m.update(b)相当于m.update(a+b)

我使用shell sha256sum命令测试了这个。

 echo -n '\0x01\0x4b\0xf5\0x12\0x2f\0x34\0x45\0x54\0xc5\0x3b\0xde\0x2e\0xbb\0x8c\0xd2\0xb7\0xe3\0xd1\0x60\0xa\0xd6\0x31\0xc3\0x85\0xa5\0xd7\0xcc\0xe2\0x3c\0x77\0x85\0x45\0x9a' | sha256sum 981fc8d471a8b01932e384ac1cd0a062c4db2c0e1358619a83d167f5e84e6a17 *- 

你从\ 0x01开始,这是第一个字节,然后其余的字节是0x01的哈希值。 结果哈希与您的Python输出匹配。

现在看看这个 – 我省略了初始的\ 0x01并得到了哈希 – 它与你的Java输出相匹配。

 > echo -n '\0x4b\0xf5\0x12\0x2f\0x34\0x45\0x54\0xc5\0x3b\0xde\0x2e\0xbb\0x8c\0xd2\0xb7\0xe3\0xd1\0x60\0xa\0xd6\0x31\0xc3\0x85\0xa5\0xd7\0xcc\0xe2\0x3c\0x77\0x85\0x45\0x9a' | sha256sum 9c12cfdc04c74584d787ac3d23772132c18524bc7ab28dec4219b8fc5b425f70 *- 

但为什么? 不应该包含最初的\ 0x01吗? 它会是,除了digest()的javadoc说:

通过执行填充等最终操作来完成哈希计算。 进行此调用后,将重置摘要。

因此,当您在java中调用digest()时,您的初始\ 0x01会被删除,而您只是在没有初始\ 0x01条目的情况下消化旧摘要。

在Java中, d.digest返回消息摘要并最终重置摘要。

在Python中, d.digest不会重置摘要。 因此,重复调用d.update实际上与先前调用传递的内容连接

你可以简单地在循环中放入d = hashlib.sha256()

 import hashlib def to_hex(s): print " ".join(hex(ord(i)) for i in s) print "Entry:" r = chr(1) to_hex(r) for i in range(2): print "Loop", i d = hashlib.sha256() d.update(r) r = d.digest() to_hex(r) 

获得与java程序相同的结果

 Entry: 0x1 Loop 0 0x4b 0xf5 0x12 0x2f 0x34 0x45 0x54 0xc5 0x3b 0xde 0x2e 0xbb 0x8c 0xd2 0xb7 0xe3 0xd1 0x60 0xa 0xd6 0x31 0xc3 0x85 0xa5 0xd7 0xcc 0xe2 0x3c 0x77 0x85 0x45 0x9a Loop 1 0x9c 0x12 0xcf 0xdc 0x4 0xc7 0x45 0x84 0xd7 0x87 0xac 0x3d 0x23 0x77 0x21 0x32 0xc1 0x85 0x24 0xbc 0x7a 0xb2 0x8d 0xec 0x42 0x19 0xb8 0xfc 0x5b 0x42 0x5f 0x70