哈希(Hash)算法又常称为指纹(Fingerprint)或摘要(Digest)算法,是非常基础也非常重要的一种算法。

哈希算法的起源可以追溯到 20 世纪 50 年代,它是为了解决信息完整性校验这个问题而被设计出来的。

在数位信息传输和存储中,需要确保信息没有被非法修改,这个过程就叫数据完整性校验。起初人们考虑使用加密算法来实现完整性校验,但加密算法有其自身的目的,不太适合完整性校验。


于是 20 世纪 50 年代,一些密码学家如拉尔夫·梅尔克尔提出使用 “ 操纵检测码 ” 来校验信息完整性。这种方法就是通过一个函数,从原始信息中提取出一个较短的固定长度的值,也就是哈希值。如果信息在传输中被修改,那么提取出来的哈希值也会改变。

这样,接收方只需要对收到的信息再次运行哈希函数,看提取出的哈希值是否和发送方提供的一致,就可以校验信息在传输过程中是否被修改过。这种方法既简单又有效,也不需要传输或者存储任何密钥。


之后,研究人员又提出了改进的哈希函数算法,如 MD5 、SHA-1 等,用来对付故意攻击。这使得基于哈希函数的信息完整性校验更加可靠。

随着时间的推移,哈希函数因为其独特的 “ 雪崩效应 ” 被广泛应用到数字签名、区块链、数据存储等诸多领域。它已经成为现代信息安全基础设施中不可或缺的组件之一。

所以可以说,哈希算法是为了解决信息传输中如何快速有效地校验完整性这个问题而被设计出来的,并最终演化成了现在数位世界必不可少的基础工具。它解决了一个重要的信任问题。


哈希算法可以将任意长度的二进制明文映射为较短的(通常是固定长度的)二进制串(哈希值),并且不同的二进制明文很难映射为相同的二进制串。

消息摘要是指采用单向哈希函数将需要计算摘要的数据提取摘要后生成一串固定长度的密文,这一串密文又称为数字指纹。数字指纹有固定的长度,并且不同的明文提取摘要生成的密文结果总是不同的,但是同样的明文产生的摘要是一致的。由于生成摘要的明文没有任何限制,但是得到的摘要却是定长的,因此必然有一些明文会产生相同的摘要,这种现象称为 “ 碰撞 ” 。为了避免这种情况的产生,哈希函数必须具备很好的抗碰撞性,这意味着在现有的计算资源(包括时间、空间、资金等)下,找到一个碰撞是不可行的。

消息摘要算法有一个特性,就是在输入消息的过程中,如果消息发生了细微的改变,如改变输入消息二进制数据中的一位,最后都会导致输出结果大相径庭。因此,消息摘要算法对于检测消息或密钥等信息对象中的微小变化非常有用。从中可以归纳出消息摘要算法的如下三个特点。


消息摘要算法的输入长度是任意的,输出长度是固定的。

对消息摘要算法给定输入,计算输出是很容易的。

常见的消息摘要算法:MD5 、SHA 、SHA256 、SHA512 、SM3 等。


消息摘要算法并不是一种加密算法,不能用于对信息的保护。但是消息摘要算法常用于对密码的保存。例如,用户登录网站需要通过用户名和密码来进行验证,如果网站后台直接保存了密码的明文,一旦发生了数据泄露,后果不堪设想,因为大多数用户都倾向于在多个网站使用相同的密码。为了避免这种情况的出现,可以利用哈希算法的特性,网站后台不直接保存明文密码,而保存用户密码的哈希值,这样当用户登录时比较密码最终的哈希值即可,如果一致,则证明登录密码是正确的,即使发生了数据泄露也很难根据单向哈希值推算出用户原始的登录密码。

但是,有时因为用户口令的强度太低,只使用一些简单的字符串,如 123456 ,攻击者可以通过对这些口令提前计算哈希值,得到口令和哈希值的一种映射关系来达到破解的目的。为了提高安全性,网站一般会通过加盐(Salt)的方式来计算哈希值,不直接保存用户密码的哈希值,而将密码加上一段随机字符(盐)再计算哈希值,这样把哈希值和盐分开保存可以极大地提高安全性。


消息摘要算法可以用于验证数据的完整性,但仅在数据的摘要与数据本身分开传输的情况下可以验证。否则攻击者可以同时修改数据和摘要,从而轻易地避开检测。消息验证码(Message Authentication Code,MAC)或密钥哈希值(Keyed Hash)是增加了身份验证来扩展摘要函数的密码学函数,只有拥有了摘要密钥,才能生成合法的 MAC 。

MAC 通常与密文一起使用。加密通信可以确保通信的机密性,却无法保证通信消息的完整性,如果攻击者 Mallory 的能力非常强大,以至于可以修改 Alice 和 Bob 通信中的密文,他就可以诱导 Bob 接收并相信伪造的信息。但是,当 MAC 和密文一起发送时,Bob 就可以确认收到的消息未遭到篡改。

任何消息摘要算法都可以作为 MAC 的基础,其中一个基础是基于摘要的消息验证码(Hash based Message Authentication Code, HMAC)。HMAC 的本质是将摘要密钥和消息以一种安全的方式交织在一起的函数。