当前所有的 TLS/SSL 版本:
- SSL 2.0 (废弃)
- SSL 3.0 (废弃)
- TLS 1.0
- TLS 1.1
- TLS 1.2
- TLS 1.3
无论是客户端还是服务器端,同时支持多个协议版本非常常见,那么不可避免的需要进行版本协商。 不过,这并不难,因为TLS协议已经制定了协商的方法。
由于当前 SSL2.0 已经没人使用,并且它的数据包格式和其他版本也不兼容,版本协商不可用,这里不在讨论 SSL2.0。
SSL 握手阶段需要通过交换 ClientHello 和 ServerHello 来协商当前即将建立的 TLS/SSL 版本。
TLS 1.3 协议下的协商
TLS1.3 扩展:supported_versions
这个扩展用来包含一个或多个 TLS 版本信息, 它可以在 ClientHello
和 ServerHello
中使用。 在 ClientHello 中表示客户端支持的 TLS 版本. 在 Server Hello 中,表示服务器端想使用的 TLS 版本.
当扩展中包含多个 TLS 版本信息时, 排位越靠前,优先级越高。
当 ClientHello 中不包含 Supported_versions 扩展时,服务器端按照 ClientHello.legacy_version
中的值(这个值可以是 0x0304 或者更新的版本)来进行 TLS 版本协商。
当 ClientHello 中包含 supported_versions 扩展时,服务器忽略ClientHello.legacy_version
中的值, 而是按照 supported_versions 中的值进行 TLS 版本协商。
如果服务器选择了一个 TLS 1.3 之前的版本,那么 ServerHello.version
必须设置为选择的版本,并且不能包含 supported_versions 扩展。
如果服务器选择使用 TLS 1.3,那么 ServerHello.legacy_version
必须设置为 0x0303 (TLS 1.2), 并且设置 supported_versions 扩展中包含 0x0304 (TLS 1.3).
协商方式
对于 TLS1.3 的客户端,如果它想连接到一个不支持TLS1.3的服务器,那么它需要发送一个 TLS1.3 ClientHello
, 其中的版本号(ClientHello.legacy_version
)为 0x0303(TLS)
, 同时需要设置正确的supported_versions
扩展。 如果服务器不支持 TLS13,它将会给客户端回复一个包含低版本号的 ServerHello
. 如果服务器支持 TLS 13,继续进行即可。
如果客户端不支持服务器端选择的协议版本,客户端必须中止握手,并发送 “protocol_version” alert.
对于 TLS1.3 服务器端来讲,它也可以接受一个携带一个低版本号的 ClientHello
,如果有supported_versions
扩展, 根据这个扩展内容来协商最终版本号。如果没有该扩展,服务器则选择ClientHello.legacy_version
和TLS 1.2
两者中较小的。例如,服务器支持 TLS 1.0, TLS 1.1, TLS 1.2, legacy_version 是 TLS 1.0, 那服务器应该选择 TLS 1.0.
如果没有supported_versions
扩展并且服务器只支持比ClientHello.legacy_version
大的协议版本,服务器必须中止握手,并发送 protocol_version alert.
现实问题:
部分 TLS 服务器端的实现没有严格按照 RFC 规定来做,他们在收到不认识的 TLS ClientHello 扩展 或者 ClientHello 版本号的时候,会中止握手,导致版本协商失败。
例子 1:客户端支持 TLS1.3和TLS1.2, 服务器只支持 TLS1.2
// Client Hello (忽略了其他字段)
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 516
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 512
Version: TLS 1.2 (0x0303)
Extension: supported_versions (len=7)
Type: supported_versions (43)
Length: 7
Supported Versions length: 6
SUpported Version: Reserved (GREASE) (0x4a4a)
Supported Version: TLS 1.3 (0x0304)
Supported Version: TLS 1.2 (0x0303)
....
ClientHello 中包含 supported_versions: TLS 1.3, TLS1.2. 按照 TLS1.3 优先,TLS1.2 次之的顺序排列。
注意: Record.Version: TLS 1.0 是为了兼容性。
// Server Hello
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 84
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 80
Version: TLS 1.2 (0x0303)
Random:
Session ID Length: 0
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Compression Method: null (0)
Extensions Length: 40
Extension: renegotiation_info (len=1)
Extension: server_name (len=0)
Extension: ec_point_formats (len=4)
Extension: session_ticket (len=0)
Extension: application_layer_protocol_negotiation (len=11)
Extension: extended_master_secret (len=0)
由于服务器不支持 TLS 1.3, 因此选择了 TLS 1.2. 此时 Record.Version 和 Handshake.Version 都设置为 TLS 1.2.
响应中并没有包含 supported_versions 扩展。
例子 2:客户端支持 TLS1.3, 服务器支持 TLS1.3
// Client Hello
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 240
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 236
Version: TLS 1.2 (0x0303)
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
....
// Server Hello
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 146
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 142
Version: TLS 1.2 (0x0303)
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
...
服务器选择了 TLS 1.3, 此时 Record.Version 和 Handshake.Version 都设置为 TLS 1.2.
使用 ServerHello 中 supported_versions 扩展告知客户端它选择了 TLS 1.3.
TLS 1.2 及以前版本下的版本协商
对于 TLS1.2 客户端来说,他们通常发送一个正常的 TLS 1.2 的 ClientHello
消息,其中 ClientHello.client_version
的值为 {3,3}(TLS 1.2)
. 如果服务器端不支持 TLS1. 2, 服务器会发送一个包含更低 TLS 版本的 ServerHello
给客户端。 如果支持,那么正常进行即可。
如果客户端不支持服务器端选择的协议版本,客户端必须中止握手,并发送 “protocol_version” alert.
如果 TLS 服务器收到一个包含更高版本号的 ClientHello
, 它必须回复一个 ServerHello
, 其中的版本号是服务器端支持的最高版本号。
服务器端可能会收到一个包含比自己最高版本号低的版本号的ClientHello
, 此时服务器可以回复一个ServerHello
, 其中的版本号是服务器端支持的版本号中不大于ClientHello.client_version
的最大版本号。
例子 3:客户端支持 TLS1.1, 服务器只支持 TLS1.0
// Client Hello
Transport Layer Security
TLSv1 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.1 (0x0302)
Length: 138
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 134
Version: TLS 1.1 (0x0302)
....
由于客户端支持的最高 TLS 版本就是 TLS 1.1. 此时 Record.Version 和 Handshake.Version 都设置为 TLS 1.1.
// Server Hello
Transport Layer Security
TLSv1 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 81
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 77
Version: TLS 1.0 (0x0301)
....
由于服务器端不支持 TLS 1.1, 所以服务器选择了 TLS 1.0, 这里 Record.Version 和 Handshake.Version 都设置为 TLS 1.0.
参考链接
ref: https://www.rfc-editor.org/rfc/rfc5246#appendix-E.1
ref: https://www.rfc-editor.org/rfc/rfc8446.html#appendix-D