TCP/IP系列(4)-SSL/TSL详解(2)

SSL/TSL详解(2)-TLS层次分类

Posted by Jason Lee on 2020-04-08

概述

前面章节,我们简单介绍了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), /* RFC 6520 */
} 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

  • length:

TLSPlaintext.fragment 的长度(以字节为单位)。长度不得超过 2 ^ 14 字节。接收超过此长度的记录的端点必须使用 "record_overflow" alert 消息终止连接。

  • fragment:

正在传输的数据。此字段的值是透明的,它并被视为一个独立的块,由类型字段指定的更高级别协议处理。

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;

  • TLS 1.2 的所有警告描述信息:
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), /* new */
(255)
} AlertDescription;
  • TLS 1.3 的所有警告描述信息:
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.lengthDTLSDTLSPlaintext.length。此外,类型 type 字段的长度是 1 个字节,并且 payload_length 的长度是 2 个字节。因此,padding_length 是TLSPlaintext.length - payload_length - 3 用于 TLS,DTLSPlaintext.length - payload_length - 3 用于 DTLSpadding_length 必须至少为 16。

HeartbeatMessage 的发送方必须使用至少 16 个字节的随机填充。必须忽略收到的 HeartbeatMessage 消息的填充。

握手协议

握手协议是整个 TLS 协议簇中最最核心的协议,HTTPS 能保证安全也是因为它的功劳。

握手协议由多个子消息构成,服务端和客户端第一次完成一次握手需要 2-RTT

握手协议的目的是为了双方协商出密码块,这个密码块会交给 TLS 记录层进行密钥加密。也就是说握手协议达成的“共识”(密码块)是整个 TLS 和 HTTPS 安全的基础。

握手协议在 TLS 1.2TLS 1.3 中发生了很大的变化。TLS 1.30-RTT是一个全新的概念。两个版本在密钥协商上,密码套件选择上都有很大不同。

  • TLS 1.2 协议数据结构如下:
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;
  • TLS 1.3 协议数据结构如下:
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; /* handshake type */
uint24 length; /* bytes in message */
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 记录层,有些会被合并到一条消息。

参考



支付宝打赏 微信打赏

赞赏一下