博主:爱码叔
个人博客站点: icodebook
公众号:漫话软件设计
专注于软件设计与架构、技术管理。擅长用通俗易懂的语言讲解技术。对技术管理工作有自己的一定见解。文章会第一时间首发在个站上,欢迎大家关注访问!
更多密码学文章
信息加密保证了机密性。这是否意味着只要合理使用加密技术就足够安全了呢?
答案肯定是否定的。加密技术仅仅解决了信息传递过程中的机密性。但如果信息被攻击者截获,篡改了里面的内容,再用公钥加密发出来,接收人是无法识别的。这里就需要使用电子签名来保证信息的完整性。
先不急着说电子签名,我们先看看基础知识 - 散列值。
散列值可以认为是信息的“指纹”。众所周知,指纹可以证明人的身份。每个人的指纹都不一样,通过对比指纹,可以确定留下指纹的人是谁。
这里用指纹做类比容易造成误解。请注意散列值是信息的指纹,也就是说 “指纹” 只能证明 “信息” 的身份,就像指纹用来证明人的身份一样。请不要理解为散列值是发信人的指纹。
我们在三方软件网站下载的某些软件,解压后会有一个 md5 字符串。这个东西就是你下载的软件文件的散列值。我们可以用下载文件重新生成 md5 值与之做对比。如果两者一致,说明软件文件没有被篡改。否则你下载的软件已经被篡改,不要再安装。
md5 其实就是一种散列算法。md5的全称是 Message-Digest Algorithm 5。散列算法的作用就是将一大段信息计算出固定长度的字符串。这个固定长度的字符串就叫做散列值。从 md5 的全称也可以看出,它是信息的摘要,并不是完整信息。
计算散列值的函数就叫做散列函数。散列函数也称为单向散列函数。函数的输入是任意长度的信息,输出为固定长度且很短的散列值。
散列函数应该具备如下特性:
同样信息,无论计算几次,算出的散列值要一致。只有这样,散列值才能用来做信息完整性的验证。不同信息散列值不同,确保不会被错误验证。第三点是对计算性能的要求。
MD5
MD5是Rivest于1991年设计的散列函数。产生的散列值长度为128bit。但是MD5已经被攻破,现在可以生成两个相同MD5的不同消息。所以不建议继续使用MD5.
SHA-1
SHA-1 是由NIST(Natinal institute of Standards and Technology)于1993 年设计的散列函数。其产生的散列值长度为160 bit。由于SHA-1也已经被攻破,所以不推荐使用。
SHA-256、SHA-384、SHA512
这些散列函数都是由NIST设计。分别对应散列值长度256、384、512 bit。统称为SHA-2。SHA-2目前还未被攻破,可以放心使用。
散列函数用来生成散列值。散列值是消息的指纹,意味着当消息被改动后,生成的散列值也会发生改变。并且不同消息很难生成同样的散列值。根据这些特性,散列函数有如下应用:
存储在数据库中的用户密码,肯定不能是明文的,否则任何接触到数据库的人员会看到系统中所有用户的密码,毫无安全可言。一种可行的方案是,将密码加盐后,生成散列值,然后存储到DB中。用户登录时,程序使用输入的密码加盐,重新计算散列值,和DB中保存的散列值对比。如果一致,认为登录成功。这样避免了存储明文密码。
散列值确保消息没有被篡改,而数字签名还能保证信息的来源可靠。数字签名是 01 世界中的签字画押。对完整信息生成数字签名非常耗时。既然散列值能 “代表” 完整信息,那么可以先将完整信息生成散列值,然后对信息的散列值生成数字签名。这就像盖章一般也不会每页都盖,通常盖在最后一页就可以了。
消息认证码用于通信过程中检查传输错误和篡改。通过通信双方共享的密钥和信息内容生成散列值,这样发送方和接收方就可以进行检查。
消息认证码使用消息和通讯双方共享的密钥生成的散列值。散列值无法用于通信方伪装的识别,但是加上共享密钥后,可以做到这一点。只有使用协商好的密钥生成认证码,通信对方才能生成同样的认证码。如果伪装者不知道密钥,就无法生成正确的认证码。
可以看出消息认证码的机制和对称加密很像,需要通信双方共享密钥。如果看过我之前关于对称加密的文章《加密就像玩魔方----详解对称加密》,应该了解此处存在密钥配送问题。要解决密钥配送问题之前的文章中也有描述(《图文彻底搞懂非对称加密(公钥密钥)》),这里就不再详述。
通信双方可以使用消息认证码来验证消息来自对方。但是由于二者持有的密码是一样的,因此无法向第三方证明某条消息是谁发出的。谁都有可能用互相知道的密钥生成一段消息的认证码。正是如此,任何一方都可以否认消息是我发出的。
以上是消息认证码的局限,数字签名可以解决这些问题。
散列值可以看作信息的身份证明,接收方重新为信息生成散列值,和发送方提供的散列值对比,可以证明信息没有被篡改。但是无法证明信息的发送方是谁。
数字签名好比发送方给信息签名盖章、按手印。收信方通过对比手印,判断是否来自合法发送方。
发送方的数字签名,不同接收方都可以验证。是不是有些似曾相识?公钥密钥的特点是任何发送方都可以对信息加密,只有接收方可以解密。是不是反过来了?没错,其实数字签名就是公钥密钥的逆向应用。
数字签名也需要用到公钥和私钥。信息发送方使用自己的私钥进行签名。这非常好理解,因为私钥是发送方的身份象征,所以需要用私钥签名。接收方则使用发送方的公钥进行验证。这样任何与发送方通讯的人都可以验证发送方签名。只要发送方保护好自己的私钥,攻击者无法仿造发送方签名。
对称加密需要对整条信息加密,原因在于解密需要还原出完整明文。但是签名不一样,签名只需要验证信息的合法性,并不需要还原签名用到的信息数据。而且由于对整条信息的签名运算非常耗时,实际应用中并不会用完整信息生成签名,而是使用完整信息的散列值来生成签名。因为散列值是信息的指纹,代表了信息的身份。所以只要通信双方协商好散列值算法,同样的信息必然运算出相同的散列值。签名和验签也就能正常工作。
需要注意,虽然数字签名使用密钥加密技术。但其实并不是为了确保信息的机密性。私钥是为了确保只有发信人才能签名,其他任何人无法签名。这样接收方通过对签名的验证,就可以确保消息来源安全。
前面用按手印作类比,帮助理解数字签名。没错,数字签名的作用和手印是相似的。
更贴切来说,每个人指纹不同,其实是每个人生成签名的私钥不同,并不是数字签名不同。
手印有一个非常大的局限性就是,不管信息内容如何手印都是一样的。这就出现了一个非常大的漏洞,我可以先让别人在白纸上按手印,然后内容我随便写。此外签名后的文件如需修改非常不方便。拿合同举例,任何内容的变化,都需要在变化处再加按手印。
此时,数字签名的优势就凸显出来。只要信息不同,数字签名就是不同的。拿白纸骗手印,再往上编内容的情况是绝不可能出现的。
我们可以将数字签名看作是将信息和指纹通过特定算法计算出来的信息。
数字签名识别伪装的同时,还能够识别篡改。从这个角度看,数字签名比签字画押更具优势。
数字签名验证的是信息篡改和伪装。但数字签名无法保证信息的机密性。 如果对安全有很高的要求,可以同时使用数字签名和加密。在金融领域,这也是常见的安全方案。
数字签名不需要被保密,也可以被任意复制。数字签名没有单独存在的意义,他必须和信息同时出现才有价值。就像在白纸上的签字没有任何意义,只有在文件签字才有意义。
由于采用了非对称加密的方式签名,所以只有信息发送方才能生成签名。这和使用对称加密生成的消息认证码是不同的。因为只有一方能生成签名,所以签名方无法否认信息的签名不是自己生成的。
如本文所述,数字签名解决了对发送方的信任问题。等等,真的解决了吗?我怎么知道手中的发送方公钥就是合法通信方的公钥呢?有没有可能攻击者一开始就伪装成合法通信方,把他的公钥给了我?如果是这样的话,整套数字签名的验证体系就被彻底攻破了!
为了解决这个问题,证书被引入进来。证书其实就是可信第三方对公钥的签名。但是在验证可信第三方的签名时,又需要使用可信第三方的公钥。可信第三方就真的可信吗?谁来证明可信第三方的公钥合法性?这样就陷入了信任链条之中。