加密算法

AES

AES 只支持 3 种不同的密钥长度,分别是 128 位、192 位和 256 位,它们的安全性依次升高,运算时间也更长。比如,当密钥为 128 比特位时,需要经过十轮操作,其中每轮要用移位法、替换法、异或操作等对明文做 4 次变换。而当密钥是 192 位时,则要经过 12 轮操作,密钥为 256 比特位时,则要经过 14 轮操作

密钥越长,虽然性能略有下降,但安全性提升很多。比如早先的 DES 算法只有 56 位密钥,在 1999 年便被破解。在 TLS1.2 及更早的版本中,仍然允许通讯双方使用 DES 算法,这是非常不安全的行为,你应该在服务器上限制 DES 算法套件的使用

分组模式

只有数百比特的密钥,到底该如何对任意长度的明文加密呢?主流对称算法会将原始明文分成等长的多组明文,再分别用密钥生成密文,最后把它们拼接在一起形成最终密文。而 AES 算法是按照 128 比特(16 字节)对明文进行分组的(最后一组不足 128 位时会填充 0 或者随机数)。为了防止分组后密文出现明显的规律,造成攻击者容易根据概率破解出原文,我们就需要对每组的密钥做一些变换,这种分组后变换密钥的算法就叫做分组密码工作模式(下文简称为分组模式),它是影响 AES 性能的另一个因素。

优秀的分组密码工作模式更难以从密文中发现规律

GCM分组模式

CBC 分组模式中,只有第 1 组明文加密完成后,才能对第 2 组加密,因为第 2 组加密时会用到第 1 组生成的密文。因此,CBC 必然无法并行计算。在材料科学出现瓶颈、单核频率不再提升的当下,CPU 都在向多核方向发展,而 CBC 分组模式无法使用多核的并行计算能力,性能受到很大影响。所以,通常我们应选择可以并行计算的 GCM 分组模式,这也是当下互联网中最常见的 AES 分组算法。

CPU AESNI指令集

由于 AES 算法中的替换法、行移位等流程对 CPU 指令并不友好,所以 Intel 在 2008 年推出了支持AES-NI 指令集的 CPU,能够将 AES 算法的执行速度从每字节消耗 28 个时钟周期,降低至 3.5 个时钟周期。在 Linux 上你可以用下面这行命令查看 CPU 是否支持 AES-NI 指令集:


# sort -u /proc/crypto | grep module |grep aes
module       : aesni_intel

因此,如果 CPU 支持 AES-NI 特性,那么应选择 AES 算法,否则可以选择CHACHA20 对称加密算法,它主要使用 ARX 操作(add-rotate-xor),CPU 执行起来更快。

密钥协商

RSA 密钥协商算法

当部署 TLS 证书到服务器上时,证书文件中包含一对公私钥(参见非对称加密),其中,公钥会在握手阶段传递给客户端。在 RSA 密钥协商算法中,客户端会生成随机密钥(事实上是生成密钥的种子参数),并使用服务器的公钥加密后再传给服务器。根据非对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务器解密后,双方就得到了相同的密钥,再用它加密应用消息。

RSA 密钥协商算法的最大问题是不支持前向保密(Forward Secrecy),一旦服务器的私钥泄露,过去被攻击者截获的所有 TLS 通讯密文都会被破解。解决前向保密的是DH(Diffie–Hellman)密钥协商算法。

DH算法

通讯双方各自独立生成随机的数字作为私钥,而后依据公开的算法计算出各自的公钥,并通过未加密的 TLS 握手发给对方。接着,根据对方的公钥和自己的私钥,双方各自独立运算后能够获得相同的数字,这就可以作为后续对称加密时使用的密钥。

即使攻击者截获到明文传递的公钥,查询到公开的 DH 计算公式后,在不知道私钥的情况下,也是无法计算出密钥的。这样,DH 算法就可以在握手阶段生成随机的新密钥,实现前向保密。

ECDH密钥交换算法

DH 算法的计算速度很慢,计算公钥以及最终的密钥时,需要做大量的乘法运算,而且为了保障安全性,这些数字的位数都很长。为了提升 DH 密钥交换算法的性能,诞生了当下广为使用的ECDH 密钥交换算法,ECDH 在 DH 算法的基础上利用ECC 椭圆曲线特性,可以用更少的计算量计算出公钥以及最终的密钥。

X25519 曲线

依据解析几何,椭圆曲线实际对应一个函数,而不同的曲线便有不同的函数表达式,目前不被任何已知专利覆盖的最快椭圆曲线是X25519 曲线,它的表达式是 y2 = x3 + 486662x2 + x。因此,当通讯双方协商使用 X25519 曲线用于 ECDH 算法时,只需要传递 X25519 这个字符串即可。在 Nginx 上,你可以使用 ssl_ecdh_curve 指令配置想使用的曲线:

ssl_ecdh_curve X25519:secp384r1;

选择密钥协商算法是通过 ssl_ciphers 指令完成的:

ssl_ciphers 'EECDH+ECDSA+AES128+SHA:RSA+AES128+SHA';

可见,ssl_ciphers 可以同时配置对称加密算法及密钥强度等信息。注意,当 ssl_prefer_server_ciphers 设置为 on 时,ssl_ciphers 指定的多个算法是有优先顺序的,我们应当把性能最快且最安全的算法放在最前面。

TLS1.3

在 TLS1.2 的握手中,先要通过 Client Hello 和 Server Hello 消息协商出后续使用的加密算法,再互相交换公钥并计算出最终密钥。TLS1.3 中把 Hello 消息和公钥交换合并为一步,这就减少了一半的握手时间

那 TLS1.3 握手为什么只需要 1 个 RTT 就可以完成呢?因为 TLS1.3 支持的密钥协商算法大幅度减少了,这样,客户端尽可以把常用 DH 算法的公钥计算出来,并与协商加密算法的 HELLO 消息一起发送给服务器,服务器也作同样处理,这样仅用 1 个 RTT 就可以协商出密钥。

安全套件命名规则

TLS_DHE_RSA_WITH_AES_256_CBC_SHA是一个密码学套件的标准名字

  • TLS代表的是TLS协议
  • WITH是一个分隔单词
    • WITH前面的表示的是握手过程所使用的非对称加密方法
    • WITH后面的表示的是加密信道的对称加密方法和用于数据完整性检查的哈希方法
  • WITH前面通常有两个单词,第一个单词(DHE)是约定密钥交换的协议,第二个单词(RSA)是约定证书的验证算法。
    • 两个节点之间交换信息和证书本身是两个不同的独立的功能。两个功能都需要使用非对称加密算法。交换信息使用的非对称加密算法是第一个单词,证书使用的非对称加密算法是第二个
    • 有的证书套件,例如TLS_RSA_WITH_AES_256_CBC_SHA,WITH单词前面只有一个RSA单词,这时就表示交换算法和证书算法都是使用的RSA,所以只指定一次即可
    • 可选的主要的密钥交换算法包括: RSA, DH, ECDH, ECDHE
    • 可选的主要的证书算法包括:RSA, DSA, ECDSA。两者可以独立选择,并不冲突
  • AES_256_CBC指的是AES这种对称加密算法的256位算法的CBC分组模式,AES本身是一类对称加密算法的统称,实际的使用时要指定位数和计算模式,CBC就是一种基于块的计算模式
  • SHA就是代码计算一个消息完整性的哈希算法

TLS1.3支持的安全套件

而且,TLS1.3 仅支持目前最安全的几个算法,比如 openssl 中仅支持下面 5 种安全套件:

  • TLS_CHACHA20_POLY1305_SHA256
  • TLS_AES_128_GCM_SHA256
  • TLS_AES_128_CCM_8_SHA256
  • TLS_AES_128_CCM_SHA256

TLS1.3因为默认都是椭圆曲线密钥协商,故隐藏了非对称加密密钥协商算法。如安全套件TLS_AES_128_CCM_SHA256,表示AES这种对称加密算法的128位算法的CCM分组模式,通过SHA256验证消息完整性