概述
前面章节,我们简单介绍了TLS的秘钥交换的算法。接下来我们来讲一下SSL层。
TLS层SSL层
TLS是介于网络层和传输层之间的半层。
协议层分类
TLS/SSL 协议位于应用层和传输层 TCP 协议之间。TLS 粗略的划分又可以分为 2 层:
靠近应用层的握手协议 TLS Handshaking Protocols
靠近 TCP 的记录层协议 TLS Record Protocol
SSL/TCL层
握手协议
TLS 握手协议还能细分为 5 个子协议:
如上图
change_cipher_spec (在 TLS 1.3 中这个协议已经删除,为了兼容 TLS 老版本,可能还会存在): 密码切换协议
alert: 告警协议
handshake: 握手协议
application_data: 应用数据协议
heartbeat (这个是 TLS 1.3 新加的,TLS 1.3 之前的版本没有这个协议) 心跳协议
开始加密通信之前,客户端和服务器首先必须建立连接和交换参数,这个过程叫做握手(handshake)。
基本过程
1、 client 端想server 端获取公钥
2、 双方协商生成"对话密钥"
3、 双方用对话秘钥进行通讯
一个TCL1.2 秘钥交换的大致过程
协议详解
记录层协议
记录层将上层的信息块分段为 TLSPlaintext
记录
TLSPlaintext
记录规范如下
1 2 3 4 5 6 struct { ContentType type; ProtocolVersion legacy_record_version; uint16 length; opaque fragment[TLSPlaintext.length]; } TLSPlaintext;
记录层将上层的信息块分段为 TLSPlaintext
记录,TLSPlaintext
中包含 2^14 字节或更少字节块的数据。根据底层 ContentType
的不同,消息边界的处理方式也不同。TLS 1.3 中的规则比 TLS 1.2 中强制执行的规则更加严格。
握手消息可以合并为单个 TLSPlaintext
记录,或者在几个记录中分段,前提是:
握手消息不得与其他记录类型交错。也就是说,如果握手消息被分成两个或多个记录,则它们之间不能有任何其他记录。
握手消息绝不能跨越密钥更改。实现方必须验证密钥更改之前的所有消息是否与记录边界对齐; 如果没有,那么他们必须用 “unexpected_message” alert 消息终止连接。因为 ClientHello,EndOfEarlyData,ServerHello,Finished 和 KeyUpdate 消息可以在密钥更改之前立即发生,所以实现方必须将这些消息与记录边界对齐。
实现方绝不能发送握手类型的零长度片段,即使这些片段包含填充。
另外 Alert 消息禁止在记录之间进行分段,并且多条 alert
消息不得合并为单个 TLSPlaintext
记录。换句话说,具有 alert 类型的记录必须只包含一条消息。
应用数据消息包含对 TLS 不透明的数据。应用数据消息始终应该受到保护。可以发送应用数据的零长度片段,因为它们可能作为流量分析对策使用。应用数据片段可以拆分为多个记录,也可以合并为一个记录。
字段详解
ContentType
1 2 3 4 5 6 7 8 enum { invalid(0 ), change_cipher_spec(20 ), alert(21 ), handshake(22 ), application_data(23 ), heartbeat(24 ), } ContentType;
消息头类型
ContentType
协议内容
change_cipher_spec
0x014
在 TLS 1.3 中这个协议已经删除,为了兼容 TLS 老版本,可能还会存在): 密码切换协议
alert
0x015
告警协议
handshake
0x016
握手协议
application_data
0x017
数据传输协议
heartbeat (TLS 1.3 新增)
0x018
心跳协议
ProtocolVersion 协议版本
协议版本
version
TLS 1.3
0x0304
TLS 1.2
0x0303
TLS 1.1
0x0302
TLS 1.0
0x0301
SSL 3.0
0x0300
fragment
TLSPlaintext.fragment
的长度(以字节为单位)。长度不得超过 2 ^ 14 字节。接收超过此长度的记录的端点必须使用 "record_overflow" alert
消息终止连接。
正在传输的数据。此字段的值是透明的,它并被视为一个独立的块,由类型字段指定的更高级别协议处理。
TLS 记录层协议在整个 TLS 协议中的定位如下:
封装处理 TLS 上层(握手层)中的平行子协议(TLS 1.3 中是 5 个子协议,TLS 1.2 及更老的版本是 4 个子协议),加上消息头,打包往下传递给 TCP 处理。
对上层应用数据协议进行密码保护,对其他的子协议只是简单封装(即不加密)
TLS 密码切换协议(change_cipher_spec)
change_cipher_spec 在TSL1.3 可能被删除
change_cipher_spec
(以下简称 CCS 协议) 协议,是 TLS 记录层对应用数据是否进行加密的分界线。客户端或者服务端一旦收到对端发来的 CCS 协议,就表明接下来传输数据过程中可以对应用数据协议进行加密了。
TLS 记录层在处理上层 5 个协议(密码切换协议,警告协议,握手协议,心跳协议,应用数据协议)的时候,TLS 不同版本对不同协议加密的情况不同,具体情况如下:
议版本
密码切换协议
警告协议
握手协议
心跳协议
应用数据协议
TLS 1.3
无
✅(根据连接状态不同进行加密,即一部分会加密)
✅(一部分加密)
❌
✅
TLS 1.2
❌
❌
❌
无
✅
协议数据结构如下:
1 2 3 struct { enum { change_cipher_spec(1 ), (255 ) } type; } ChangeCipherSpec;
经过 TLS 记录层包装以后,结构如下:
TLS 警告协议(alert)
TLS
提供 alert
内容类型用来表示关闭信息和错误。
与其他消息一样,alert 消息也会根据当前连接状态的进行加密。
在 TLS
1.3 中,错误的严重性隐含在正在发送的警报类型中,并且可以安全地忽略 “level
” 字段。
"close_notify
" alert
用于表示连接从一个方向开始有序的关闭。收到这样的警报后,TLS
实现方应该表明应用程序的数据结束。
收到错误警报后,TLS
实现方应该向应用程序表示出现了错误,并且不允许在连接上发送或接收任何其他数据。
协议数据结构如下:
1 2 3 4 5 enum { warning(1 ), fatal(2 ), (255 ) } AlertLevel;struct { AlertLevel level; AlertDescription description; } Alert;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 enum { close_notify(0 ), unexpected_message(10 ), bad_record_mac(20 ), decryption_failed_RESERVED(21 ), record_overflow(22 ), decompression_failure(30 ), handshake_failure(40 ), no_certificate_RESERVED(41 ), bad_certificate(42 ), unsupported_certificate(43 ), certificate_revoked(44 ), certificate_expired(45 ), certificate_unknown(46 ), illegal_parameter(47 ), unknown_ca(48 ), access_denied(49 ), decode_error(50 ), decrypt_error(51 ), export_restriction_RESERVED(60 ), protocol_version(70 ), insufficient_security(71 ), internal_error(80 ), user_canceled(90 ), no_renegotiation(100 ), unsupported_extension(110 ), (255 ) } AlertDescription;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 enum { close_notify(0 ), unexpected_message(10 ), bad_record_mac(20 ), decryption_failed_RESERVED(21 ), record_overflow(22 ), decompression_failure_RESERVED(30 ), handshake_failure(40 ), no_certificate_RESERVED(41 ), bad_certificate(42 ), unsupported_certificate(43 ), certificate_revoked(44 ), certificate_expired(45 ), certificate_unknown(46 ), illegal_parameter(47 ), unknown_ca(48 ), access_denied(49 ), decode_error(50 ), decrypt_error(51 ), export_restriction_RESERVED(60 ), protocol_version(70 ), insufficient_security(71 ), internal_error(80 ), inappropriate_fallback(86 ), user_canceled(90 ), no_renegotiation_RESERVED(100 ), missing_extension(109 ), unsupported_extension(110 ), certificate_unobtainable_RESERVED(111 ), unrecognized_name(112 ), bad_certificate_status_response(113 ), bad_certificate_hash_value_RESERVED(114 ), unknown_psk_identity(115 ), certificate_required(116 ), no_application_protocol(120 ), (255 ) } AlertDescription;
TLS 1.3 比 TLS 1.2 新增了 9 个警告描述信息:
1 2 3 4 5 6 7 8 9 inappropriate_fallback(86 ), missing_extension(109 ), certificate_unobtainable_RESERVED(111 ), unrecognized_name(112 ), bad_certificate_status_response(113 ), bad_certificate_hash_value_RESERVED(114 ), unknown_psk_identity(115 ), certificate_required(116 ), no_application_protocol(120 ),
数据传输协议(application_data)
应用数据协议就是 TLS 上层的各种协议,TLS 主要保护的数据就是应用数据协议的数据。
TLS 记录层会根据加密模式的不同在应用数据的末尾加上 MAC 校验数据。
心跳协议
这个协议是 TLS 1.3 新增的。更加细节可以看这篇文章《TLS & DTLS Heartbeat Extension》 , [RFC 6520] 翻译的。感兴趣的可以去看看这篇文章。这篇文章还涉及到了 DTLS 和 PMTU 发现。
协议数据结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 enum { heartbeat_request(1 ), heartbeat_response(2 ), (255 ) } HeartbeatMessageType; struct { HeartbeatMessageType type; uint16 payload_length; opaque payload[HeartbeatMessage.payload_length]; opaque padding[padding_length]; } HeartbeatMessage;
经过 TLS 记录层包装以后,结构如下:
根据 [RFC6066] 中的定义,在协商的时候,HeartbeatMessage 的总长度不得超过 2 ^ 14 或 max_fragment_length。
HeartbeatMessage
的长度为 TLS 的 TLSPlaintext.length
和 DTLS
的 DTLSPlaintext.length
。此外,类型 type
字段的长度是 1 个字节,并且 payload_length
的长度是 2 个字节。因此,padding_length 是TLSPlaintext.length - payload_length
- 3 用于 TLS,DTLSPlaintext.length - payload_length - 3
用于 DTLS
。padding_length
必须至少为 16。
HeartbeatMessage
的发送方必须使用至少 16 个字节的随机填充。必须忽略收到的 HeartbeatMessage
消息的填充。
握手协议
握手协议是整个 TLS
协议簇中最最核心的协议,HTTPS
能保证安全也是因为它的功劳。
握手协议由多个子消息构成,服务端和客户端第一次完成一次握手需要 2-RTT
。
握手协议的目的是为了双方协商出密码块,这个密码块会交给 TLS 记录层进行密钥加密。也就是说握手协议达成的“共识”(密码块)是整个 TLS 和 HTTPS
安全的基础。
握手协议在 TLS 1.2
和 TLS 1.3
中发生了很大的变化。TLS 1.3
的 0-RTT 是一个全新的概念。两个版本在密钥协商上,密码套件选择上都有很大不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 enum { hello_request(0 ), client_hello(1 ), server_hello(2 ), certificate(11 ), server_key_exchange (12 ), certificate_request(13 ), server_hello_done(14 ), certificate_verify(15 ), client_key_exchange(16 ), finished(20 ) (255 ) } HandshakeType; struct { HandshakeType msg_type; uint24 length; select (HandshakeType) { case hello_request: HelloRequest; case client_hello: ClientHello; case server_hello: ServerHello; case certificate: Certificate; case server_key_exchange: ServerKeyExchange; case certificate_request: CertificateRequest; case server_hello_done: ServerHelloDone; case certificate_verify: CertificateVerify; case client_key_exchange: ClientKeyExchange; case finished: Finished; } body; } Handshake;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 enum { hello_request_RESERVED(0 ), client_hello(1 ), server_hello(2 ), hello_verify_request_RESERVED(3 ), new_session_ticket(4 ), end_of_early_data(5 ), hello_retry_request_RESERVED(6 ), encrypted_extensions(8 ), certificate(11 ), server_key_exchange_RESERVED(12 ), certificate_request(13 ), server_hello_done_RESERVED(14 ), certificate_verify(15 ), client_key_exchange_RESERVED(16 ), finished(20 ), certificate_url_RESERVED(21 ), certificate_status_RESERVED(22 ), supplemental_data_RESERVED(23 ), key_update(24 ), message_hash(254 ), (255 ) } HandshakeType; struct { HandshakeType msg_type; uint24 length; select (Handshake.msg_type) { case client_hello: ClientHello; case server_hello: ServerHello; case end_of_early_data: EndOfEarlyData; case encrypted_extensions: EncryptedExtensions; case certificate_request: CertificateRequest; case certificate: Certificate; case certificate_verify: CertificateVerify; case finished: Finished; case new_session_ticket: NewSessionTicket; case key_update: KeyUpdate; }; } Handshake
握手消息类型虽然有很多种,但是最终传到 TLS 记录层,有些会被合并到一条消息。
参考