TLS 的主要目标是为通信的双方提供一个安全的通道。对下层传输的唯一要求是一个可靠的有序的数据流。
以上 3 点是必须要保证的,即使网络攻击者已经完全掌握了网络,发生了 RFC 3552 中发生的情况。关于 TLS 安全问题,下面有单独的文章专门再讨论。
TLS 协议主要由 2 大组件组成:
TLS 是一个独立的协议;高层协议可以透明地位于 TLS 之上。然而,TLS 标准并未指定协议如何增强 TLS 的安全,如何发起 TLS 握手以及如何理解认证证书交换,这些都留给运行在 TLS 之上的协议的设计者和实现者来判断。
本文档定义了 TLS 1.3 版。虽然 TLS 1.3 不是直接的与之前的版本兼容,所有版本的TLS都包含一个版本控制机制,即允许客户端和服务器通过协商,选出通信过程中采用的 TLS 版本。
TLS 1.3 的标准中取代和废除了以前版本的 TLS,包括 1.2 版本RFC5246 The Transport Layer Security (TLS) Protocol Version 1.2。也废除了在 RFC5077 Transport Layer Security (TLS) Session Resumption without Server-Side State 里面定义的 TLS ticket 机制,并用 Pre-Shared Key (PSK) 机制取代它。由于 TLS 1.3 改变了密钥的导出方式,它更新了RFC5705 Keying Material Exporters for Transport Layer Security (TLS)。它也改变了在线证书状态协议(OCSP)消息的传输方式,因此更新了RFC6066 https://tools.ietf.org/html/rfc6066,废除了RFC6961 he Transport Layer Security (TLS) Multiple Certificate Status Request Extension,如 OCSP Status and SCT Extensions 这一章节所述。
下面描述了 TLS 1.2 和 TLS 1.3 的主要的差异。除去这些主要的差别以外,还有很多细微的不同。
TLS 1.3 规范中还定义了一些可选的针对 TLS 1.2 的实现,包括那些不支持 TLS 1.3 的实现。
安全通道所使用的密码参数由 TLS 握手协议生成。这个 TLS 的子协议,握手协议在 Client 和 Server 第一次通信时使用。握手协议允许两端协商一个协议版本,选择密码算法,选择性互相认证,并建立共享的密钥数据。一旦握手完成,双方就会使用建立好的密钥保护应用层数据。
一个失败的握手或其它的协议错误会触发连接的中止,在这之前可以有选择地发送一个警报消息,遵循 Alert Protocol 协议。
TLS 1.3 支持 3 种基本密钥交换模式:
下图显示了 TLS 握手的全部流程:
Client Server
Key ^ ClientHello
Exch | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ Key
+ key_share* | Exch
+ pre_shared_key* v
{EncryptedExtensions} ^ Server
{CertificateRequest*} v Params
{Certificate*} ^
{CertificateVerify*} | Auth
{Finished} v
<-------- [Application Data*]
^ {Certificate*}
Auth | {CertificateVerify*}
v {Finished} -------->
[Application Data] <-------> [Application Data]
复制代码
+ 表示的是在以前标注的消息中发送的值得注意的扩展 * 表示可选的或者依赖一定条件的消息/扩展,它们不总是发送 () 表示消息由从 Client_early_traffic_secret 导出的密钥保护 {} 表示消息使用从一个 [sender]_handshake_traffic_secret 导出的密钥保护 [] 表示消息使用从 [sender]_application_traffic_secret_N 导出的密钥保护
握手可以被认为有三个阶段(见上图):
在密钥交换阶段,Client 会发送 ClientHello 消息,其中包含了一个随机 nonce(ClientHello.random);它提供了协议版本,一个对称密码/HKDF hash 对的列表;一个 Diffie-Hellman 密钥共享集合或一个预共享密钥标签(在 "key_share" 扩展中)集合,或二者都有;和可能的其它扩展。
Server 处理 ClientHello 并为连接确定合适的密码参数。然后它会以自己的 ServerHello 作为响应,其中表明了协商好的连接参数。ClientHello 和 ServerHello 合在一起来确定共享密钥。如果已经建立的 (EC)DHE 密钥正在被使用,则 ServerHello 中会包含一个 ”key_share” 扩展,和这个扩展一起的还有 Server 的临时 Diffie-Hellman 共享参数,这个共享参数必须与 Client 的一个共享参数在相同的组里。如果使用的是 PSK 密钥,则 ServerHello 中会包含一个 "pre_shared_key" 扩展以表明 Client 提供的哪一个 PSK 被选中。需要注意的是实现上可以将 (EC)DHE 和 PSK 一起使用,这种情况下两种扩展都需要提供。
随后 Server 会发送两个消息来建立 Server 参数:
最后,Client 和 Server 交换认证消息。TLS 在每次基于证书的认证时使用相同的消息集,(基于 PSK 的认证是密钥交换中的一个副作用)特别是:
接收到 Server 的消息之后,Client 会响应它的认证消息,即 Certificate,CertificateVerify (如果需要), 和 Finished。
这时握手已经完成,client 和 server 会提取出密钥用于记录层交换应用层数据,这些数据需要通过认证的加密来保护。应用层数据不能在 Finished 消息之前发送数据,必须等到记录层开始使用加密密钥之后才可以发送。需要注意的是 server 可以在收到 client 的认证消息之前发送应用数据,任何在这个时间点发送的数据,当然都是在发送给一个未被认证的对端。
如果 client 没有提供一个充分的 ”key_share” 扩展(例如,它只包含 server 不接受或不支持的 DHE 或 ECDHE 组),server 会使用一个 HelloRetryRequest 来纠正这个不匹配问题,client 需要使用一个合适的 ”key_share” 扩展来重启握手,如下图所示。如果没有通用的密码参数能够协商,server 必须发出一个适当的警报来中止握手。
Client Server
ClientHello
+ key_share -------->
HelloRetryRequest
<-------- + key_share
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
[Application Data] <-------> [Application Data]
复制代码
如上图,一个带有不匹配参数的完整握手过程的消息流程
注意,这个握手过程包含初始的 ClientHello/HelloRetryRequest 交换;它不能被新的 ClientHello 重置。
TLS还允许基本握手的几种优化变体,如以下部分所述。
虽然 TLS 预共享密钥(PSK)能够在带外建立,预共享密钥也能在一个之前的连接中建立然后重用(会话恢复)。一旦一次握手完成,server 就能给 client 发送一个与一个独特密钥对应的 PSK 密钥,这个密钥来自初次握手。然后 client 能够使用这个 PSK 密钥在将来的握手中协商相关 PSK 的使用。如果 server 接受它,新连接的安全上下文在密码学上就与初始连接关联在一起,从初次握手中得到的密钥就会用于装载密码状态来替代完整的握手。在 TLS 1.2 以及更低的版本中,这个功能由 "session IDs" 和 "session tickets" [RFC5077]来提供。这两个机制在 TLS 1.3 中都被废除了。
PSK 可以与 (EC)DHE 密钥交换算法一同使用以便使共享密钥具备前向安全,或者 PSK 可以被单独使用,这样是以丢失了应用数据的前向安全为代价。
下图显示了两次握手,第一次建立了一个 PSK,第二次时使用它:
Client Server
Initial Handshake:
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[Application Data] <-------> [Application Data]
Subsequent Handshake:
ClientHello
+ key_share*
+ pre_shared_key -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
{Finished}
<-------- [Application Data*]
{Finished} -------->
[Application Data] <-------> [Application Data]
复制代码
当 server 通过一个 PSK 进行认证时,它不会发送一个 Certificate 或一个 CertificateVerify 消息。当一个 client 通过 PSK 想恢复会话的时候,它也应当提供一个 "key_share" 给 server,以允许 server 拒绝恢复会话的时候降级到重新回答一个完整的握手流程中。Server 响应 "pre_shared_key" 扩展,使用 PSK 密钥协商建立连接,同时响应 "key_share" 扩展来进行 (EC)DHE 密钥建立,由此提供前向安全。
当 PKS 在带外提供时,PSK 密钥和与 PSK 一起使用的 KDF hash 算法也必须被提供。
注意:当使用一个带外提供的预共享密钥时,一个关键的考虑是在密钥生成时使用足够的熵,就像 [RFC4086] 中讨论的那样。从一个口令或其它低熵源导出的一个共享密钥并不安全。一个低熵密码,或口令,易遭受基于 PSK 绑定器的字典攻击。指定的 PSK 密钥并不是一个基于强口令的已认证的密钥交换,即使使用了 Diffie-Hellman 密钥建立方法。具体来说,它不会阻止可以观察到握手过程的攻击者对密码/预共享密钥进行暴力攻击。
当 client 和 server 共享一个 PSK(从外部获得或通过一个以前的握手获得)时,TLS 1.3 允许 client 在第一个发送出去的消息中携带数据("early data")。Client 使用这个 PSK 来认证 server 并加密 early data。
如下图所示,0-RTT 数据在第一个发送的消息中被加入到 1-RTT 握手过程中。握手的其余消息与带 PSK 会话恢复的 1-RTT 握手消息相同。
Client Server
ClientHello
+ early_data
+ key_share*
+ psk_key_exchange_modes
+ pre_shared_key
(Application Data*) -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
+ early_data*
{Finished}
<-------- [Application Data*]
(EndOfEarlyData)
{Finished} -------->
[Application Data] <-------> [Application Data]
复制代码
上图是 0-RTT 的信息流
+ 标明是在以前标注的消息中发送的值得注意的扩展 * 表示可选的或者依赖一定条件的消息/扩展,它们不总是发送 () 表示消息由从client_early_traffic_secret导出的密钥保护 {} 表示消息使用从一个[sender]_handshake_traffic_secret导出的密钥保护 []表示消息使用从[sender]_application_traffic_secret_N导出的密钥保护
0-RTT 数组安全性比其他类型的 TLS 数据要弱一些,特别是:
0-RTT 数据不能在连接中被复制(即 server 不会为同一连接处理相同的数据两次),并且攻击者将无法使 0-RTT 数据伪装起来像 1-RTT数据(因为它受不同的密钥保护)。
关于 0-RTT 的安全性,会单独有一篇文章来讨论。
Reference:
GitHub Repo:Halfrost-Field Follow: halfrost · GitHub Source: github.com/halfrost/Ha…