我试图在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
存在以下问题:
-
在Python代码中错误地实现了移位操作:
在Python代码中,生成的哈希存储在类似字节的对象中,作为
0
和255
[1]之间的无符号整数值的列表,例如0xc8 = 11001000 = 200
。在Java中,整数存储为有符号值,其中二者的补码用于表示负数[2][3]。如果值0x8c
存储在byte
变量中,则该值将被解释为-56
。对于有符号值和无符号值,
>>
-运算符在二进制级别上产生不同的结果,因为它是一个保留符号[4][5][6]的算术移位运算符。示例:signed -56 >> 1 = 1110 0100 = -28 unsigned 200 >> 1 = 0110 0100 = 100
另一方面,
<<
-运算符不会导致上述问题,但可能导致无法用字节表示的值。示例: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](。
与
0xFF
一起使用位和(&
(可确保在Python代码中只考虑相关字节,类似于Java代码[5]。
-
有几种方法可以将类似列表/字节的对象转换为十六进制字符串(如在Java代码中(,例如使用[8][9]
bytes(xor_bytes).hex()
或使用[8][10](作为二进制字符串(
binascii.b2a_hex(bytes(xor_bytes))
-
在Python代码中,必须考虑salt的编码。由于salt已经作为二进制字符串传递(在Java代码中,它是作为字符串传递的(,因此必须在调用函数之前执行编码:
saltStr = 'MySalt' salt = saltStr.encode('ISO-8859-1')
为了实现与Java代码的功能一致性,salt必须作为字符串传递,并且必须在函数中执行编码。