openssl1.1.1 final正式发布后,准备在线上开始灰度了。虽然之前有一些了解,但是其实没有准确的认知,所以需要再好好的分析一下:

大体上的区别

我们先看下tls1.3和tls1.2有什么区别:
tls1.3:

tls1.2:

从这两张图就可以明显的看出tls1.3和tls1.2的数据包的不同,在tls1.3中,自从ServerHello之后全是Application Data了,甚至一度怀疑是不是wireshark这个软件是不是实现有问题。

我们看下tls1.3 rfc,如下整理了下
tls1.3完整握手:

tls1.2完整握手:

通过tls1.3和tls1.2的握手流程图对比,可以明显的看见tls1.3的握手流程比tls1.2的要少两次握手。虽然握手少了两次,但是像服务器证书这些必要的信息并没有减少提供,只是说在tls1.3中通过ClientHello和ServerHello这两步信息交换,就已经完成了tls1.2上的ServerKeyExchange和ClientKeyExchange这两个密钥交换的操作,在tls1.3的ServerHello之后的信息都已经走了加密通道。

解析ClientHello

先看一下使用openssl进行tls1.3握手的ClientHello的数据包结构:

看一下rfc中对ClientHello的结构定义:

uint16 ProtocolVersion;
     opaque Random[32];

     uint8 CipherSuite[2];    /* Cryptographic suite selector */

     struct {
         ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
         Random random;
         opaque legacy_session_id<0..32>;
         CipherSuite cipher_suites<2..2^16-2>;
         opaque legacy_compression_methods<1..2^8-1>;
         Extension extensions<8..2^16-1>;
     } ClientHello;

需要注意的是,在rfc文档中已经说明,legacy_version的值必须是0x0303(TLS1.2),并且session_id的长度必须设置为0

现在在看一下tls1.2的ClientHello数据包:

在这里我首先注意到的是两个Version后面的值,这两个值tls1.3和tls1.2是没有区别的,那么又是如何区分出是tls1.2还tls1.3的clientHello呢?在tls1.3的rfc中有说明:

The "supported_versions" ClientHello extension can be used to
      negotiate the version of TLS to use, in preference to the
      legacy_version field of the ClientHello.

在区分出tls1.3或tls1.2的数据包是通过supported_versions这个扩展字段中区分出来的:

tls1.3的ClientHello增加了很多的扩展,这些扩展先暂时放一边,看一下我感觉最差异的地方:Cipher Suites。

Cipher Suites

tls1.3现在支持的Cipher_Suites如下:

+------------------------------+-------------+
| Description                  | Value       |
+------------------------------+-------------+
| TLS_AES_128_GCM_SHA256       | {0x13,0x01} |
|                              |             |
| TLS_AES_256_GCM_SHA384       | {0x13,0x02} |
|                              |             |
| TLS_CHACHA20_POLY1305_SHA256 | {0x13,0x03} |
|                              |             |
| TLS_AES_128_CCM_SHA256       | {0x13,0x04} |
|                              |             |
| TLS_AES_128_CCM_8_SHA256     | {0x13,0x05} |
+------------------------------+-------------+

tls1.3 目前定义了这5个密钥套件,从表面上看已经隐藏了密钥交换算法了,因为都是使用ECDH算法。

更多的扩展

通过openssl抓包,可以看见多的扩展有这些:

supported_versions
pre_shared_key
key_share

supported_versions

客户端使用这个扩展表示它能够支持哪些版本的tls,按照这个扩展中的列举的tls版本进行顺序选择。并且如果该扩展存在那么服务器端必须忽略ClientHello.legacy_version的值(就是那个规定必须写成0x0303的),仅使用supported_versions中的扩展中存在的tls版本,并且忽略该扩展中任何未知的版本。

pre_shared_key

该扩展是用来标识在PSK握手中的共享密钥。(ps: 该扩展在使用openssl进行tls1.3握手的时候,没有获取到)

key_share

在该扩展中包含端点的密码参数。
在rfc中是这样定义的:

struct {
         NamedGroup group;
         opaque key_exchange<1..2^16-1>;
     } KeyShareEntry

在上面的的key_share中选用x25519DH参数。还有可以使用类似secp256r1、secp384r1、secp521r1的DH参数

解析ServerHello信息

先看一下openssl响应的ServerHello信息:

在这里可以可以看到服务器端选择了TLS_AES_256_GCM_SHA384这个加密套件,并且只返回了key_share这个扩展信息,并没有pre_shared_key扩展,因此根据rfc文档,那么服务端选择的就是(EC)DHE ,

在看一眼key_share中的结构,和ClientHello中的key_share结构是一致的,到这里,Client和Server已经完成了密钥交换了,剩下的信息就全部通过加密通道进行传输了。

如何做密码协商

Client一般通过ClientHello信息提供这些信息:

客户端支持的加密套件 (Cipher Suites)
客户端支持的椭圆算法:(Supported Groups)
客户端支持的签名算法:(Signature_algorithms)
客户端支持的可能和PSK一起使用的密钥交换模式:(psk_key_exchange_modes)

密码协商有两种方式:

PSK
DHE

如果服务器选择是使用DHE的协商模式,那么,服务器支持的加密套件、椭圆算法和签名算法必须和客户端提供的存在交集(三者都需要)。

如果服务器选择是使用PSK的协商模式,那么,服务器必须从客户端的psk_key_exchange_modes的扩展中提供的支持的模式。

如果服务器使用的是PSK模式,那么就会在ServerHello的扩展中添加pre_shared_key这个扩展。

如果服务器使用的是EC(DHE)模式,那么服务器会在ServerHello的扩展中添加key_share扩展。

总结

总体介绍大概是这样,后面我会再介绍下session reuse的区别,和如何做到0 RTT。