如何正确的比较两个文件是否相同

一、问题切入

前段时间,有人问我:“如何判断两个文件是否相同?”,顿时让我想到了之前在技术博客上看到的文章,大部分都是说使用散列MD5的方法去判断,后来有个大神跟我说,他的结论:“两个相同的文件散列值一定相同,但是不同的文件有可能散列值相同”,为什么呢?

二、问题求解

首先回顾一下密码学中的散列函数

1、什么是散列算法呢?

散列算法可以把【任意尺寸】的数据(原始数据)转变为一个【固定尺寸】的“小”数据(叫“散列值”或“摘要”)。

2、散列算法摘要长度

对于某个具体的散列算法,得到的散列值长度总是固定的。散列值的长度又称“摘要长度”。
以下是常见散列算法的摘要长度:
CRC32 32比特(4字节)
MD5 128比特(16字节)
SHA1 160比特(20字节)

3、散列特点

  1. 不可逆性
      从刚才的描述看,散列似乎有点像压缩。其实捏,散列算法跟压缩算法是完全不同滴。压缩算法是可逆的(可以把压缩后的数据再还原),而【散列算法是不可逆的】。
      还有一些人把散列算法称为“加密算法”,这也是不对的。因为加密算法是可逆的(“加密”的逆操作就是“解密”),而散列算法是【不可逆】的。

  2. 确定性
      通过某种散列算法,分别对两个原始数据计算散列值。如果算出来的散列值不同,那么可以 100% 肯定这两段数据是不同的——这就是“确定性”。
      但反过来,如果这两段数据的散列值相同,则只能说,这两段数据【非常可能】相同。所谓的“非常可能”,就是说,还达不到百分百。具体原因,请看下一节“散列函数的可靠性”。

4、散列碰撞

(1)、散列碰撞定义

两段不同的原始数据,计算出相同的散列值。这种情况称之为“散列碰撞”或“散列冲突”。

(2)、碰撞类型

  1. 随机碰撞
      随机碰撞就像买彩票中大奖,完全是出于小概率的偶然因素——你碰巧遇见两个不同的数据(文件),具有相同的散列值。
      理论上讲,任何散列算法都存在随机碰撞的可能性,只是可能性有大有小。

  2. 人为碰撞
      人为碰撞就是说,有人(通常是恶意的攻击者)故意制造散列碰撞,以此来骗过“基于散列值的完整性校验”。

三、问题结论

由于散列算法存在碰撞,则我们使用散列MD5去判断两个文件是否相同是不科学的,正确的姿势应该是通过比较文件的内容去判断,如果是大文件,可以将文件切割成小片段进行比较。

下面是一段比较文件内容的shell脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

file1=$1
file2=$2

if [ -f $file1 ] && [ -f $file2 ]
then
diff $file1 $file2 > /dev/null
if [ $? != 0 ]
then
echo "Diff!"
else
echo "Same!"
fi
else
echo "$file1 or $file2 does not exist, please check filename."
fi