【问题标题】:Replicating this Java hash in Python在 Python 中复制这个 Java 哈希
【发布时间】:2020-06-18 16:06:12
【问题描述】:

我正在尝试在 Python 中复制此哈希代码,但两种语言处理字节的方式不同,并生成非常不同的输出。

有人可以在这里指导我吗?

Java 代码(原始代码)

public static String hash(String filePath, String salt) {

        String finalHash = null;
        Path path = Paths.get(filePath);

        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] data = Files.readAllBytes(path);
            byte[] dataDigest = md.digest(data);
            byte[] hashDigest = md.digest(salt.getBytes("ISO-8859-1"));
            byte[] xorBytes = new byte[dataDigest.length];

            for (int i = 0; i < dataDigest.length && i < hashDigest.length; i++) {
                xorBytes[i] = (byte) (dataDigest[i] << 1 ^ hashDigest[i] >> 1);
            }

            finalHash = (new HexBinaryAdapter()).marshal(xorBytes);
        } catch (IOException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return finalHash;
    }

Python 代码(由我翻译)

def generate_hash(file_path: str, salt: bytes) -> str:
    with open(file_path, 'rb') as f:
        data = f.read()

    hashed_file = sha1(data).digest()
    hashed_salt = sha1(salt).digest()

    xor_bytes = []

    for i in range(len(hashed_file)):
        xor_bytes.append((hashed_file[i] << 1 ^ hashed_salt[i] >> 1))

    return ''.join(map(chr, xor_bytes))  # This is probably not equivalent of HexBinaryAdapter

【问题讨论】:

    标签: java python hash byte


    【解决方案1】:

    存在以下问题:

    • 移位操作在Python代码中实现错误:

      在 Python 代码中,生成的哈希存储在类似字节的对象中,作为 0255 [1] 之间的 无符号 整数值列表,例如0xc8 = 11001000 = 200。在Java 中,整数被存储为有符号 值,其中二进制补码用于表示负数[2][3]。如果存储在byte 变量中,值0x8c 将被解释为-56

      &gt;&gt;-运算符在 二进制 级别上为有符号和无符号值生成不同的结果,因为它是一个 算术 移位运算符,保留符号 @987654324 @[5][6]。示例:

      signed       -56 >> 1 = 1110 0100 = -28
      unsigned     200 >> 1 = 0110 0100 = 100
      

      另一方面,&lt;&lt;-运算符不会导致上述问题,但会导致无法用字节表示的值。示例:

      signed       -56 << 1 = 1 1001 0000 = -112
      unsigned     200 << 1 = 1 1001 0000 = 400
      

      由于这些原因,在 Python 代码中的以下行

      xor_bytes.append((hashed_file[i] << 1 ^ hashed_salt[i] >> 1))
      

      必须替换为

      xor_bytes.append((hashed_file[i] << 1 ^ tc(hashed_salt[i]) >> 1) & 0xFF)
      

      在哪里

      def tc(val):
          if val > 127:
              val = val - 256
          return val
      

      确定二进制补码表示的负值(或更复杂的 按位 运算符请参阅[7])。

      位与 (&amp;) 与0xFF 一起使用可确保在Python 代码中仅考虑相关字节,类似于Java 代码[5]


    • 有几种方法可以将类似列表/字节的对象转换为十六进制字符串(如在 Java 代码中),例如与[8][9]

      bytes(xor_bytes).hex() 
      

      [8][10](作为二进制字符串)

      binascii.b2a_hex(bytes(xor_bytes))
      


    • 在 Python 代码中,必须考虑 salt 的编码。由于 salt 已经作为 binary 字符串传递(在 Java 代码中它作为字符串传递),因此必须在调用函数之前执行编码:

      saltStr = 'MySalt'
      salt = saltStr.encode('ISO-8859-1')
      

      为了与 Java 代码功能一致,盐必须作为字符串传递,并且必须在函数内执行编码。


    【讨论】:

    • 我现在正在比较数据,并注意到 java 与 python 中的字节问题。通过现在提供的更正,代码返回相同的输出。非常感谢!!
    猜你喜欢
    • 1970-01-01
    • 2014-02-01
    • 2021-07-03
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-31
    相关资源
    最近更新 更多