1. 什么是单向散列函数
首先,我们先通过Alice的一段故事,介绍一个可能要到单向散列函数的场景。然后,我们再来介绍单向散列函数需要具备的性质。
1.1 这个文件是不是真的呢
Alice在公司里从事软件开发。一天结束了,她保存了新开发的软件在电脑中。第二天来上班的时候,她忽然有个疑问:“今天打开的文件和昨天生成的文件是一样的吗?”。Alice需要知道从昨天到今天的这段时间内,Mallory是否篡改了文件的内容。
现在,Alice想知道自己手上的文件是不是真的。这种“是真的”的性质称为完整性(integrity)
,也称一致性。
稍微想一想我们就能找到一种确认文件完整性的简单方法 – 事先将文件保存在安全的地方随后进行对比。
不过,这种确认完整性的方法其实是毫无意义的。
- 如果事先能见文件保存在安全的地方,那根本就不需要确认完整性。
- 效率问题。如果需要确定完整性的文件非常巨大,那么文件的复制,保存以及比较都非常耗时。
是该“单向散列函数”出场了。
单向散列函数所生成的散列值,就相当于消息的“指纹”。
1.2 什么是单向散列函数
单向散列函数(one-way hash function)有一个输入和一个输出,其中输入称为消息(message),输出称为散列值(hash value)。单向散列函数可以根据消息的内容计算出散列值,而散列值就可以被用来检查消息的完整性。
这里的消息不一定是人类能够读懂的文件,也可以是图像文件或者声音文件。单向散列函数不需要知道消息实际代码的含义。无论任何消息,单向散列函数都会将它作为单纯的比特序列来处理,即根据比特序列计算出散列值。
散列值的长度和消息的长度无关。
无论消息是1比特,还是100MB,甚至是100GB,单向散列函数都会计算出固定长度的散列值。以SHA-256单向散列函数为例,它所计算出的散列值的长度永远是256比特(32字节)。
由于散列值很短,因此很容易处理和使用。用散列值处理完整性问题,关键点在于,我们不需要对比消息本身,只要对比单向散列函数计算出的散列值就可以了。
1.3 单向散列函数的性质
- 根据任意长度的消息计算出固定长度的散列值。
- 能够快速计算出散列值。
- 消息不同散列值不同。哪怕只有1个比特的改变,所产生的散列值完全不同。
- 具备单向性。无法通过散列值反算出消息。
1.4 关于术语
单向散列函数也称为消息摘要函数(message digest function)、哈希函数或者杂凑行数。
输入单向散列函数的消息也称为原像(pre-image)。
单向散列函数输出的散列值也称为消息摘要(message digest)或者指纹(fingerprint)。
完整性也称为一致性。
顺便说一句,单向散列函数中的“散列”的英文“hash”一词,原意是古法语中的“斧子”,后来被引申为“剁碎的肉末”,也许是用斧子一通乱剁再搅在一起的那种感觉吧。单向散列函数的作用,实际上就是将很长的消息剁碎,然后再混合成固定长度的散列值。
2. 单向散列函数的实际应用
2.1 检验软件是否被篡改
很多软件,尤其是安全相关的软件都会把通过单向散列函数计算出的散列值公布在自己的官方网站上。用户在下载到软件之后,可以自行计算散列值,然后与官网上公布的散列值进行对比。通过散列值,用户可以确认自己所下载到的文件与软件作者所提供的文件是否一致。
2.2 基于口令的加密
基于口令加密(Password Based Encryption, PBE)的原理是将口令和盐(salt,通过伪随机数生成器产生的随机数)混合后计算其散列值,然后将这个散列值用作加密的密钥。通过这样的方法能够防御针对口令的字典攻击。
2.3 消息认证码
使用单向散列函数可以构造消息认证码。消息认证码是将“发送者和接受者之间的共享密钥”和“消息”进行混合后计算出的散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。
2.4 数字签名
数字签名是显示社会中的签名和盖章这样的行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容直接施加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值施加数字签名。
2.5 伪随机数生成器
密码技术中所使用的随机数需要具备“事实上不可能根据过去的随机数列预测未来的随机数列”这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性。
2.6 一次性口令
一次性口令(one-time password)经常被用于服务器对客户端的合法性认证。在这种方式中,通过单向散列函数可以保证口令只在通信链路上传送一次(one-time),因此即使窃听者窃取了口令,也无法使用。
3. 单向散列函数的具体例子
- MD4、MD5
- SHA-1、SHA-256、SHA-384、SHA-512
- RIPEMD-160
- SHA-3(Keccak)
4. 应该使用哪种单向散列函数呢
- 首先,
MD5是不安全的,也不应该使用
。 SHA-1除了用于对过去生成的散列值进行校验之外,不应该被用于新的用途,而是应该迁移到SHA-2
。- SHA-2有效应对了针对SHA-1的攻击方法,因此是安全的,可以使用。
- SHA-3是安全的,可以使用。
和对称密码算法一样,我们不应该使用任何自制算法。
5. 对单向散列函数的攻击
暴力破解
,利用文件的冗余性生成具有相同散列值的另一个文件。这相当于一种试图破解单向散列函数的“弱抗碰撞性”的攻击。生日攻击
,攻击者Mallory所进行的攻击不是寻找生成特定散列值的消息,而是要找到散列值相同的两条消息,而散列值则可以使任意的。这是一种试图破解单向散列函数的“强抗碰撞性”的攻击。
6. 单向散列函数无法解决的问题
单向散列函数能够辨别出“篡改”,但无法辨别出“伪装”
。
当我们不仅需要确认文件的完整性,同时还需要确认这个文件是否真的属于Alice时,仅靠完整性检查是不够的,我们还需要进行认证
。
用于认证的技术包括消息验证码
和数字签名
。消息认证码能够向通信对象保证消息没有被篡改,而数字签名不仅能够向通信对象保证消息没有被篡改,还能够向所有第三方做出这样的保证。
认证需要使用密钥,也就是通过对消息附加Alice的密钥(只有Alice才知道的秘密消息)来确保消息真的属于Alice。