Go语言中文网,致力于每日分享编码、开源等知识,欢迎关注我,会有意想不到的收获!
这是 直观感受 TLS 握手流程 最后一部分。
十. 直观感受 TLS 1.3 会话恢复
这一章,笔者用 wireshark 抓取 TLS 1.3 会话恢复中的数据包,让读者直观感受一下 TLS 1.3 会话恢复流程。
1. PSK 会话恢复

这是 TLS 1.3 会话恢复的完整流程。




上面这 4 个扩展是 TLS 1.3 PSK 会话恢复中 ClientHello 必须配置的。psk_key_exchange_modes、pre_shared_key、key_share、supported_versions。



上面这 3 个扩展是 TLS 1.3 PSK 会话恢复中 ServerHello 必须配置的。pre_shared_key、key_share、supported_versions。

一旦 PSK 校验完成,Server 就不需要再次发送证书了,直接回应 ChangeCipherSpec、Encrypted Extensions、Finished 即可完成会话恢复。
2. 0-RTT
截止到笔者写这篇文章为止,当前主流浏览器对 TLS 1.3 的支持度如下图。

Google Chrome Canary 最新 74.0.3702.0 还不能支持 0-RTT 模式,Firefox Nightly 最新 67.0a1 可以支持 0-RTT 模式(在 about:config 中 security.tls.enable_0rtt_data 设置为 true),Safari 最新的 12.0.3 (14606.4.5) 还不能支持 0-RTT 模式。所以笔者只能用 Firefox Nightly 抓取 0-RTT 的包。
当然 OpenSSL 最新版 1.1.1a 的 Client 是支持发送 early_data 的,也就是支持 0-RTT 的,用它来调试 TLS 1.3 0-RTT 也更加方便。
先来看看支持 0-RTT 的 Firefox Nightly 抓到的包是怎么样的。








可以发现整个会话恢复过程满足了 0-RTT 的条件,所以 0-RTT 开启成功。
在用 OpenSSL 的 Client 来测试测试 0-RTT。
先将必要参数导出来,比如协商的密钥和 session 信息。
$ openssl s_client -connect halfrost.com:443 -tls1_3 -keylogfile=/Users/ydz/Documents/sslkeylog.log -sess_out=/Users/ydz/Documents/tls13.sess
输出如下:
CONNECTED(00000006)
depth=1 C = US, O = Let’s Encrypt, CN = Let’s Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
0 s:CN = halfrost.com
i:C = US, O = Let’s Encrypt, CN = Let’s Encrypt Authority X3
1 s:C = US, O = Let’s Encrypt, CN = Let’s Encrypt Authority X3
i:O = Digital Signature Trust Co., CN = DST Root CA X3
2 s:C = US, O = Let’s Encrypt, CN = Let’s Encrypt Authority X3
i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEljCCA36gAwIBAgISA9VdA6rPN6mIzBxEPL/3iAICMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAyMTAwMTQxMjJaFw0x
OTA1MTEwMTQxMjJaMBcxFTATBgNVBAMTDGhhbGZyb3N0LmNvbTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABA7sYzIwq29BkT1mQ2TSZRPe34BlnuqN65xoLY+A87M8
PpblV0IvNyj4ZdcgiSmSZffocVF6wzck6TmsQ/j2/sujggJyMIICbjAOBgNVHQ8B
Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB
/wQCMAAwHQYDVR0OBBYEFOD4YIpf+PkD1Jvy+eayPn0csEi/MB8GA1UdIwQYMBaA
FKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcw
AYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcw
AoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wKQYDVR0RBCIw
IIIMaGFsZnJv\ghfhjghjjbmd3cuaGFsZnJvc3QuY29tMEwGA1UdIARFMEMwCAYG
Z4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMu
bGV0c2VuY3J5cHQub3JnMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHUA4mlLribo
6UAJ6IYbtjuD1D7n/nSI+6SPKJMBnd3x2/4AAAFo1UfZTgAABAMARjBEAiAsXJLC
A5uO2R926Dba3fZpV/zvzG9tCPVtTKAeso5bAwIgMXoLRtLqhG5bEcXIpGXJcrd0
6S8tbUdS9YRAIWpMX1oAdgApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0
eAAAAWjVR9lQAAAEAwBHMEUCIHv6NJ9MWMiL+AHxU8ilL3APMmPkUcc03SjBiDaW
Vm6JAiEA5YF/XHKuYH0S0+mqfB+YdT0FIey9wFQObkR4/Qvzla4wDQYJKoZIhvcN
AQELBQADggEBAHU7a+EgzdhrsyD+2ch7AGD1n1TjDfdxkEjmoitN0Tjh4q3jP/IK
7FPs0LBsDRusmtJVK3gZQc9cTEy/om86VQtcnV0LhK83GnFUIuLTEzeTZmnz6Qbs
3KznprZH0DRUbfpmZsDNIfBEOUOXiBR4DpLd3tPVfRkQowmO6o39vM4UOGlB0zIA
g977q97IT6wS9BCEiGmuF0HSjpLfiPhTy9bpl2VGcJVpIy2TS+d4+JWRI7K5BFSz
ncGDzHJ+zGsx4wS+dxuiwaS9hw4c0FG2V4kMFnA+orAa/oTnfwFlRIehTbDBO+rN
TNtjm4yh63M9gInoQEI1REl2EkGcWug6Ijs=
-----END CERTIFICATE-----
subject=CN = halfrost.com
issuer=C = US, O = Let’s Encrypt, CN = Let’s Encrypt Authority X3
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3912 bytes and written 316 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: DECE5063ABC2D1162A5E767C55083FDFFA6A86B64082FE3AD990A213AE
Session-ID-ctx:
Resumption PSK: EACCC93ACB3DC420DF5027BEC576EE130D11BF546463034C1BB92B54806057E0C9F5C3DB557AD10D425E
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 86400 (seconds)
TLS session ticket:
0000 - 0b 8d e5 44 b2 62 71 9d-f9 0a ec da f0 d0 6a 0b ...D.bq.......j.
0010 - 97 5d 63 21 ea 1e 8a 69-01 52 a9 0a 19 bf 5c a3 .]c!...i.R....\.
0020 - 67 45 a3 a0 28 65 ea 9c-c8 d4 cf df 5d c5 5a be gE..(e......].Z.
0030 - 32 45 0d 1e af f7 32 67-4a d8 66 cb b6 cb c8 0e 2E....QgJ.f.....
0040 - 6b b8 53 a8 d2 d4 4b 7b-cc a6 cb 52 39 61 20 6d k.S...K{...R9a m
0050 - 75 f8 cb 43 11 1d 58 a2-de 2b 74 b0 ca 70 a2 9c u..C..X..+t..p..
0060 - 85 6b 1a 00 9a f1 bd 9b-8c b4 5a 41 aa 4b 64 5d .k........ZA.Kd]
0070 - 5a 48 23 a6 10 49 4f 61-c9 57 74 f4 56 50 83 1a ZH#..IOa.Wt.VP..
0080 - 1b 74 6c ea 09 99 42 f5-d6 3c 6d 4f 5b 98 ca b3 .tl...B..<mO....
0090 - c7 72 56 5c 6c 67 71 77-8d 68 f7 54 e5 e3 7b d3 .rV\lgqw.h.T..{.
00a0 - 24 ff 42 0c 3f 12 27 42-7f 9e 0a 4c c2 79 60 45 $.B.?.’B...L.y`E
00b0 - 2d 77 a2 c8 2f f5 85 34-fa ce 79 ee 0b ea 00 c1 -w../..4..y.....
00c0 - 74 33 f0 6c af 7a 1a 55-f8 35 bd 5e 49 66 6f 06 t3.l.z.U.5.^Ifo.
00d0 - c6 38 ed a6 82 e2 c8 77-99 b7 34 9a 4a 9a 31 40 .8.....w..4.J.1@
00e0 - f1 93 a0 94 7f 1e 8d e0-54 29 dc e3 6f 5c 93 21 ........T)..o\.!
Start Time: 1549886406
Timeout : 7200 (sec)
Verify return code: 20 (unable to get local issuer certificate)
Extended master secret: no
Max Early Data: 16384
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: B7E28DE5DF2C95F2E3DE43732E4F9A45A8943ED3856B73CAB5E7260E7
Session-ID-ctx:
Resumption PSK: BF2BA2304BEB2B948F7BF6617D0KDRNFB9CD5466DEC1EB9697D2543B7BB913BC7854359D7F5DF7559D67
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 86400 (seconds)
TLS session ticket:
0000 - 0b 8d e5 44 b2 62 71 9d-f9 0a ec da f0 d0 6a 0b ...D.bq.......j.
0010 - b4 9f cc 17 63 9a 70 c8-63 f8 2e c4 9f d4 a1 f8 ....c.p.c.......
0020 - 22 34 22 03 d0 f9 78 66-a0 d4 2f 62 53 d3 d8 e3 "4"...xf../bS...
0030 - 55 2c a5 7c 0b 19 b3 fc-77 55 8c de 0b 2d 00 bd U,.|....wUL..-..
0040 - b8 fa 2e 00 30 78 c8 dc-35 14 d3 61 f0 69 38 59 ...%0x..5..a.i8Y
0050 - ee 2a 75 7e 50 34 3f e3-25 04 71 1c 6e c9 c8 20 .*u~P4?.%.q.n..
0060 - d7 4e 44 b3 69 56 50 23-38 c2 f1 1e ac 10 a7 ff .ND.iVP#8.......
0070 - 96 cf fe ff 4d 07 7e 08-2d 37 49 78 ab 1d 78 6e ....M.~.-7Ix..xn
0080 - 62 4b 99 e7 37 03 3e a2-89 de 61 48 a1 c5 77 18 bK..7.>...aH..w.
0090 - 6f 1c 95 8a 0d 1d 17 68-88 8a 01 5b f0 dc ea 06 o......h...[....
00a0 - 98 dc 7e 94 f8 ef 4a 72-ff ba e5 03 07 c7 3d d0 ..~...Jr......=.
00b0 - c8 91 a6 ae 9a df 92 25-05 63 77 03 b0 bc b4 ab .......%.c......
00c0 - 36 cb 0f 8c 5d ec 58 65-7c 97 2a 30 57 4a 96 b9 6...].Xe|.*0WJ..
00d0 - 60 21 12 76 77 4c 6d 0d-12 0c 50 cc f5 da 54 4e `!.vwLm...P...TN
00e0 - 4b 27 5f 1b dd 11 b1 8d-7f e0 37 43 34 a3 88 34 K’_.......7C4..4
Start Time: 1549886406
Timeout : 7200 (sec)
Verify return code: 20 (unable to get local issuer certificate)
Extended master secret: no
Max Early Data: 16384
---
read R BLOCK
接下来在复用刚刚的连接,命令如下:
$ openssl s_client -connect halfrost.com:443 -tls1_3 -keylogfile=/Users/ydz/Documents/sslkeylog.log -sess_in=/Users/ydz/Documents/tls13.sess -early_data=/Users/ydz/Documents/req.txt
req.txt 里面只是简单的写一个 GET 请求:
GET / HTTP/1.1 HOST: halfrost.com Early-Data: 657567765
执行 s_client 以后,输出如下:
CONNECTED(00000006) --- Server certificate -----BEGIN CERTIFICATE----- MIIElzCCA3+gAwIBAgISA604VEs+7Wwch5cNQDshC4t+MA0GCSqGSIb3DQEBCwUA MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODEyMDgxMzQzMzhaFw0x OTAzMDgxMzQzMzhaMBcxFTATBgNVBAMTDGhhbGZyb3N0LmNvbTBZMBMGByqGSM49 AgEGCCqGSM49AwEHA0IABA7sYzIwq29BkT1mQ2TSZRPe34BlnuqN65xoLY+A87M8 PpblV0IvNyj4ZdcgiSmSZffocVF6wzck6TmsQ/j2/sujggJzMIICbzAOBgNVHQ8B Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB /wQCMAAwHQYDVR0OBBYEFOD4YIpf+PkD1Jvy+eayPn0csEi/MB8GA1UdIwQYMBaA FKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcw AYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcw AoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wKQYDVR0RBCIw IIIMaGFsZnJvc3QuY29tghB3d3cuaGFsZnJvc3QuY29tMEwGA1UdIARFMEMwCAYG Z4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMu bGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHUA4mlLribo 73qkwe6lN9vZWu1dJV8+Q41cFLGYMJhDD56x7QIgL+V6g1CQst9UDXobdkAEnjah KiJWihr/Qn3plzgzjiIAdwApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0 eAAAAWeORhq2AAAEAwBIMEYCIQD1Mf1GtmegyTqIu0S3Q4afNDt0srIFyrtROtn0 jQAV1gIhAJwXIGyMj87kjHtRc/mHJOOCZRSUvoasvWrytCv2dPwXMA0GCSqGSIb3 DQEBCwUAA4IBAQB3sC7jKVGHR8MnAOWnECO/V5Z4oBqbahogwyhOSrbxuutijhyk 8kb3A73Q++Ey150Y+hlNUQStmG9JBGg9pyLG2Yug9p5L13a6VrNaL1VQ1Dq6YgS5 5J8ElsalUgr+9jvTJesdYzfXPdsc8IK67tBXhukqc0/cT3I1QHNwAVru/AKWrkne H4AcadSeLGe5he2X9OV3JJg+gb/vE90UaVmqwUuSGMzluyBXPMuznTa/+7+31vWV Q8aWE32X+E5qHSyeLU808mZHYjvKHvuDnNNu6I0KlNcVJf1s0jOQOjgo7hIP/OR4 OlW6ywk07IupV4w07xykP1/tWBsSCviXECcZ -----END CERTIFICATE----- subject=CN = halfrost.com issuer=C = US, O = Let’s Encrypt, CN = Let’s Encrypt Authority X3 --- No client certificate CA names sent Server Temp Key: X25519, 253 bits --- SSL handshake has read 245 bytes and written 649 bytes Verification error: unable to get local issuer certificate --- Reused, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 256 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated Early data was accepted Verify return code: 20 (unable to get local issuer certificate) ---
从输出中可以看到 Early data was accepted。这个时候转到 wireshark,看抓到的包是怎么样的。

可以看到 Client 在 ClientHello 之后,就立即发送了 Application Data。
在 wireshark 中首选项,把下图中的勾去掉。

配置生效以后,可以看到 Application Data 里面的请求了。

普通的 GET 请求中 header 中带了 Early-Data 的值。这个值就会传给 Server 处理了。
十一. TLS 1.3 的状态机
TLS 1.3 相对 TLS 1.2 握手流程发生了巨大的变化,所以状态机也发生了巨大的变化。下面放 2 张状态流转图,最为总结,对应的也是本篇文章的精华。
START <----+ Send ClientHello | | Recv HelloRetryRequest [K_send = early data] | | v | / WAIT_SH ----+ | | Recv ServerHello | | K_recv = handshake Can | V send | WAIT_EE early | | Recv EncryptedExtensions data | +--------+--------+ | Using | | Using certificate | PSK | v | | WAIT_CERT_CR | | Recv | | Recv CertificateRequest | | Certificate | v | | | WAIT_CERT | | | | Recv Certificate | | v v | | WAIT_CV | | | Recv CertificateVerify | +> WAIT_FINISHED <+ | | Recv Finished \ | [Send EndOfEarlyData] | K_send = handshake | [Send Certificate [+ CertificateVerify]] Can send | Send Finished app data --> | K_send = K_recv = application after here v CONNECTED
这图是 Client 在握手流程上的状态机。如果读者还不清楚中间的某个步骤,可以对照上文中的内容查缺补漏。
START <-----+ Recv ClientHello | | Send HelloRetryRequest v | RECVD_CH ----+ | Select parameters v NEGOTIATED | Send ServerHello | K_send = handshake | Send EncryptedExtensions | [Send CertificateRequest] Can send | [Send Certificate + CertificateVerify] app data | Send Finished after --> | K_send = application here +--------+--------+ No 0-RTT | | 0-RTT | | K_recv = handshake | | K_recv = early data [Skip decrypt errors] | +------> WAIT_EOED -+ | | Recv | | Recv EndOfEarlyData | | early data | | K_recv = handshake | +------------+ | | | +> WAIT_FLIGHT2 <--------+ | +--------+--------+ No auth | | Client auth | | | v | WAIT_CERT | Recv | | Recv Certificate | empty | v | Certificate | WAIT_CV | | | Recv | v | CertificateVerify +-> WAIT_FINISHED <---+ | Recv Finished | K_recv = application v CONNECTED
这图是 Server 在握手流程上的状态机。如果读者还不清楚中间的某个步骤,可以对照上文中的内容查缺补漏。读者能理解透上面这 2 张状态机,TLS 1.3 也就掌握透彻了。
全文完。
- Reference:
- RFC 8466
- TLS1.3 draft-28
GitHub Repo:Halfrost-Field
Follow: halfrost · GitHub
Source: https://halfrost.com/HTTPS_handshake/
本文作者:于德志 (@halfrost),原创授权发布