TLS
全称
传输层安全协议,上一代是安全套接层(SSL,不安全),用途广泛,最知名的是用于http,使http升级为https协议,最新版本为TLSv1.3(推荐使用)。
TLS通过建立客户端和服务器之间的安全通道,确保传输的数据是
机密的、经认证的和未被修改的。
TLS工作在
传输层协议和应用层协议之间。
TLS通过使用证书和可信证书颁发机构对服务器(或客户端)进行身份验证,从而抵御中间人攻击。
TLS设计目标
满足高效性、跨平台、可扩展和通用性。
通信两端传输的数据应该是安全的,不可伪造和篡改的。
TLS/SSL协议是标准的,任何开发者基于TLS/SSL的RFC设计规范都可以实现该协议,开发者也很容易在应用中引入TLS/SSL协议。
密码学算法是不断迭代的,随着时间的推移,会出现更安全的算法,为了保障持续的安全,TLS/SSL协议允许动态地引入新的算法。由于通信体之间环境是不一样的,协议允许双方协商出都支持的密码学算法。
解决方案必须是高效的,TLS/SSL协议涉及了很多密码学算法的运算,增加了通信延时和机器负载,但现在,有一些新的技术和解决方案在逐步提升TLS/SSL协议的效率。
TLS协议套件
TLS由多个协议组成,最主要的两个是
记录协议和
握手协议。记录协议定义了一种数据包格式封装来自更高层协议的数据,并将该数据发送给另一方。握手协议是TLS的
密钥协商协议。
记录协议
所有数据以一系列TLS记录发送,称为TLS数据包。TLS记录协议(记录层)本质上是一个传输协议,与传输数据的含义无关;
TLS记录协议首先用于握手期间交换的数据。一旦握手完成,并且双方共享一个会话密钥,应用程序数据就会被分割成块,作为TLS记录的一部分进行传输。
报文结构
TLS记录协议的数据块最多16KB,结构如下:
●
第一字节表示发送的数据类型,
22表示握手数据,23表示加密数据,21表示告警。在TLS 1.3规范中,这个值被称为内容类型(ContentType)。
●
第二字节和第三字节的值被称为协议版本(Protocol Version),例如tlsv1.3这两个字节
分别设置为3和1。
●
第四字节和第五字节表示将要作为传输的数据的长度,该整数不能大于
字节(16KB)。
●
其余的字节是要传输的数据(也称为有效载荷),其长度等于由记录的第四字节和第五字节编码的值。
握手协议
握手协议是TLS的核心协议,是客户端和服务器建立会话密钥以便发起安全通信的过程。
在TLS握手过程中,客户端和服务器扮演不同的角色,双方进行协商。客户端提出了一些配置(TLS版本和密码套件,优先顺序等),服务器选择要使用的配置。服务器一般遵循客户端的偏好,但也可能选择其他的配置。
大体流程如下:
客户端与服务器建立TLS连接,每次TLS会话会生成一个DH密钥对,发送给服务器支持的加密算法和客户端DH公钥,还包括32字节的随机数和可选信息(附加参数等)。
服务器端生成DH(Diffie-Hellman)密钥对,并根据服务器的DH私钥和发送过来的客户端的DH公钥计算协商的预备主密钥secret,用KDF导出主密钥keys。服务器端响应ServerHello消息,内容有:用于加密TLS记录的密码、服务器DH公钥、32字节的随机数、证书、ClientHello和ServerHello中所有消息的签名(使用与证书公钥相对应的私钥计算)、同样信息的MAC以及签名。MAC使用公共密钥计算。
客户端接收到ServerHello消息时,首先先验证证书的有效性,验证签名,计算DH的会话密钥并从中得到对称密钥,验证服务器发送的MAC。一旦完成所有验证,客户端就准备好将加密的消息发送到服务器。
密码套件
密码套件一般指几种密码学算法的组合。主要包括身份验证算法(通过证书)、密钥协商算法、加密算法或者加密模式、HMAC算法的加密基元、KDF的加密基元等等。密码套件的强度决定了TLS的安全程度,web服务器通常可以配置密码套件,如nginx等,所以现在一些https网站已经禁止使用有风险的密码套件。
TLS 1.3仅支持密码套件:TLS_AES_256_GCM_SHA384、TLS_CHACHA20_POLY1305_SHA256、TLS_AES_128_GCM_SHA256。
下面使用openssl查看支持的密码套件
openssl ciphers -V | column -t
#选取第一条结果,各列说明如下:
#第一列:数值代表密码套件的编号,每个密码套件的编号由IANA(互联网数字分配机构)定义。
#第二列:代表密码套件的名称,虽然密码套件编号是一致的,不同的TLS/SSL协议实现其使用的名称可能
是不一样的。
#第三列:表示该密码套件适用于哪个TLS/SSL版本的协议。
#第四列:表示密钥协商算法。
#第五列:表示身份验证算法。
#第六列:表示加密算法、加密模式、密钥长度。
#第七列:表示HMAC算法。其中AEAD表示采用的是AEAD加密模式(比如AES128-GCM),无须HMAC算法
1 2 3 4 5 6 7
#0x13,0x02 - TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
密钥块
一般来说,TLS要保证数据库的机密性和完整性,需要用到对称加密算法(相比非对称加密计算较快)和消息认证码(MAC),所以需要一组密钥,这些密钥由
主密钥使用
密钥导出函数(KDF)生成。
密钥块的
个数和密钥块的
长度都依赖于协商出来的对称加密码算法和HMAC算法,也就是由密码套件决定。
例如客户端和服务器端协商出加密算法采用AES-256-CBC,消息认证码采用HMAC,则它们必须持有相同的AES密钥、HMAC密钥、初始化向量等等,这些就称为密钥块。
主密钥和预备主密钥
主密钥由
预备主密钥生成,预备主密钥也采用
密码导出函数(KDF)转换为主密钥,预备主密钥通信双方协商(采用RSA或者DH(ECDH)算法)得出。
先使用预备主密钥转换出固定长度的主密钥,然后主密钥转换出任意数量、任意长度的密钥块。
密钥导出函数(KDF)
常见的KDF有PBKDF2、PRF等。
KDF的输入参数包含输入值和salt,如果输入值和salt是一样的,则输出也是一样的。对于通信双方来说,每一次连接的salt是变化的,但是为了保证输出结果一致,双方每次持有的salt值必须相同。
TLS1.2使用
PRF进行密码推导(TLS1.3 是用的
HKDF),它需要三个参数(secret,label,seed),其中:secret是输入,label是个固定值,seed就是salt,是一个随机值,计算过程大体如下:
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +...
HMAC_hash(secret, A(i) + seed)
PRF(secret, label, seed) = P_<hash>(secret, label + seed)
/*
其中,A(0) = seed,A(i) = HMAC_hash(secret, A(i-1))
hash指的是单向hash函数,如sha256等等。
label是个固定字符串,ASCII码。
以sha256为例,一次HMAC_sha256产生的数据为32字节。如果想得到80字节数据,则令i=3,这样会得到
96字节数据,丢弃后16字节就可得到80字节。
*/
密钥协商
为了安全传输对称加密和MAC用的密钥块,不能再使用另一个密钥去加密这些密钥块,这会陷入加密的无限循环中,我们需要一种动态密钥,每次会话都不一样,这样也具有前向保密性。
这时候就可以使用公开密钥算法了。
每个客户端和服务器端初始化连接的时候生成
预备主密钥,每次的值都是不一样的。
预备主密钥在会话结束后(连接关闭后),会自动释放,不会持久存储。
预备主密钥必须保证是机密的,确保攻击者无法从任何手段得出预备主密钥。
RSA协商
步骤如下:
1.客户端向服务器端发起连接请求,服务器端发送RSA密钥对的公钥给客户端。
2.客户端通过
随机数生成器生成一个预备主密钥,用服务器的公钥加密并发送给服务器端。
3.服务器解密预备主密钥,如果成功解密,则预备主密钥正式生效。
RSA协商虽然步骤简洁,但是不能保证前向安全性(指由于泄露密钥导致历史数据被解密)。
DH协商
步骤如下:
1.客户端向服务器端发起连接请求。
2.服务器端生成一个RSA密钥对,并将公钥发送给客户端。
3.服务器端生成DH参数和服务器DH密钥对,用RSA私钥签名DH参数和服务器DH公钥,最后将签名值、DH参数、服务器DH公钥发送给客户端。
4.客户端通过服务器RSA的公钥验证签名,获取到DH参数和服务器DH公钥。
5.客户端通过DH参数生成客户端的DH密钥对,并将客户端DH公钥发送给服务器端。
6.客户端通过客户端DH私钥和服务器端DH公钥计算出预备主密钥。
7.服务器端接收到客户端的DH公钥,与自己的DH私钥计算出预备主密钥。
自此协商完成。
静态DH
每次会话时,服务器每次发送的DH参数和DH公钥是一样的(省去计算时间),这称为静态DH算法,它不能保证前向安全性。
动态DH
通信双方每次连接的时候,服务器通过DH参数生成的服务器DH密钥对是不一样的,在会话结束后,服务器DH密钥对也会失效,由于每次会话DH密钥不一样,所以这种方式能提供前向安全性。
TLS1.3的新特性
降级保护、单次往返握手和会话恢复。
降级保护
TLS 1.3的降级保护特性被设计为抵御降级攻击,其中攻击者迫使客户端和服务器使用比1.3更弱版本的TLS。为了执行降级攻击,攻击者通过拦截和修改ClientHello消息以告诉服务器客户端不支持TLS 1.3,迫使服务器选择较弱版本的TLS,从而其可以利用较早版本的TLS中的漏洞。
为了抵御降级攻击,TLS 1.3服务器在ServerHello消息中发送的32字节随机值中使用三种模式来标识请求的连接类型。该模式应与客户端对特定类型TLS连接的请求相匹配。如果客户端接收到错误的模式,它就知道有可能正在受到攻击。
如果客户端请求TLS 1.2连接,则32个字节中的前8个字节被设置为44 4F 57 4E 47 52 44 01,如果客户端请求TLS 1.1连接,则将它们设置为44 4F 574E 47 52 44 00。如果客户端请求TLS 1.3连接,则前8字节应该是随机的。例如,如果客户端发送了要求TLS 1.3连接的ClientHello,但是网络上的攻击者将其修改为请求TLS 1.1连接,则当客户端接收到具有错误模式的ServerHello时,将知道其发送的ClientHello消息已被修改。(攻击者不能任意修改服务器的32字节随机值,因为这个值是加密签名的。)
单次握手
在典型的TLS1.2握手中,客户端向服务器发送一些数据,等待响应,然后在发送加密消息之前发送更多数据并等待服务器的响应,延迟是两个往返时间(RTT)。而TLS1.3的握手需要一个往返时间。
会话恢复
会话恢复是一种利用在前一个会话中客户端和服务器之间交换的预共享密钥来引导新会话的方法。会话恢复带来了两个主要好处:客户端可以立即开始加密,并且不需要在后续的会话中使用证书。
首先,客户端发送一个ClientHello消息,该消息包括与服务器已经共享的密钥的标识符(预共享密钥用PSK表示),以及一个新的DH公钥。客户端还可以在该第一个消息中包括加密数据(这种数据被称为0-RTT数据)。当服务器响应ClientHello消息时,它提供MAC。客户端验证MAC,并且知道它正在与之前同一台服务器进行通信,无需进行证书验证。客户端和服务器就像普通握手协议一样执行Diffie-Hellman密钥协商,随后的消息使用依赖于PSK和新计算的Diffie-Hellman共享密钥的密钥加密。