重用hashlib.md5可以计算相同字符串的不同值

时间:2018-05-25 19:45:35

标签: python hash cryptography md5

这是我的第一个测试代码:

   import hashlib
   md5Hash = hashlib.md5()
   md5Hash.update('Coconuts')
   print md5Hash.hexdigest()

   md5Hash.update('Apples')
   print md5Hash.hexdigest()

   md5Hash.update('Oranges')
   print md5Hash.hexdigest()

这是我的第二大块代码:

    import hashlib
    md5Hash = hashlib.md5()
    md5Hash.update('Coconuts')
    print md5Hash.hexdigest()

    md5Hash.update('Bananas')
    print md5Hash.hexdigest()

    md5Hash.update('Oranges')
    print md5Hash.hexdigest()

但第一个代码的输出是:

    0e8f7761bb8cd94c83e15ea7e720852a
    217f2e2059306ab14286d8808f687abb
    4ce7cfed2e8cb204baeba9c471d48f07

第二个代码是:

   0e8f7761bb8cd94c83e15ea7e720852a
   a82bf69bf25207f2846c015654ae68d1
   47dba619e1f3eaa8e8a01ab93c79781e

我将第二个字符串从'Apples'替换为'Bananas',第三个字符串仍然保持不变。但是我仍然得到第三个字符串的不同结果。 Hashing应该每次都有相同的结果。 我错过了什么吗?

3 个答案:

答案 0 :(得分:4)

因为您正在使用update方法,所以md5Hash对象将被重用于3个字符串。所以它基本上是连接在一起的3个字符串的哈希值。因此,更改第二个字符串也会改变第三个字符的结果。

您需要为每个字符串声明一个单独的md5对象。使用循环(符合python 3的代码需要字节前缀BTW,并且也适用于python 2):

import hashlib
for s in (b'Coconuts',b'Bananas',b'Oranges'):
    md5Hash = hashlib.md5(s)  # no need for update, pass data at construction
    print(md5Hash.hexdigest())

结果:

0e8f7761bb8cd94c83e15ea7e720852a
1ee31b77d0697c36914b99d1428f7f32
62f2b77089fea4c595e895901b63c10b

请注意,这些值现在不同了,但至少它是每个字符串的MD5,独立计算。

答案 1 :(得分:3)

hashlib.md5.update() 将数据添加到哈希中。它不能取代现有的价值观;如果要散列新值,则需要初始化新的hashlib.md5对象。

您要散列的值是:

"Coconuts"               -> 0e8f7761bb8cd94c83e15ea7e720852a
"CoconutsApples"         -> 217f2e2059306ab14286d8808f687abb
"CoconutsApplesOranges"  -> 4ce7cfed2e8cb204baeba9c471d48f07

"Coconuts"               -> 0e8f7761bb8cd94c83e15ea7e720852a
"CoconutsBananas"        -> a82bf69bf25207f2846c015654ae68d1
"CoconutsBananasOranges" -> 47dba619e1f3eaa8e8a01ab93c79781e

答案 2 :(得分:0)

预期结果

您期望的是通常的加密库应该是您期望的。在大多数加密库中,在调用完成计算的方法(例如hexdigest)后重置哈希对象。似乎hashlib.md5使用了替代行为。

hashlib.md5

的结果

MD5要求输入填充1位,零个或多个0位以及输入的长度(以位为单位)。然后计算最终的哈希值。 hashlib.md5内部似乎使用单独的变量执行最终计算,在没有最终填充的情况下对每个字符串进行散列后保持状态。

因此,哈希的结果是前面的字符串与给定字符串的串联,后跟正确的填充,duskwulf pointed out in his answer

hashlib正确记录了这一点:

  

hash.digest()

     
    

返回到目前为止传递给update()方法的字符串的摘要 。这是一个digest_size字节的字符串,可能包含非ASCII字符,包括空字节。

  

  

hash.hexdigest()

     
    

digest()类似,但摘要以双倍长度的字符串形式返回,仅包含十六进制数字。这可用于在电子邮件或其他非二进制环境中安全地交换值。

  

hashlib.md5

的解决方案

由于似乎没有reset()方法,您应该为要创建的每个单独哈希值创建一个新的md5对象。幸运的是,哈希对象本身相对较轻(即使哈希本身不是这样),所以这不会占用很多CPU或内存资源。

讨论差异

对于散列本身,重置终结器中的散列可能没有多大意义。但它对签名生成很重要:您可能希望初始化相同的签名实例,然后使用它生成多个签名。哈希函数应该重置,以便它可以计算多条消息的签名。

有时,应用程序需要在多个输入上使用聚合哈希,包括中间哈希结果。在这种情况下,使用Merkle tree个散列,其中中间散列本身再次进行散列。

如上所述,我认为这是hashlib作者的糟糕API设计。对于密码学家来说,它肯定不会遵循最不惊讶的规则。