浅谈网络安全(二)- 报文完整性与数字签名

在上一篇笔记中,我们提到安全的网络通信有三点基本特性:保密性、报文完整性和身份认证。这篇笔记我们就来聊聊报文完整性和身份认证,二者是息息相关的。
还是用 Alice 和 Bob 来举例,当 Bob 收到一条来自 Alice 的消息,他需要验证如下内容:

  • 这条消息在传输过程中没有被篡改 - 验证报文完整性
  • 这条消息确实是 Alice 发的 - 进行身份认证

加密哈希函数

在报文完整性校验的实现中,我们会用到加密哈希函数

一般哈希函数具有如下特性:

  1. 接受任意长度的输入
  2. 产生固定长度的输出
  3. 计算时间在合理范围内

像之前在介绍可靠数据传输中用于错误检测的校验和 (checksum) 和循环冗余校验(Cyclic Redundant Check) 都满足这些特性。
相比于这些一般哈希函数,加密哈希函数还要满足一个特性:

  1. 无碰撞性 (collision free) - 对于两个不同的输入,其输出的哈希值一定是不同的。

常用的加密哈希算法有:MD5(虽然现在有了彩虹表可以破解很多常见密文,但是目前应用还是挺广泛的), SHA-1, SHA-2, Whirlpool 等。

举个例子,下面这段 js 代码对字符串 hello, world 采用了 md5 加密,控制台将输出对应的哈希值 e4d7f1b4ed2e42d15898f4b27b019da4

1
2
3
const crypto = require('crypto')
let hash = crypto.createHash('md5').update("hello, world").digest("hex")
console.log(hash)


MAC - Message Authentication Code

别激动,此 MAC 非苹果家的 mac,而是 Message Authentication Code 的缩写,翻译有好多种,我也不知道叫哪个比较合适,这里就直接简称 MAC 了。
那 MAC 到底是什么呢?它又如何保证报文完整性呢?
首先,我们假设 Alice 和 Bob 共享一个只有二人知道的小秘密 (shared secret),用 s 表示,当 Alice 和 Bob 通信时:

  1. Alice 想给 Bob 发送消息 m,于是她把 ms 拼起来得到 m+s,然后使用哈希算法得到哈希值 H(m+s),这个 H(m+s) 就是 Message Authentication Code (MAC)
  2. 然后 Alice 把 (m, H(m+s)) 一起发给 Bob
  3. Bob 收到消息 (m, h),并且 Bob 知道 s,那么 Bob 就可以根据 ms 计算出 H(m+s),如果 H(m+s) = h,那么 Bob 就认为消息一切正常

MAC 同时满足了报文完整性的检查和身份的认证:

  1. 对哈希值的检验可以判断报文完整性,得到相同的哈希值意味着消息和秘密都是一致的
  2. 而秘密只有 Alice 和 Bob 两人知道,因此 Bob 可以认为发送方身份通过了验证(当然,若秘密泄漏了,则另当别论)

上述描述的 MAC 的基本思想是基于哈希算法,这也是如今最主流的一种 MAC 标准,详细信息见 HMAC(Hash-based MAC)](https://en.wikipedia.org/wiki/HMAC)。

使用基于哈希的 MAC 校验报文完整性不需要加密算法,这非常适合用于一些对保密性没有要求的场景。比如在网络层使用链路状态路由算法同步路由信息时,就不要求保密性,毕竟所有消息本身就是同步到所有路由器上的。

除此之外,还有采用分组加密算法实现的 MAC,比如OMAC, CBC-MAC, PMAC等,更多 MAC 相关的信息见 MAC - wiki


数字签名

在现实世界中,很多文件的真实性或有效性的依据都是由某个授权者的手写签名来决定,数字签名则是一种在网络中保障文件真实性的手段。与手写签名一样,数字签名要可验证,且具有不可否认性

一条经过数字签名的消息具备如下属性:

  • 接收方可以验证签名者的身份
  • 消息内容不可被篡改或伪造

如何实现数字签名呢?
MAC 行不行?不行。MAC 中通信双方共享一个 secret,这意味当 Alice 使用它对某个文件进行签名后,获得这个签名文件的第三方并不能确定这个文件就是 Alice 签名的,因为 Bob 拥有同样的 secret。

最常见的实现是采用非对称密钥加密技术。你会发现公钥私钥机制很天然地满足了身份验证、不可否认性和不可伪造这几大要求。下面我们就来看看如何使用非对称密钥加密实现数字签名吧。

假设 Alice 现在需要对某个文件 m 进行签名,那么她可以使用她的私钥对文件进行加密,加密后的密文即为经过数字签名的文件。为什么说这份密文满足了数字签名的要求:

  • 只有使用 Alice 的私钥才能生成这份密文,而 Alice 的私钥理论上只有 Alice 自己拥有,因此可以验证签名者的身份和消息的完整性
  • 如果文件被修改为 m',那么再次使用 Alice 的私钥对其签名将得到不同的签名文件,因此 Alice 也没法对曾经签过名的文件抵赖(不可否认性)

消息摘要

由于上述数字签名方式是对整个文件进行私钥加密,这种做法有其局限性:

  • 针对整个文件进行加密,将保密性与认证耦合在一起了,但是很多时候保密性并不是必须的
  • 非对称加密计算量复杂,非常消耗计算资源

针对不需要保密的场景,我们可以使用前面提到的加密哈希函数对文件生成消息摘要,然后对消息摘要进行签名,并将其与原文件一起发送。

采用消息摘要数字签名的过程图示如下:
Digital singature


MAC vs. 数字签名

首先,MAC 中只用到了哈希函数,既没有使用对称加密也没有使用非对称加密;而在数字签名中,我们先使用哈希函数得到消息摘要,然后利用了非对称加密对摘要进行签名。相比之下,MAC 是比数字签名更“轻量级”的技术,由于数字签名用到了非对称加密技术,通常需要公钥基础设施(PKI)的支持。
其次,这两者使用的场景其实不太一样。MAC 应用于提供两个通信的对象之间消息的完整性,对于接收方而言,它要验证的身份是与之通信的发送方是否是其声称的那个。而在使用数字签名的场景中,身份验证是对于消息所属者的认证,而不是通信方。
举个例子,一份所属于 Alice 的签名文件可能存放在第三方服务器上,当 Bob 向第三方服务器请求这份文件时,他可以基于 MAC 来验证文件确实响应自第三方服务器(要求 Bob 与第三方服务器共享一个 secret),然后 Bob 可以通过数字签名验证文件是否真实属于 Alice。


在上面的例子中,我们忽略了一个非常重要的问题:Bob 怎么能保证他手上的 Alice 的公钥就是 Alice 的呢?
考虑如下场景,如果在 Bob 和 Alice 之前还有一个第三者 X,在三者之间发生了如下操作:

  1. X 以某种方式将 Bob 手上的 Alice 的公钥替换成了自己的
  2. Bob 向 Alice 请求文件
  3. X 截获了这个请求,并使用自己的私钥对一份伪造文件进行了签名,然后将这个伪造文件连同 X 的签名被发给 Bob
  4. Bob用他手上的 “Alice” 的公钥(实际上已经是 X 的公钥)对签名解密,并将结果与对收到的伪造文件计算的摘要进行比对,一切正常

在这个场景中,Bob 收到了一份 X 伪造的文件,并且认为该文件是 Alice 签名的,而事实上 Alice 对此一无所知。

那么,如何才能保证公钥的真实性呢?网络上的通信双方如何安全的交换彼此的公钥?
我们下文见~


参考资料

  • 计算机网络,第五版
  • Computer Networking: A Top-Down Approach, 7th Edition
  • MAC - wiki