Python和Java中的迭代哈希返回不同的值

4
我将尝试将一个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;
}
2个回答

2

在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

2
两种语言对update()digest()的运行方式不同。
Python文档中update()的说明如下:
更新哈希对象使用字符串arg。重复调用等效于一次调用所有参数的连接: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条目。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接