前篇博客:图解HTTP中谈到,HTTP/1.1 协议默认是以明文方式传输数据的,这就带来三个风险:窃听风险、伪装风险、篡改风险。HTTP 协议自身没有加密机制,但可以通过和 TLS (Transport Layer Security) / SSL (Secure Socket Layer) 的组合使用,加密 HTTP 的通信内容,借助 TLS / SSL 提供的信息加密功能、完整性校验功能、身份验证功能保障网络通信的安全,与 TLS / SSL 组合使用的 HTTP 被称为 HTTPS(HTTPSecure),可以说 HTTPS 相当于身披 SSL / TLS 外壳的 HTTP。
近几年,网络安全问题越来越受重视,国内外各大互联网公司比如Google、Baidu、Facebook等,都在不谋而合地开始大力推行 HTTPS,为鼓励全球网站的 HTTPS 实现,一些互联网公司都提出了自己的要求:
因此,我们要开发网络应用,也应该跟上全网 HTTPS 的潮流,使用 HTTPS 协议为我们的数据安全上一把锁。前一篇博客已经详细介绍了 HTTP 协议,下面重点介绍网络安全通信协议 TLS / SSL。
SSL(Secure Socket Layer) / TLS(Transport Layer Security) 是独立于 HTTP 的协议,所以不光是 HTTP 协议,其他运行在应用层的 SMTP 和 Telnet 等协议均可配合 SSL / TLS 协议使用,可以说 SSL / TLS 是当今世界上应用最为广泛的网络安全技术。
Netscape 开发了原始的 SSL 协议,SSL 1.0 因为存在严重的安全缺陷从未公开发布,SSL 2.0于 1995年发布,但其中仍包含许多安全缺陷,于是在1996年发布了SSL 3.0。随着加密与解密安全攻防战的博弈,SSL 协议的安全性逐渐满足不了需求,SSL 2.0 在 2011 年被RFC 6176弃用,SSL 3.0 于 2015 年被RFC 7568弃用。
TLS 1.0 在 1999 年发布于RFC 2246中,TLS 1.0 和 SSL 3.0 之间的差异并不显著(TLS 1.0 等同于 SSL 3.1),改名主要是为了和Netscape撇清关系(SSL协议由Netscape主导开发),表示一个新时代的来临(TLS协议由 IETF 进行标准化)。
随着网络攻击技术的发展,TLS 加密算法也需要进行升级,TLS 1.1 于 2006 年在RFC 4346中定义,TLS 1.2 于 2008 年在RFC 5246中定义,并于2011年在RFC 6176中完成优化,最新版本的TLS 1.3 于 2018 年在RFC 8446中定义。早在2018年10月,苹果、谷歌、微软和Mozilla就联合宣布,将在2020年3月弃用TLS 1.0和TLS 1.1(当你读到本篇博客时,TLS 1.0和TLS 1.1 已经被弃用),由于 TLS 1.3 才发布没多久,未来几年内 TLS 1.2 将成为网络安全协议的主流(HTTP/2协议便使用TLS 1.2+版本作为安全加密层)。
早在几千年前,人们就有了对信息加密的需求和实践,最初人们只是将完整信息隐藏,后来将字符进行移位变换处理以隐藏原来的意思(可以参考博客:谍战中的古典密码学)。随着上世纪计算机的诞生和数学的进步,将有效信息编码为二进制数据,并对这些二进制数据进行数学运算处理的现代密码学获得了飞速发展(可以参考博客:区块链中的现代密码学)。
现代密码学根据密钥不同,可以分为对称密钥加密法和非对称密钥加密法,二者区别对比如下:
类别 | 对称密钥加密法 | 非对称密钥加密法 |
---|---|---|
特点 | 加密和解密使用相同的的密钥 | 加密和解密使用成对的公钥和私钥, 可以使用公钥解密私钥解密(信息加密), 也可以使用私钥加密公钥解密(数字签名) |
优点 | 加密和解密的效率高(比非对称加密效率高几百倍),适合大数据量加解密 | 不需要交换密钥,安全性更强,还可用于身份认证 |
缺点 | 双方需要交换密钥,密钥的安全无法保证 | 加密和解密的效率低(只有对称加密效率的几百分之一),不适合大数据量加解密 |
常用算法 | DES、3DES、IDEA、AES | RSA、DSA、ECC、Diffie-Hellman |
从上面的对比可以看出,Web的数据量还是比较大的,对Web数据信息进行加密适合选择对称密钥加密法。但对称密钥加密法有个缺陷,通信双方需要交换密钥,密钥的安全没法得到保证,这就需要借助非对称密钥加密法来交换密钥了。TLS 也正是采用类似的策略,通过非对称密钥加密法来交换会话密钥,通过对称密钥加密法来加密信息。
我们先看对Web信息加密使用的对称密钥加密法,按照加密方式不同可以将对称密码分为两大类:序列密码(流密码) 和分组密码(块密码),二者的区别对比如下:
类别 | 序列密码 / 流密码 | 分组密码 / 块密码 |
---|---|---|
特点 | 以最小单位比特作为一次加解密的操作元素,采用设计好的算法对明文数据流进行加密与解密操作 | 将明文分为若干长度固定的组块(长度不足的组块按特定格式填充),以每个组块作为一次加解密的操作元素,采用设计好的算法对明文数据组块进行加密与解密操作 |
优点 | 速度快,便于硬件实现,错误传播较轻 | 统计特性优良,修改敏感 |
缺点 | 统计混乱不足,修改不敏感 | 速度慢,错误传播严重 |
常用算法 | WEP、RC4、ChaCha20 | DES、3DES、IDEA、AES |
流密码算法是以“一次性密码本“为雏形演变出来的加密算法,一次性密码本算法很重要的一个特性就是密钥使用的”一次性“,流密码算法安全性的关键也在于绝不第二次使用相同的密钥,这就需要能产生大量的随机数。流密码使用伪随机数发生器产生一次性密钥流,可以说流密码的安全性取决于伪随机数发生器的性能,高性能的伪随机数发生器(一个硬件模块)也可以构建出强度较高的流密码算法。我们一般使用的计算机并没有专门提供高性能的伪随机数发生器硬件模块,所以使用流密码算法的比较少(流密码算法便于硬件实现,一般在专门设计有伪随机数发生器的平台上使用),更常用的是分组密码算法。
分组密码的每个组块加解密类似于流密码,每个组块的长度可以作为衡量加密强度的一个指标,现在一般采用128位、192位、256位的组块长度(需要平衡加密强度与加密效率,随着处理器计算能力提升也会同步提升加密强度)。DES(Data Encryption Standard)是1977年美国联邦信息处理标准(FIPS)种所采用的一种对称密码(3DES是其三重加密增强版),目前已能在短时间内被破解。现在推荐使用的对称加密算法是AES(Advanced Encryption Standard),该算法是在2001年通过全世界范围内公开竞争方式选拔出来的,Rijndael 算法最终力压群雄被美国国家标准技术研究所(NIST)选定为AES标准。
分组密码的关键特性是每个组块都使用不同的组块密码,这些组块密码可以有相关性,也即后一组的密码可以通过前一组的密码迭代而来(分组密码靠迭代关系降低了对伪随机数发生器的性能需求)。根据分组密码迭代方式的不同,产生了多种不同的分组密码模式,常见的有五种模式ECB、CBC、CFB、OFB、CTR。这五种分组密码模式就不一一介绍了,这里主要介绍两种常用的模式:CBC(Cipher Block Chaining mode)和CTR(Counter mode),二者的主要区别对比如下:
类别 | CBC(Cipher Block Chaining mode) | CTR(Counter mode) |
---|---|---|
特点 | 首先将明文分组与前一个密文分组进行XOR异或运算,然后再进行加密;加密第一个分组时,使用一个随机产生的初始化向量 IV(Initialization Vector)来代替前一个密文分组 | 每个分组对应一个逐次累加的计数器,并通过对计数器加密生成分组密码;最终的密文分组是通过将计数器加密得到的密钥流与明文分组进行XOR异或运算得到的 |
优点 | 明文的重复排列不会反映在密文中,支持并行解密 | 支持并行加解密运算,不需要填充 |
缺点 | 加密不支持并行运算,密文分组包含某些错误比特时会影响后续所有的分组解密 | 主动攻击者反转密文分组中的某些比特时会引起明文分组对应比特位的反转 |
在早期的TLS / SSL版本中主要使用 AES CBC 进行信息加密,后来 AES CTR 支持并行加解密的优势逐渐凸显,被TLS 1.2 及之后的版本选用,TLS / SSL 各版本支持的信息加密算法如下:
TLS 1.3版本增强安全性的同时对协议进行了简化,删除了对不安全加密算法的支持,对于分组密码只支持AES GCM(Galois-MAC + CTR Mode)和AES CCM(CBC-MAC + CTR Mode ),这两种加密算法实际上都是AES CTR,只是在此基础上增加了MAC(Message Authentication Code)用来验证数据完整性,这部分在下文介绍。
我们在博客:哈希算法能用来干啥?中介绍了Hash散列算法能用来进行数据完整性校验,Hash散列函数最常见的使用场合是以紧凑的方式表示并比较大量数据,主要是基于Hash函数的以下特点:
因此对信息数据经过Hash函数计算后的散列值可称为消息摘要(Message Digest)或消息指纹(Message Fingerprint),所使用的Hash函数也称为消息摘要函数,常见的安全Hash算法(Secure Hash Algorithms)如下:
从上图可以看出MD5、SHA-1 已经不再安全,现在用的比较多的是SHA-256、SHA-512等,SHA-3算法(和AES一样,也是通过全球范围内公开竞争方式实现标准化的)因为发布时间较晚,且目前 SHA-2 尚未被攻破,因此SHA-2仍是主流(比特币中使用的便是SHA-256),TLS 1.2及更新的版本使用 SHA-256/384 取代了之前的MD5 / SHA-1。
使用Hash散列值可以实现数据完整性检查,能够辨别出“篡改”,但无法辨别出“伪装”。假如将消息密文与对应的散列值一起发送,攻击者可以同时修改密文和对应的散列值,接收端仍然能通过消息完整性校验。怎么才能保证Hash散列值不被篡改呢?一个简单的思路就是对散列值进行加密后发送,使用密钥加密后的散列值称为消息认证码(Message Authentication Code)。密文与对应的MAC一起发送,由于攻击者不知道密钥,即便修改密文也没法生成合法的MAC,也就能保证散列值不被篡改(前提是密钥没泄露),接收方可以同时辨识出数据的“篡改”和“伪装”。
消息认证码MAC有两个输入数据:消息和密钥,输入的消息可以是明文也可以是密文,输入的密钥可以是分组密码、流密码、公钥密码等,MAC算法中至少包含一种散列算法以便获取消息的散列值。根据选用的输入密钥Key类型和MAC算法,消息认证码MAC可以有不同的实现方式,比如:
我们先看下TLS / SSL 各版本使用了哪些数据完整性校验算法:
在TLS 1.2版本之前采用HMAC-MD5、HMAC-SHA1作为数据完整性校验算法,前面已经介绍过MD5和SHA1 已经不再安全,因此TLS 1.3 删除了对HMAC算法的支持。TLS 1.3仅支持AEAD来校验数据完整性,这个AEAD是什么呢?
**AEAD(Authenticated Encryption with Associated Data)**全称关联数据认证加密,认证加密是一种将对称密码加密和消息认证码相结合,同时满足机密性、完整性和认证功能的机制。AEAD包含对称加密和MAC计算两部分,根据二者的先后顺序可以将AEAD分为Encrypt-then-MAC、Encrypt-and-MAC、MAC-then-Encrypt 三种方法:
既然AEAD是对称加密算法与MAC计算的组合,TLS 1.3 支持的AEAD算法有三种:AES-CCM、AES-GCM、ChaCha20-Poly1305。最后一种流密码认证加密算法一般需要硬件上提供一个伪随机数发生器,前两个分组密码认证加密算法使用场景比较多,举例如下:
AEAD算法 | AEAD方法 | 优缺点对比 | 使用场景举例 |
---|---|---|---|
AES-CCM (AES-CTR + CBC-MAC) | Encrypt-and-MAC | 受限于CBC模式特性,不能充分发挥并行处理的优势 | TLS 1.2/1.3、IPsec、WLAN WPA2、BT LE等 |
AES-GCM (AES-CTR + Galois-MAC) | Encrypt-then-MAC | 可以充分利用并行处理提高效率 | TLS 1.2/1.3、IPsec、WLAN WPA3、SSH等 |
AES-GCM认证加密算法可以充分利用并行处理提高效率,也是TLS 1.2/1.3 优先推荐的认证加密算法,下面简单介绍下GCM认证加密过程(图片取自博客:什么是 AES-GCM加密算法):
为了更直观了解AES-GCM加密算法的效率,下面给出几种对称加密与完整性校验算法的性能对比图示(只找到以AES-128-CBC-SHA1为参考的对比图,但SHA1已不再安全):
从上面的对比图可以看出,AES-128-GCM-SHA256和AES-256-GCM-SHA384的运算效率明显高于其它对称加密与完整性校验算法,AES-GCM认证加密算法的并行处理优势还是挺明显的,这也是TLS 1.2/1.3 优先推荐使用AES-GCM认证加密算法的原因。
TLS (Transport Layer Security)协议是由TLS 记录协议(TLS Record Protocol)和TLS 握手协议(TLS Handshake Protocol)这两层协议叠加而成的,位于底层的TLS 记录协议负责进行信息传输和认证加密,位于上层的TLS 握手协议则负责除加密以外的其它各种操作,比如密钥协商交换等。上层的TLS 握手协议又可以分为4个子协议,TLS 协议的层次结构如下图所示:
前面已经介绍了TLS 协议的认证加密算法,这里先介绍TLS 记录协议的报文结构,每一条TLS 记录以一个短标头起始,标头包含记录内容的类型(或子协议)、协议版本和长度,消息数据紧跟在标头之后,如下图所示:
TLS记录报文的字段可以用如下数据结构定义:
enum {
change_cipher_spec (20),
alert (21),
handshake (22),
application_data (23)
} ContentType;
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
struct {
ContentType type;
ProtocolVersion version;
uint16 length; /* 最大长度为2^14(16 384)字节 */
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
了解了TLS 记录报文结构,前面也介绍了认证加密过程,下面以AES-GCM为例,看看密文(消息明文AES-CNT加密而来,当不需要对消息加密时,此处保持明文即可)和MAC(将序列号、标头等附加信息与密文一起通过Galois-MAC计算而来)是如何添加进TLS 记录报文的:
上图中的几个字段作用如下:
握手是TLS协议中最精密复杂的部分,我们后面再介绍。回到开头介绍信息加密时对称密钥加密与非对称密钥加密的优缺点对比,对称密钥加密虽然比较高效,TLS 1.3 优先推荐的AES-GCM认证加密算法也能同时保证通信消息的机密性、完整性、真实性,但共享密钥的交换是个问题,非对称密钥加密法正好能弥补这个不足,作为通信双方传递共享密钥的加密算法,下面介绍TLS 的共享密钥是如何交换的。
早在DES对称加密法诞生前,共享密钥的交换就一直是个难题,在没有良好解决方案之前靠专人面对面传递共享密钥(还记得碟战中的密码本么)。直到1976年,共享密钥交换的难题被sun公司的高级工程师迪菲和斯坦福大学电子系教授黑尔曼两人解决,他们提出的共享密钥交换方案被命名为Diffie–Hellman key exchange,这也是非对称公私密钥加密系统诞生的基础。
Diffie–Hellman是如何实现共享密钥交换的呢?答案自然来自于数学,他们两人花了两年多时间找到了一种难以逆转的单向函数:取模运算(modular arithmetic),单向函数或不可逆函数的特点就是正向计算结果唯一、逆向运算结果不确定(比如 11 * 9 = 8 (mod 13)正向计算结果唯一,但问你“在模等于13的体系中,什么数乘以9后余数为8?”,这个问题显然不能逆运算,所以“模运算”就是一个单向函数,前面介绍的单向散列函数内部也用到了模运算)。非对称密钥加密原理和Diffie–Hellman密钥交换原理在博文:区块链中的现代密码学中已经有过简单介绍,再将该博文中关于Diffie–Hellman密钥交换原理的介绍展示如下:
明白了模运算,我们就能知道它是怎么用来传递钥匙的了,过程如下:
- 爱丽丝和鲍勃两个人都约定使用Gx mod P函数(G、P要求是质数,比如G = 7、P = 101);
- 爱丽丝随便想出一个数A(比如A = 3),鲍勃也随便想出一个数B(比如B = 6);
- 他们两个人分别把自己想出来的这个数,代入函数Gx mod P计算一下,比如爱丽丝算完等于α,鲍勃算完等于β;
- 两人把计算结果告诉对方,直接打电话也行,写信也行,反正是不怕被伊芙偷听的。原因刚刚说过:因为伊芙偷听到的只不过是模运算的结果,而这个结果是不可逆的,所以就算伊芙知道结果,也没法倒推出之前的值;
- 等爱丽丝和鲍勃都得到对方的结果之后,爱丽丝再做一次βA mod P运算(也即GA·B mod P),鲍勃做一次αB mod P运算(也即GA·B mod P);
- 这两个人算完后,他们将得到完全一样的结果(按上例取值的结果是45),这个相同的结果就可以当作他们的钥匙。整个过程没人传递过钥匙,但双方都拿到了同样的钥匙。对窃听者伊芙来说,她偷听到的只是模运算的结果,因为这种运算是不可逆的,所以伊芙偷听了也白听。
从上面Diffie–Hellman密钥交换过程可知,爱丽丝与鲍勃各自生成两个参数A + α、B + β,相互发送其中一个参数(α 和 β)到对端,再经过计算,就可以得到共享密钥了。最开始两人还需要约定计算函数(Gx mod P),因此还需要两个参数 G + P(可以称为域参数,对于Web通信常由服务器选取域参数并发送给客户端)来约定初始条件,而DH密钥交换的安全性在很大程度上也取决于这两个域参数的质量。
DH密钥交换过程中,由服务器向客户端提供三个参数(两个域参数和一个计算结果),客户端向服务器提供一个计算结果,有些场景下两个域参数以静态方式被嵌入到服务器和客户端的证书中,这就可能导致DH密钥交换的结果是一直不变的共享密钥,就无法具备前向保密(指即便在密钥泄露的情况下,也不能拿泄露密钥解密之前截获的密文)的能力。要想具备前向保密能力,就需要每次交换的共享密钥都不一样,也就要求DH密钥交换中的两个域参数每次都是临时生成且不重复的,具备这种特性的DH称为DHE (Ephemeral Diffie-Hellman),DHE也是TLS 1.2/1.3 支持的密钥交换方案。
通过DHE密钥协商方案,就可以实现对称加密中共享密钥的交换,有了共享密钥,通信双方就可以使用认证加密技术进行安全通信了吗?假如主动攻击者在密钥交换前冒充对端,与通信双方分别完成密钥交换,并进行正常的认证加密通信,通信双方可能是难以察觉的,这就带来了信息泄露和被篡改的风险,这种攻击也称为中间人攻击(Man-In-The-Middle attack),MITM攻击过程图示如下:
我们该如何应对中间人攻击呢?最主要的就是要确认通信对端的身份,前面介绍过的消息认证码MAC具有身份认证的功能(只能确认密钥持有者的身份),主动攻击者不知道密钥所以没法生成合法的MAC,我们可以借鉴这种思路吗?显然是不能的,正因为没有共享密钥才需要DHE密钥协商,有了密钥自然就不需要DHE密钥协商了。
还记得博文:区块链中的现代密码学中提到的数字签名吗?非对称密钥加密法正向使用(公钥加密 + 私钥解密)可以用来加密信息,逆向使用(私钥加密 + 公钥解密)可以作为数字签名。该特性主要得益于公私密钥对的一个性质:公钥加密的信息私钥能够解密,私钥加密的信息公钥也能够解密,公钥对外公开任何人都可以持有,私钥则只有本人持有。
这就意味着任何想向私钥持有者(比如Alice)发送敏感信息的一方(比如Bob),只要获取到Alice的公钥,就可以使用该公钥加密敏感信息后将密文发送给Alice,该密文只有私钥持有者Alice能解密。Alice如果想对外发布一个声明,为了防止别人以自己的名义伪造该声明,可以使用自己的私钥将该声明加密后,将密文发布给目标人群,任何人都可以使用Alice的公钥来解密该声明,确认该声明确实由Alice本人发布,别人无法伪造,Alice也无法否认。非对称密钥加密法正向和逆向使用的示意图示如下:
最早发明的非对称密钥加密法是RSA加密法,RSA的诞生也得益于Diffie、Hellman两人发表的非对称公私密钥加密系统的概念。在DH密钥交换方案发布的第二年,Ron Rivest、Adi Shamir、Leonard Adleman三人就实现了首个非对称公私密钥加密算法:RSA加密法,该加密算法的实现同样离不开不可逆的单向函数 — 模运算。
RSA加密法的实现原理已经在博文:区块链中的现代密码学中简单介绍过了,再将该博文中RSA加密法实现原理展示如下:
- 爱丽丝展示出来的公开钥匙,是通过两个比较大的素数p和q相乘得到的一个更大的数N得到的。p和q具体是多少,爱丽丝只要自己知道就行,千万不要告诉别人,而乘积N是公开的,谁都可以知道;
- 凡是要给爱丽丝发消息的人,都需要用N来加密,加密的过程依然用的是模运算,而且模就是N。整个数学过程会保证这个模运算不可逆,所以伊芙就算知道N也没用;
- 爱丽丝解密时就不需要N了,而是要用到p和q的具体值,而这两个值别人都不知道,只有爱丽丝自己知道。具体来说,爱丽丝私下做的另一个模运算中的模,并不是刚刚我们说的N,而是另外一个值(p-1) x (q-1),这个公式中就必须要知道p和q到底是多少才行。至于为什么是(p-1) x (q-1),数学原理会保证这样操作能算出一把新钥匙,这把新钥匙就是爱丽丝自己的私钥,用这把私钥一定可以解出原文。
从上面的过程可以看到,RSA加密法公私钥的安全性取决于P、Q的选取和保密,公钥E 和 公共模数N 是对外公开的,外界在知道N 和E 的情况下,是没法进行质因数分解反求出P 和Q 的,特别在N 比较大的情况下。也就是说,RSA加密法的安全强度是跟N 的长度直接相关的,1024位长度的N (也即RSA-1024)已经被证明不安全了,目前TLS 1.2/1.3 推荐使用N 的长度大于2048位。下面给出一份粗略对比不同类型加密算法的安全强度位数与安全级别的列表供参考(表中数据是2012年统计的,随着计算机性能提升,攻击手段更强了,安全强度自然也要随时间提升):
前面介绍了TLS 1.2/1.3 目前推荐的对称密钥加密法安全强度为AES-128,与此安全级别差不多的RSA加密法与DHE密钥交换算法的安全强度为RSA-3248和DHE-3248,对应安全级别的单向散列函数的安全强度为SHA-256/384。上表还列举出一个椭圆曲线加密法,该算法属于哪一类呢?
椭圆曲线加密法ECC (Elliptic-Curve Cryptography)是最近备受关注的一种非对称公私密钥加密算法(可以参考博文:椭圆曲线密码算法),从上面的安全强度位数对比中可以看出:
因此,ECC还是有取代RSA的潜力的,不过ECC更多的是作为一种数学运算工具来升级现有的非对称密钥加密算法,比如 ECDHE(= DHE + ECC) 就是使用ECC在有限域上的椭圆曲线运算取代DHE在有限域(Galois Field)上的指数运算,大幅缩短密钥长度并降低计算量。
反应快的朋友可能会提出一个疑问:RSA算法既然可以同时支持信息加密与数字签名,客户端直接使用服务器端提供的RSA公钥将共享密钥加密后发给服务器,服务器只有使用自己的RSA私钥才能解密,这不就可以解决对称加密中共享密钥交换的问题,而且不需要DHE密钥协商了吗?
单靠RSA加密法确实也可以实现共享密钥的交换,但RSA加密法有个缺点,由于服务器公钥更新并不方便,所以服务器公钥与私钥一般会保持多年不变,一旦RSA私钥泄露或者被破解(随着计算机性能提升,安全强度低的旧密码可以被破解),之前截获的消息就可以解密了,攻击者就可以利用这点长期记录所有加密流量,等待有朝一日获得RSA私钥后解密过去记录的所有加密信息,也就是说RSA并不支持前向保密性,也因此TLS 1.3 不再单独使用RSA进行共享密钥交换。
TLS 1.3 支持的具有前向保密性的密钥交换方案都是“DHE + 数字签名”的组合,DHE负责密钥协商,数字签名负责验证通信对端的身份。我们很自然就能想到"DHE + RSA"的密钥协商和身份验证方案,这也是TLS 1.3 支持的方案之一。这套方案有改进空间吗?
前面介绍的EC (Elliptic-Curve)椭圆曲线数学工具就派上用场了,使用椭圆曲线(Elliptic-Curve)运算取代DHE中有限域(Galois Field)上的指数运算,就可以将DHE密钥协商算法的安全性提升、计算量减小,升级为ECDHE(Elliptic-Curve Diffie–Hellman Ephemeral)密钥协商方案。数字签名算法可以借助ECC实现升级吗?
RSA数字签名算法并不适合用椭圆曲线运算升级,NIST(美国国家标准技术研究所)于1991年将DSA(Digital Signature Algorithm)用于数字签名标准(DSS: Digital Signature Standard),DSA算法跟DHE采用了类似的数学工具:有限域中的离散对数概念(想了解DSA原理,可参考博文:DSA-数据签名算法),跟DHE和RSA有相近的安全强度,由于DSA是专门为数字签名设计的算法(标准DSA算法不能用于数据加密),因此在数字签名方面比RSA效率跟高一些。
由于DSA跟DHE采用了类似的数学工具,都是基于有限域中的指数运算(利用有限域中的离散对数运算非常困难的特性,来保证破解的难度),因此也可以使用椭圆曲线运算取代DSA中有限域上的指数运算,将DSA数字签名算法的安全性提升、计算量减小,升级为ECDSA (Elliptic Curve Digital Signature Algorithm)。
类别 | DHE | RSA | DSA | ECC |
---|---|---|---|---|
特点 | 利用在有限域中求解离散对数的困难度保证安全性 | 利用对大数进行质因数分解的困难度保证安全性 | 利用在有限域中求解离散对数的困难度保证安全性 | 利用在椭圆曲线上求解离散对数的困难度保证安全性 |
优点 | 可以安全的进行密钥协商,支持前向保密性 | 既可以用于信息加密,又可以用于数字签名 | 进行数字签名的效率比RSA更高 | 有更强的安全性,更短的密钥长度,更高的计算效率 |
缺点 | 无法验证通信对端的身份,需要配合数字签名算法使用 | 不支持前向保密性 | 不能用于信息加密,支持率不如RSA | 更多的是作为一种数学工具,跟其它的公私钥加密方案配合使用 |
算法升级 | 借助ECC提供的数学工具升级为ECDHE | - | 借助ECC提供的数学工具升级为ECDSA | - |
TLS 1.2/1.3支持的密钥协商与身份验证算法如下:
从上图可以看出,TLS 1.3 优先推荐的密钥协商与身份验证算法是ECDHE-ECDSA,考虑到ECC普及度暂时还不如RSA,同时提供了对ECDHE-RSA、DHE-RSA的支持。
前面已经介绍了TLS 使用到的认证加密算法、完整性校验算法、密钥协商算法、数字签名算法等,这些算法都是要配合使用的(单独使用无法保证信息安全传输),各类算法相互组合可以形成一套套加密方案。TLS 握手阶段,服务器与客户端需要协商都使用哪些加密算法,如果逐个协商显然效率较低,TLS将可用的加密算法组合为一个个密码套件,服务器与客户端只要协商一种密码套件,就确定了整个加密方案中用到的所有加密算法,能提高通信效率。
TLS密码套件主要由密钥交换方法、身份验证方法、密码定义(包括对称加密算法、安全强度、认证与分组模式)以及可选的MAC或PRF算法组合而成(如果没有采用AEAD认证加密方案,则需提供MAC消息认证码,若采用AEAD则不再需要单独指定MAC),图示如下:
下面给出几个密码套件示例:
上表中的PRF(Pseudo Random Function)指的是伪随机函数,不管是对称密钥非对称密钥的生成都离不开随机数,随机数质量的好坏决定了加密法的安全强度,真正的随机数很难获取,更常用的是通过算法实现的伪随机数。
从TLS 1.2起,所有的算法套件都需要明确指定它们的PRF,一般伪随机函数PRF由一个密钥secret、一颗种子seed、一个唯一标签label 构成(引入种子和标签便于每次生成不同的伪随机数,而密钥secret可以重用)。PRF的生成主要使用了Hash算法,所以密码套件中的PRF字段是以散列函数强度来描述的(比如SHA-256),PRF的计算过程简单展示如下:
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +
HMAC_hash(secret, A(3) + seed) + ...
// A(i) 函数定义如下:
A(1) = HMAC_hash(secret, seed)
A(2) = HMAC_hash(secret, A(1))
...
A(i) = HMAC_hash(secret, A(i-1))
// PRF则是结合标签和种子对 P_hash 的封装:
PRF(secret, label, seed) = P_hash(secret, label + seed)
技术是在解决问题的过程中进步的,网络安全也是在网络攻击中逐渐提高的,前面介绍的每种加密方案都是为了解决当前加密方案遇到的问题而诞生的,比如MAC是为解决消息可能被篡改或伪造的问题产生的、DHE是为了解决共享密钥交换问题发明的、DSA是为了解决通信对端可能伪装身份的问题产生的。
我们已经介绍了TLS 加密方案中用到的所有加密算法,这套加密方案能挡住几乎所有的网络攻击吗?我们来回顾下整个信息安全传输流程:
看起来整个过程挺安全,密钥协商过程解决了共享密钥交换的问题,数字签名可以确认通信对端的身份,认证加密算法可以保证通信内容的机密性、完整性、真实性,看似无懈可击时回到起点,审视下初始条件或假设。这里的初始条件是我们已经获得了通信对端的公钥,但这个公钥确实是通信对端的吗?攻击者可以让你获得的是他伪造的公钥吗?
还拿中间人攻击举例,当客户端与服务器互相发送公钥C 和公钥S 时,攻击者截获这两个公钥,自己再生成两对儿公私钥A和B,分别冒充客户端和服务器的通信对端分别与他们交换公钥、协商共享密钥、认证加密通信,这样攻击者就可以获知共享密钥解密后续所有的消息。这又回到了前面介绍DHE密钥协商时遇到的攻击者伪装成通信对端的问题,数字签名确认通信对端身份的作用没有发挥出来,问题出在哪呢?
再回顾整个过程不难发现,问题就出在公钥的交换上,我们无法确认我们获得的公钥确实是通信对端的公钥,这也是数字签名无法解决的问题。也就说是,要正确使用数字签名,前提就是用于验证签名的公钥必须属于真正的通信对端。该如何确认自己得到的公钥是合法的呢?
公钥不怕泄露,唯一担心的就是被篡改伪造。生活中我们想判断一则消息是否真实可信,常常会看是否有权威机构或个人为其背书,我们想确认自己得到的公钥是否真实合法,也可以引入权威第三方对其签名,经过可信第三方签名的公钥可以称为证书。客户端与服务器在交换公钥阶段,改为交换由可信第三方签名的证书,就能保证自己获得的公钥是真实可信的吗?
假如攻击者也获得了一个由可信第三方签名的证书,岂不是像前面直接交换公钥遇到的问题一样。很显然,证书里面不止包含通信对端的公钥,还应有身份标识信息。证书系统是如何工作的?TLS 握手协议都做了哪些工作?我们在下一篇博客:TLS 1.2/1.3 握手协议与性能优化中介绍。