仕事で TLS 接続がなぜか強制RSTされる調査をするときに、TLS negotiation に関する知識不足で死にました。結局 Java の bug だったということで解決はしたのですが、後学のためにパケットを読めるようにしておきます。
test case
Mac Chrome Version 103.0.5060.53 から https://example.com へのアクセスを wireshark でキャプチャしました。ひとつずつパケットを見ていきます。HTTPSとは言っても最初は暗号化されていなかったりします。そういったところも勉強していきます。(と思いましたが、ClientHelloだけで終わりました)
all packets
今回の全パケットはこちらです。わかりやすいように src/dest IP address は名前にしてあります。
# | time | src | dest | protocol | length | TCP flags | seq | ack | info |
---|---|---|---|---|---|---|---|---|---|
130 | 2.487113 | localhost | example.com | TCP | 78 | ····CE····S· | 0 | 0 | 58364 → 443 [SYN, ECN, CWR] Seq=0 Win=65535 Len=0 MSS=1460 WS=64 TSval=107391323 TSecr=0 SACK_PERM=1 |
133 | 2.641277 | example.com | localhost | TCP | 74 | ·····E·A··S· | 0 | 1 | 443 → 58364 [SYN, ACK, ECN] Seq=0 Ack=1 Win=65535 Len=0 MSS=1382 SACK_PERM=1 TSval=2936265566 TSecr=107391323 WS=512 |
134 | 2.641642 | localhost | example.com | TCP | 66 | ·······A···· | 1 | 1 | 58364 → 443 [ACK] Seq=1 Ack=1 Win=131520 Len=0 TSval=107391476 TSecr=2936265566 |
135 | 2.642373 | localhost | example.com | TLSv1.3 | 583 | ·······AP··· | 1 | 1 | Client Hello |
139 | 2.805043 | example.com | localhost | TCP | 66 | ·······A···· | 1 | 518 | 443 → 58364 [ACK] Seq=1 Ack=518 Win=67072 Len=0 TSval=2936265726 TSecr=107391476 |
140 | 2.805047 | example.com | localhost | TLSv1.3 | 165 | ·······AP··· | 1 | 518 | Hello Retry Request, Change Cipher Spec |
141 | 2.805283 | localhost | example.com | TCP | 66 | ·······A···· | 518 | 100 | 58364 → 443 [ACK] Seq=518 Ack=100 Win=131392 Len=0 TSval=107391638 TSecr=2936265727 |
142 | 2.80656 | localhost | example.com | TLSv1.3 | 409 | ·······AP··· | 518 | 100 | Change Cipher Spec, Client Hello |
145 | 2.967469 | example.com | localhost | TCP | 66 | ·······A···· | 100 | 861 | 443 → 58364 [ACK] Seq=100 Ack=861 Win=68096 Len=0 TSval=2936265895 TSecr=107391639 |
146 | 2.96767 | example.com | localhost | TLSv1.3 | 1436 | ·······A···· | 100 | 861 | Server Hello, Application Data |
147 | 2.972801 | example.com | localhost | TCP | 1436 | ·······AP··· | 1470 | 861 | 443 → 58364 [PSH, ACK] Seq=1470 Ack=861 Win=68096 Len=1370 TSval=2936265897 TSecr=107391639 [TCP segment of a reassembled PDU] |
148 | 2.972935 | example.com | localhost | TLSv1.3 | 1436 | ·······A···· | 2840 | 861 | Application Data, Application Data |
149 | 2.97294 | example.com | localhost | TLSv1.3 | 117 | ·······AP··· | 4210 | 861 | Application Data |
150 | 2.973082 | localhost | example.com | TCP | 66 | ·······A···· | 861 | 2840 | 58364 → 443 [ACK] Seq=861 Ack=2840 Win=128768 Len=0 TSval=107391804 TSecr=2936265897 |
151 | 2.973117 | localhost | example.com | TCP | 66 | ·······A···· | 861 | 4261 | 58364 → 443 [ACK] Seq=861 Ack=4261 Win=127296 Len=0 TSval=107391804 TSecr=2936265897 |
152 | 2.997266 | localhost | example.com | TCP | 66 | ·······A···· | 861 | 4261 | [TCP Window Update] 58364 → 443 [ACK] Seq=861 Ack=4261 Win=131072 Len=0 TSval=107391828 TSecr=2936265897 |
153 | 2.998117 | localhost | example.com | TLSv1.3 | 140 | ·······AP··· | 861 | 4261 | Application Data |
154 | 2.998521 | localhost | example.com | TLSv1.3 | 158 | ·······AP··· | 935 | 4261 | Application Data |
155 | 2.998889 | localhost | example.com | TLSv1.3 | 534 | ·······AP··· | 1027 | 4261 | Application Data |
173 | 3.151537 | example.com | localhost | TCP | 66 | ·······A···· | 4261 | 935 | 443 → 58364 [ACK] Seq=4261 Ack=935 Win=68096 Len=0 TSval=2936266078 TSecr=107391828 |
174 | 3.15154 | example.com | localhost | TLSv1.3 | 321 | ·······AP··· | 4261 | 935 | Application Data |
175 | 3.151542 | example.com | localhost | TLSv1.3 | 321 | ·······AP··· | 4516 | 935 | Application Data |
176 | 3.151543 | example.com | localhost | TLSv1.3 | 162 | ·······AP··· | 4771 | 935 | Application Data, Application Data |
177 | 3.151817 | localhost | example.com | TCP | 66 | ·······A···· | 1495 | 4516 | 58364 → 443 [ACK] Seq=1495 Ack=4516 Win=130816 Len=0 TSval=107391979 TSecr=2936266078 |
178 | 3.151817 | localhost | example.com | TCP | 66 | ·······A···· | 1495 | 4771 | 58364 → 443 [ACK] Seq=1495 Ack=4771 Win=130560 Len=0 TSval=107391979 TSecr=2936266078 |
179 | 3.151817 | localhost | example.com | TCP | 66 | ·······A···· | 1495 | 4867 | 58364 → 443 [ACK] Seq=1495 Ack=4867 Win=130432 Len=0 TSval=107391979 TSecr=2936266078 |
180 | 3.152917 | localhost | example.com | TLSv1.3 | 97 | ·······AP··· | 1495 | 4867 | Application Data |
181 | 3.158304 | example.com | localhost | TLSv1.3 | 97 | ·······AP··· | 4867 | 1027 | Application Data |
182 | 3.158506 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 4898 | 58364 → 443 [ACK] Seq=1526 Ack=4898 Win=131008 Len=0 TSval=107391985 TSecr=2936266080 |
183 | 3.171335 | example.com | localhost | TLSv1.3 | 981 | ·······AP··· | 4898 | 1495 | Application Data, Application Data, Application Data |
184 | 3.171566 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 5813 | 58364 → 443 [ACK] Seq=1526 Ack=5813 Win=130112 Len=0 TSval=107391998 TSecr=2936266087 |
185 | 3.326376 | example.com | localhost | TCP | 66 | ·······A···· | 5813 | 1526 | 443 → 58364 [ACK] Seq=5813 Ack=1526 Win=69120 Len=0 TSval=2936266254 TSecr=107391980 |
1098 | 48.839818 | localhost | example.com | TCP | 54 | ·······A···· | 1525 | 5813 | [TCP Keep-Alive] 58364 → 443 [ACK] Seq=1525 Ack=5813 Win=131072 Len=0 |
1108 | 48.992493 | example.com | localhost | TCP | 66 | ·······A···· | 5813 | 1526 | [TCP Keep-Alive ACK] 443 → 58364 [ACK] Seq=5813 Ack=1526 Win=69120 Len=0 TSval=2936311918 TSecr=107391998 |
1369 | 64.549821 | example.com | localhost | TCP | 66 | ·······A···· | 5812 | 1526 | [TCP Keep-Alive] 443 → 58364 [ACK] Seq=5812 Ack=1526 Win=69120 Len=0 TSval=2936327237 TSecr=107391998 |
1370 | 64.55017 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 5813 | [TCP Keep-Alive ACK] 58364 → 443 [ACK] Seq=1526 Ack=5813 Win=131072 Len=0 TSval=107452954 TSecr=2936311918 |
2417 | 109.887607 | localhost | example.com | TCP | 54 | ·······A···· | 1525 | 5813 | [TCP Keep-Alive] 58364 → 443 [ACK] Seq=1525 Ack=5813 Win=131072 Len=0 |
2418 | 110.041827 | example.com | localhost | TCP | 66 | ·······A···· | 5813 | 1526 | [TCP Keep-Alive ACK] 443 → 58364 [ACK] Seq=5813 Ack=1526 Win=69120 Len=0 TSval=2936372958 TSecr=107452954 |
2790 | 125.889367 | example.com | localhost | TCP | 66 | ·······A···· | 5812 | 1526 | [TCP Keep-Alive] 443 → 58364 [ACK] Seq=5812 Ack=1526 Win=69120 Len=0 TSval=2936388677 TSecr=107452954 |
2791 | 125.889583 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 5813 | [TCP Keep-Alive ACK] 58364 → 443 [ACK] Seq=1526 Ack=5813 Win=131072 Len=0 TSval=107513834 TSecr=2936372958 |
4317 | 171.346262 | localhost | example.com | TCP | 54 | ·······A···· | 1525 | 5813 | [TCP Keep-Alive] 58364 → 443 [ACK] Seq=1525 Ack=5813 Win=131072 Len=0 |
4318 | 171.522983 | example.com | localhost | TCP | 66 | ·······A···· | 5813 | 1526 | [TCP Keep-Alive ACK] 443 → 58364 [ACK] Seq=5813 Ack=1526 Win=69120 Len=0 TSval=2936434446 TSecr=107513834 |
4366 | 183.843569 | example.com | localhost | TLSv1.3 | 123 | ·······AP··· | 5813 | 1526 | Application Data |
4367 | 183.843575 | example.com | localhost | TLSv1.3 | 90 | ·······AP··· | 5870 | 1526 | Application Data |
4368 | 183.843577 | example.com | localhost | TCP | 66 | ·······A···F | 5894 | 1526 | 443 → 58364 [FIN, ACK] Seq=5894 Ack=1526 Win=69120 Len=0 TSval=2936446685 TSecr=107513834 |
4369 | 183.844156 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 5870 | 58364 → 443 [ACK] Seq=1526 Ack=5870 Win=131008 Len=0 TSval=107571319 TSecr=2936446685 |
4370 | 183.844157 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 5894 | 58364 → 443 [ACK] Seq=1526 Ack=5894 Win=130944 Len=0 TSval=107571319 TSecr=2936446685 |
4371 | 183.8442 | localhost | example.com | TCP | 66 | ·······A···· | 1526 | 5895 | 58364 → 443 [ACK] Seq=1526 Ack=5895 Win=130944 Len=0 TSval=107571319 TSecr=2936446685 |
4372 | 183.846191 | localhost | example.com | TCP | 54 | ·······A·R·· | 1526 | 5895 | 58364 → 443 [RST, ACK] Seq=1526 Ack=5895 Win=131008 Len=0 |
各 TLSパケットの後にTCPでackが返ってきてますね。
解読開始
TCP 3-way handshake
冒頭は TCP 通信が3つ入ります。かの有名な 3-way handshakeで syn, syn+ack, ack で TCP接続を確立します。ここは TLS layerではないので本記事ではスキップします。当たり前ですが、localhostから通信が始まっています。
# | src | dest | type | length | info |
---|---|---|---|---|---|
130 | **localhost** | example.com | TCP | 78 | 58364 → 443 [SYN, ECN, CWR] Seq=0 Win=65535 Len=0 MSS=1460 WS=64 TSval=107391323 TSecr=0 SACK_PERM=1 |
133 | example.com | **localhost** | TCP | 74 | 443 → 58364 [SYN, ACK, ECN] Seq=0 Ack=1 Win=65535 Len=0 MSS=1382 SACK_PERM=1 TSval=2936265566 TSecr=107391323 WS=512 |
134 | **localhost** | example.com | TCP | 66 | 58364 → 443 [ACK] Seq=1 Ack=1 Win=131520 Len=0 TSval=107391476 TSecr=2936265566 |
ClientHello
さあ、TLS通信の始まりです。まずはお互いに「どうやって暗号化するか」をネゴシエーションしていきます。ここはまだ平文通信というところがポイントです。最初に client が送るのが ClientHello です。
# | src | dest | type | length | info |
---|---|---|---|---|---|
4 | **localhost** | example.com | TLSv1.3 | 583 | Client Hello |
ClientHello 中身全部
Frame 135: 583 bytes on wire (4664 bits), 583 bytes captured (4664 bits) on interface en0, id 0
Ethernet II, Src: Apple_3f:65:ae (38:f9:d3:3f:65:ae), Dst: da:46:f8:db:b2:0a (da:46:f8:db:b2:0a)
Internet Protocol Version 4, Src: 192.168.61.86, Dst: 93.184.216.34
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x02 (DSCP: CS0, ECN: ECT(0))
Total Length: 569
Identification: 0x0000 (0)
Flags: 0x40, Don't fragment
...0 0000 0000 0000 = Fragment Offset: 0
Time to Live: 64
Protocol: TCP (6)
Header Checksum: 0x04e4 [validation disabled]
[Header checksum status: Unverified]
Source Address: 192.168.61.86
Destination Address: 93.184.216.34
Transmission Control Protocol, Src Port: 58364, Dst Port: 443, Seq: 1, Ack: 1, Len: 517
Source Port: 58364
Destination Port: 443
[Stream index: 6]
[Conversation completeness: Complete, WITH_DATA (63)]
[TCP Segment Len: 517]
Sequence Number: 1 (relative sequence number)
Sequence Number (raw): 4163027035
[Next Sequence Number: 518 (relative sequence number)]
Acknowledgment Number: 1 (relative ack number)
Acknowledgment number (raw): 2279611937
1000 .... = Header Length: 32 bytes (8)
Flags: 0x018 (PSH, ACK)
000. .... .... = Reserved: Not set
...0 .... .... = Nonce: Not set
.... 0... .... = Congestion Window Reduced (CWR): Not set
.... .0.. .... = ECN-Echo: Not set
.... ..0. .... = Urgent: Not set
.... ...1 .... = Acknowledgment: Set
.... .... 1... = Push: Set
.... .... .0.. = Reset: Not set
.... .... ..0. = Syn: Not set
.... .... ...0 = Fin: Not set
[TCP Flags: ·······AP···]
Window: 2055
[Calculated window size: 131520]
[Window size scaling factor: 64]
Checksum: 0x97d4 [unverified]
[Checksum Status: Unverified]
Urgent Pointer: 0
Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
TCP Option - No-Operation (NOP)
Kind: No-Operation (1)
TCP Option - No-Operation (NOP)
Kind: No-Operation (1)
TCP Option - Timestamps: TSval 107391476, TSecr 2936265566
Kind: Time Stamp Option (8)
Length: 10
Timestamp value: 107391476
Timestamp echo reply: 2936265566
[Timestamps]
[Time since first frame in this TCP stream: 0.155260000 seconds]
[Time since previous frame in this TCP stream: 0.000731000 seconds]
[SEQ/ACK analysis]
[iRTT: 0.154529000 seconds]
[Bytes in flight: 517]
[Bytes sent since last PSH flag: 517]
TCP payload (517 bytes)
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 512
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 508
Version: TLS 1.2 (0x0303)
Random: a990959f0793ba1ff5a09d1a5eec06e87706c88ea969457fa59d9ba9dc0f0e32
Session ID Length: 32
Session ID: b1c8e600556b5561ceda33e4c9c0b59221e45d11fb5cc03de4c165861b34f642
Cipher Suites Length: 32
Cipher Suites (16 suites)
Cipher Suite: Reserved (GREASE) (0x6a6a)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 403
Extension: Reserved (GREASE) (len=0)
Type: Reserved (GREASE) (64250)
Length: 0
Data: <MISSING>
Extension: server_name (len=16)
Type: server_name (0)
Length: 16
Server Name Indication extension
Server Name list length: 14
Server Name Type: host_name (0)
Server Name length: 11
Server Name: example.com
Extension: extended_master_secret (len=0)
Type: extended_master_secret (23)
Length: 0
Extension: renegotiation_info (len=1)
Type: renegotiation_info (65281)
Length: 1
Renegotiation Info extension
Renegotiation info extension length: 0
Extension: supported_groups (len=10)
Type: supported_groups (10)
Length: 10
Supported Groups List Length: 8
Supported Groups (4 groups)
Supported Group: Reserved (GREASE) (0xcaca)
Supported Group: x25519 (0x001d)
Supported Group: secp256r1 (0x0017)
Supported Group: secp384r1 (0x0018)
Extension: ec_point_formats (len=2)
Type: ec_point_formats (11)
Length: 2
EC point formats Length: 1
Elliptic curves point formats (1)
EC point format: uncompressed (0)
Extension: session_ticket (len=0)
Type: session_ticket (35)
Length: 0
Data (0 bytes)
Extension: application_layer_protocol_negotiation (len=14)
Type: application_layer_protocol_negotiation (16)
Length: 14
ALPN Extension Length: 12
ALPN Protocol
ALPN string length: 2
ALPN Next Protocol: h2
ALPN string length: 8
ALPN Next Protocol: http/1.1
Extension: status_request (len=5)
Type: status_request (5)
Length: 5
Certificate Status Type: OCSP (1)
Responder ID list Length: 0
Request Extensions Length: 0
Extension: signature_algorithms (len=18)
Type: signature_algorithms (13)
Length: 18
Signature Hash Algorithms Length: 16
Signature Hash Algorithms (8 algorithms)
Extension: signed_certificate_timestamp (len=0)
Type: signed_certificate_timestamp (18)
Length: 0
Extension: key_share (len=43)
Type: key_share (51)
Length: 43
Key Share extension
Client Key Share Length: 41
Key Share Entry: Group: Reserved (GREASE), Key Exchange length: 1
Group: Reserved (GREASE) (51914)
Key Exchange Length: 1
Key Exchange: 00
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: ab83faa28c812c3237c264b8d213968dadf7450bcc0c69d9f37fe6775f1fe015
Extension: psk_key_exchange_modes (len=2)
Type: psk_key_exchange_modes (45)
Length: 2
PSK Key Exchange Modes Length: 1
PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
Extension: supported_versions (len=7)
Type: supported_versions (43)
Length: 7
Supported Versions length: 6
Supported Version: Reserved (GREASE) (0x2a2a)
Supported Version: TLS 1.3 (0x0304)
Supported Version: TLS 1.2 (0x0303)
Extension: compress_certificate (len=3)
Type: compress_certificate (27)
Length: 3
Algorithms Length: 2
Algorithm: brotli (2)
Extension: application_settings (len=5)
Type: application_settings (17513)
Length: 5
ALPS Extension Length: 3
Supported ALPN List
Supported ALPN Length: 2
Supported ALPN: h2
Extension: Reserved (GREASE) (len=1)
Type: Reserved (GREASE) (39578)
Length: 1
Data: 00
Extension: padding (len=204)
Type: padding (21)
Length: 204
Padding Data: 000000000000000000000000000000000000000000000000000000000000000000000000…
[JA3 Fullstring: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21,29-23-24,0]
[JA3: cd08e31494f9531f560d64c695473da9]
TLS Record Layer
まず見ていくのは TLS Record Layerです。
これはたぶん rfc5246 で定まったあたりの内容です。TLSの中でなんの処理をするのか、などの指定がされていると思われます。
version negotiation
TLS1.3の特徴として、下位互換のためにversionが読み取りづらいという点があります。下の画像には3つのversionがあることがわかるでしょうか。
もっとも特徴的なのは Handshake Protocol の version が TLS1.2 なところです。さらに下に行くと、 Extention: supported_versions
の中で、 TLS 1.2 とともに 1.3 を候補に上げています。これでこの client(Chrome)が TLS 1.3 に対応していることをserverに要望しています。
なお、私が持つ TLS 1.3 の全知識をここで披露し終わったことと、そもそもまともにpacket見るのがこれが初めてということをここに宣言させていただきます。
さて、Handshake protocol
の中には server_name
という項目があり、私がリクエストした example.com
が記載されています。server側はこれを見て、SNIなどのチェックを経て ServerHelloに繋げていきます。
重要なのは ここは平文 ということです。HTTPSでも、同一ネットワークにいる Mallory(悪役) にはどのドメインに通信しようとしたかがモロバレってことです。
さぁ、パケットを見ていきます。Hex dumpはこちら。最終的にはこの中が壊れていても読めるようになりたい。
0000 da 46 f8 db b2 0a 38 f9 d3 3f 65 ae 08 00 45 02
0010 02 39 00 00 40 00 40 06 04 e4 c0 a8 3d 56 5d b8
0020 d8 22 e3 fc 01 bb f8 22 c0 5b 87 e0 1e 21 80 18
0030 08 07 97 d4 00 00 01 01 08 0a 06 66 a9 f4 af 03
0040 db 5e 16 03 01 02 00 01 00 01 fc 03 03 a9 90 95
0050 9f 07 93 ba 1f f5 a0 9d 1a 5e ec 06 e8 77 06 c8
0060 8e a9 69 45 7f a5 9d 9b a9 dc 0f 0e 32 20 b1 c8
0070 e6 00 55 6b 55 61 ce da 33 e4 c9 c0 b5 92 21 e4
0080 5d 11 fb 5c c0 3d e4 c1 65 86 1b 34 f6 42 00 20
0090 6a 6a 13 01 13 02 13 03 c0 2b c0 2f c0 2c c0 30
00a0 cc a9 cc a8 c0 13 c0 14 00 9c 00 9d 00 2f 00 35
00b0 01 00 01 93 fa fa 00 00 00 00 00 10 00 0e 00 00
00c0 0b 65 78 61 6d 70 6c 65 2e 63 6f 6d 00 17 00 00
00d0 ff 01 00 01 00 00 0a 00 0a 00 08 ca ca 00 1d 00
00e0 17 00 18 00 0b 00 02 01 00 00 23 00 00 00 10 00
00f0 0e 00 0c 02 68 32 08 68 74 74 70 2f 31 2e 31 00
0100 05 00 05 01 00 00 00 00 00 0d 00 12 00 10 04 03
0110 08 04 04 01 05 03 08 05 05 01 08 06 06 01 00 12
0120 00 00 00 33 00 2b 00 29 ca ca 00 01 00 00 1d 00
0130 20 ab 83 fa a2 8c 81 2c 32 37 c2 64 b8 d2 13 96
0140 8d ad f7 45 0b cc 0c 69 d9 f3 7f e6 77 5f 1f e0
0150 15 00 2d 00 02 01 01 00 2b 00 07 06 2a 2a 03 04
0160 03 03 00 1b 00 03 02 00 02 44 69 00 05 00 03 02
0170 68 32 9a 9a 00 01 00 00 15 00 cc 00 00 00 00 00
0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0240 00 00 00 00 00 00 00
まず、wiresharkはデフォで hex 表示しますが、今回はこれを bitにして見ていきたいと思います。どこで切れてるか意識したいためです。 右クリックで as bits を選びます。
bitになりました。先頭の 3bytes を選んだら、 Ethernet layerのaddress周りが選択されました。こうして互いの関連がわかります。めっちゃ便利。まあ、今回問題になったのはパケット壊れてるからこの関係性がわからず、ここはなんのフィールド??でかなり詰みました。そのためにこの勉强をしています。
で、ここで wiresharkの初歩に気づきました。上のセクションと下のbitセクションは連携してて、bitセクションもethernet frameからtlsまで全部ひとつづきに書かれてます。まず TLS layerを選んでから、下のbitsを見るようにします。
Centent Type
青く光った最初の 1 bypte を選ぶと、 Content Type: Handshake (22)
が光りました。
Content Typeは20, 21, 22, 23 があります。
enum {
change_cipher_spec(20), alert(21), handshake(22),
application_data(23), (255)
} ContentType;
ここでは handshakeが送られてますね。ちなみにハマったパケットは 21 alert(ただし中身はこわれてる) でした。
Version
その次の 2bytes は version. これは TLS1.0の頃の表記なんだろうか。major version で1バイト、マイナーで1 byteです。TLS1.2/1.3ではここは使われないと思います。
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
Length
次の 2bytesは length. 今回は 512 (単位は bytes) です。packet全体は 583 bytes なので、そことは一致しません。これは 583 の方には Ethernet frame, tcp header などの領域が含まれているからです。
そして確認すると、Content Type 22 で送る Handshake protocol で送信するデータ内容が 512 bytes ということでした。TLS record全体のlengthではないんですね。パケットの構造は、こうして「後続する領域がどれくらいあるか」を示すためにlengthが使われます。
Handshake Protocol: Client Hello
さあ、ここから深淵に足を踏み入れます。長い Handshake protocolの始まりです。
Handshake Type
次の 1byte は どんな handshake か、です。
Handshake Typeは様々なものがあります。こちらは TLS 1.2 の例です。
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 にはさらにかなりの追加がされています。TLS 1.3 がいかに複雑になったかが、ここからもよくわかると思います。パット見で明らかに追加されたものに矢印を付けました。
B.3. Handshake Protocol
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;
TLS 1.2 の Handshake typeと見比べると、結構な違いがありますね。
これこそが handshake の全貌です。恐ろしい。
Length
その次の 3 bytes は length = 508 bytes. なんのlengthかはよくわからないけどこの handshake protocol のこれから始まるdata周りの合計でしょう。
Version
次の 2bytes は version. TLS 1.2 は 0x0303 = 771 です。TLS 1.3でもここは 1.2 になります。
Random
次の 32 bytes は random. これはとても大事なんですが説明は割愛。詳しくは Professional SSL/TLS を御覧ください。末尾4 bytes はclientの時刻情報が入るはずなんですが、browser finger printとして使われる恐れがあることから今は形骸化してただのランダムらしいです。なので、値を調べるのはやめときます。
TLS 1.2 の説明ですが、randomは鍵交換で重要な役割を果たします。この乱数に脆弱性があると通信が読み取られることになります。
ServerHello~ClientKeyExchangeの通信過程で、クライアントとサーバの両方にClientHello初期乱数、ServerHello初期乱数、pre_master_secretの3つの乱数が手に入ったことになります。ここからmaster_secret(48バイト)を算出し、そこからさらにkey_blockを計算します。key_blockは理論上無限長の疑似乱数列で、必要な長さだけ計算し、そこからアプリケーションデータの暗号通信に使う共有鍵を取り出します。
https://qiita.com/n-i-e/items/41673fd16d7bd1189a29
Session ID Length
1byteで、32が入っている
Session ID
32bytesありました。TLS 1.2の説明ですが
SessionIDは、これがクライアント値とサーバ値で一致すれば、双方にキャッシュされた計算済みパラメータを利用して、ハンドシェイク手順をすっとばしてChangeCipherSpecまで進めるというものです。
https://qiita.com/n-i-e/items/41673fd16d7bd1189a29
Cipher Suites Length
2 bytes
Cipher suites
Cipher suitesはclientが使いたい暗号化の種類です。先のlength通り 32bytesあり、ひとつの suiteにつき 2bytes使われていました。つまり 32 /2 = 16 の cipher suitesをブラウザは候補としてサーバにお伺いしています。
ここで clientが提案したのは16個。後述する Hello Retry Request では TLS_AES_256_GCM_SHA384
が選ばれ、採用されていました。
Cipher Suites (16 suites)
Cipher Suite: Reserved (GREASE) (0x6a6a)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302) <---------
Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
clientによって対応しているcipher suitesが違うので、ここは可変領域(lengthがついている)んですね。パケットパーサーが、どこまでを可変領域か判断できるようにするために、lengthは必ずdata領域の先頭に配置されます。「Cipher suites lengthが来るはずだ」「32bytesが入ってるぞ」「じゃあ、この後の32bytesは cipher suitesだ」「その次からは、次のComporession Methods lengthが来るはずだ」と感じでパケットが読まれていくわけです。
Compression Method Length
1 byte
そのとおり、次のfieldがcipher suitesの32bytesの直後に来ました。
Compression Method
Compression Method は 1 BYTE で NULL(00000000) です。
これはTLSの圧縮には攻撃がされた過去があり(CRIME脆弱性)、現在では使われないオプションとなっています。似た文字列を圧縮しつづけることで内容を推測できることが理由です。
むちゃくちゃな例
知らない文字列 x (1 byte) があるとする。末尾に3 bytes足して xabc を送ると、4 bytes になるはず。しかし圧縮されて結果が 3 bytes になったら、abcのどれかが x に一致していると推測できる。そんなんで x がわかっちゃったりする。
現在のTLSでは圧縮できないので、データを圧縮したかったら application dataの中でする(そもそも zip ファイルを送るとか)んだと思う。
Extention Length
2bytes. ここから地獄の Extentionが始まります。403 bytesあるそうです。
Extention Padding
Extentionの詳細は飛ばして、最後のpaddingを見ます。全体では 208 bytes ありますが、len=204とあります。これはさらに細かく見れるからです
Extention Padding: Type = 2 bytes
まず 2bytes で extentionの種類が paddingだと宣言されてます。
Extention Typeは想像の通り目がくらむほどあります。paddingは RFC 7685 に書いてあるという優しさが見えます。
enum {
server_name(0), /* RFC 6066 */
max_fragment_length(1), /* RFC 6066 */
status_request(5), /* RFC 6066 */
supported_groups(10), /* RFC 8422, 7919 */
signature_algorithms(13), /* RFC 8446 */
use_srtp(14), /* RFC 5764 */
heartbeat(15), /* RFC 6520 */
application_layer_protocol_negotiation(16), /* RFC 7301 */
signed_certificate_timestamp(18), /* RFC 6962 */
client_certificate_type(19), /* RFC 7250 */
server_certificate_type(20), /* RFC 7250 */
padding(21), /* RFC 7685 */
RESERVED(40), /* Used but never
assigned */
pre_shared_key(41), /* RFC 8446 */
early_data(42), /* RFC 8446 */
supported_versions(43), /* RFC 8446 */
cookie(44), /* RFC 8446 */
psk_key_exchange_modes(45), /* RFC 8446 */
RESERVED(46), /* Used but never
assigned */
certificate_authorities(47), /* RFC 8446 */
oid_filters(48), /* RFC 8446 */
post_handshake_auth(49), /* RFC 8446 */
signature_algorithms_cert(50), /* RFC 8446 */
key_share(51), /* RFC 8446 */
(65535)
} ExtensionType;
Extention Padding: Length = 2 bytes
後続する Padding data の中身が 204 bytes と 2bytes で書かれてます。
Extention Padding: Padding Data = 最大 n bytes
paddingなので全部0です。これはblockの長さを固定化するために使われます。たぶん。
謎の JA3 文字列
extention, TLS record の末尾にある JA3 という2行は謎でした。選択してもbitsが反転しないので、Wiresharkがつけた付加情報なんでしょうか。
Extention: key_share
key_shareは鍵交換のキモらしいです。
TLS1.3では、鍵交換の方法はPSK/DHE/ECDHE/PSKのいずれかとなっており、どれを使うかをClient Helloの key_share パラメーターで選んでいます。
その際DH/ECDHEを使うなら key_share に共有値を詰めて送信します。(共有鍵は互いのkey_shareが完了後に内部で計算して生成。
https://qiita.com/developer-kikikaikai/items/055a344c847379b471f7
Extension: key_share (len=43)
Type: key_share (51)
Length: 43
Key Share extension
Client Key Share Length: 41
Key Share Entry: Group: Reserved (GREASE), Key Exchange length: 1
Group: Reserved (GREASE) (51914)
Key Exchange Length: 1
Key Exchange: 00
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: ab83faa28c812c3237c264b8d213968dadf7450bcc0c69d9f37fe6775f1fe015
恐らく下4行がclientの重要な部分ではと推測します。 Group: x25519 は ECDH用のカーブでした。
Key Exchange の乱数 32 bytes が(EC)DHEでmaster_secretを計算するためのclient側共有値。ちなみに、この後 Client Hello Requestで返ってくるのは共有値なしで、 Selected Group
だけ。
Extension: key_share (len=2)
Type: key_share (51)
Length: 2
Key Share extension
Selected Group: secp256r1 (23)
server側から secp256r1 を使え、と言われているように思います。更にその後、clientはサーバのretry requestを受けてclient helloをもう一度送ります。そのときの key_shareは以下のように、 secp256r1 が指定されていました。
Extension: key_share (len=71)
Type: key_share (51)
Length: 71
Key Share extension
Client Key Share Length: 69
Key Share Entry: Group: secp256r1, Key Exchange length: 65
Group: secp256r1 (23)
Key Exchange Length: 65
Key Exchange: 042c60dacb51f9139a814981fe52c830f990fb739ba98ac993ef19b823cfb5be2ab0371f…
共有値も再生成されています。
そのさらに後、サーバは server hello でやっとサーバの共有地を返してきます。
Extension: key_share (len=69)
Type: key_share (51)
Length: 69
Key Share extension
Key Share Entry: Group: secp256r1, Key Exchange length: 65
Group: secp256r1 (23)
Key Exchange Length: 65
Key Exchange: 0457a797dc210b089cbe0b74106050b96b022f58a8c68862d10823c10cc624946eea4c59…
これで ECDHEの鍵交換ができて、お互いに共有鍵を計算できるようになったんだと思います。
Extension: psk_key_exchange_modes
もう一つ気になったのが psk_key_exchange_modes.
PSKを使う場合はpre_shared_keyに共有鍵を詰めて送信します。
https://qiita.com/developer-kikikaikai/items/055a344c847379b471f7
pre_shared_key は見当たらなかったですが、PSKを (EC)DHEで行うように要望しているように見えました。
Extension: psk_key_exchange_modes (len=2)
Type: psk_key_exchange_modes (45)
Length: 2
PSK Key Exchange Modes Length: 1
PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
Extension: signature_algorithms
ここは動的な長さ。今回は22 bytes. どうやらサーバのSSL/TLS証明書の種類(RSA/ECC)をclientからリクエストできるようです。
以下、自信ない話
知らなかったんですが、webサーバはクライアントのリクエストに応じて、同じcommon nameでも、異なる証明書を返せるそうです。
Apacheサーバでは、RSAとECCで署名した証明書を出し分ける事が出来る。confファイルに、RSAとECCで署名されたサーバ証明書と鍵をそれぞれ記述するだけです
https://asnokaze.hatenablog.com/entry/20141013/1413194597
証明書をCAから買う際、実は我々は RSA証明書を買っています。手元で openssl -genkey
で秘密鍵を作る、それを使ってCSRを作る。CSRをCAに提出すると公開鍵が発行されます。このとき、秘密鍵を openssl ecparams -genkey
すると、 ECCに対応した証明書を申請できます。
RSA証明書のメリットは計算量が少ないこと、ECC証明書のメリットはサイズ(転送量)が小さくなること。まだECC証明書はそんなに広まってないという認識です。
例えば amazon.co.jp や example.com の証明書は RSA/SHA256で、もっともpopularなものだと思います。これは今回 CLientHelloにあるsignature_algorithmsでは rsa_pkcs1_sha256
として指定されているようです。
Extension: signature_algorithms (len=18)
Type: signature_algorithms (13)
Length: 18
Signature Hash Algorithms Length: 16
Signature Hash Algorithms (8 algorithms)
Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
Signature Hash Algorithm Hash: SHA256 (4)
Signature Hash Algorithm Signature: ECDSA (3)
Signature Algorithm: rsa_pss_rsae_sha256 (0x0804)
Signature Hash Algorithm Hash: Unknown (8)
Signature Hash Algorithm Signature: SM2 (4)
Signature Algorithm: rsa_pkcs1_sha256 (0x0401) <-------
Signature Hash Algorithm Hash: SHA256 (4)
Signature Hash Algorithm Signature: RSA (1)
Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
Signature Hash Algorithm Hash: SHA384 (5)
Signature Hash Algorithm Signature: ECDSA (3)
Signature Algorithm: rsa_pss_rsae_sha384 (0x0805)
Signature Hash Algorithm Hash: Unknown (8)
Signature Hash Algorithm Signature: Unknown (5)
Signature Algorithm: rsa_pkcs1_sha384 (0x0501)
Signature Hash Algorithm Hash: SHA384 (5)
Signature Hash Algorithm Signature: RSA (1)
Signature Algorithm: rsa_pss_rsae_sha512 (0x0806)
Signature Hash Algorithm Hash: Unknown (8)
Signature Hash Algorithm Signature: Unknown (6)
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: RSA (1)
実際に example.com が返してきた証明書は、RSA SHA256でした。
現状ECC証明書がほとんど使われてない気がするので、 signature_algorithms はあまり意味がない気がする。
なお、サーバ側は対応した証明書をServerHelloで返すだけなので、サーバがsignature_algorithmsを返答することはない様子でした。
Extension: application_layer_protocol_negotiation (len=14)
application_layer_protocol_negotiation は ALPN と略されます。ALPNを使うと、TLSの上でHTTP/1.1とHTTP/2の両方を使う、というようなことが可能になります。
今回は全体では 18 bytes あります。 h2, http/1.1 という文字が見えますね。
Extension: application_layer_protocol_negotiation: Type: application_layer_protocol_negotiation (16)
2 bytes で application_layer_protocol_negotiation
続いて 2 bytes で lengthがあります。この先は動的な領域です。14bytesあるそうです。
Extension: application_layer_protocol_negotiation: ALPN Extension Length: 12
さらに ALPN Extention Lengthが 2bytes来ました。lengthだけじゃだめなのか。14-2で12bytesになってます。
Extension: application_layer_protocol_negotiation: ALPN Protocol
まず ALPN string length: 2 が 1 byte あります。この2bytesは、続く文字列 h2
が2bytesという意味を表しています。
ALPN Next Protocl で h2 が指定されています。2 bytesです。
http/2 では文字が多すぎたのでしょうか。HTTP2の不思議です。
その次の ALPN string length = 8 bytes は、http/1.1
の 8bytes ですね。
これにより、この client は ALPNとして http/2 と http/1.1 の利用を求めていることがわかります。http/3はありませんね。ないのか。さすがにまだないのか。
application_settingsにも似たようなのがあって、そこでは h2 だけが指定されてました。
Extension: signed_certificate_timestamp (len=0)
CT logに関するrequestをclientからもできるらしいのですが、len=0でした。使ってないみたい。
supported_groups
手元の書籍では elliptic_curves というextentionで楕円曲線の指定ができるらしいのですが見当たりませんでした。その代わり、ここらへんがそれっぽかったです(調べてない)。ec_point_formats で楕円曲線上の点を圧縮できるらしいのですがあまり効果がないらしく、uncompressedになってました。
Extension: supported_groups (len=10)
Type: supported_groups (10)
Length: 10
Supported Groups List Length: 8
Supported Groups (4 groups)
Supported Group: Reserved (GREASE) (0xcaca)
Supported Group: x25519 (0x001d)
Supported Group: secp256r1 (0x0017)
Supported Group: secp384r1 (0x0018)
挫折
ServerHelloにすらたどり着けず数時間が溶けました。続きはいつかやりたいと想います。