TLS 握手的前幾個消息都是明文的,能夠在 Wireshark 里直接看。
但只要出現了“Change Cipher Spec”,后面的數據就都是密文了,看到的也就會是亂碼,不知道究竟是什么東西。
為了更好地分析 TLS 握手過程,可以再對系統和 Wireshark 做一下設置,讓瀏覽器導出握手過程中的秘密信息,這樣 Wireshark 就可以把密文解密,還原出明文。
首先,你需要在 Windows 的設置里新增一個系統變量SSLKEYLOGFILE
,設置瀏覽器日志文件的路徑,比如
D:\http_study\log\sslkey.log
然后在 Wireshark 里設置“Protocols-TLS”
(較早版本的 Wireshark 里是“SSL”),在“(Pre)-Master-Secret log filename”里填上剛才的日志文件
ECDHE握手過程
在 TCP 建立連接之后,瀏覽器會首先發一個“Client Hello”消息,也就是跟服務器“打招呼”。
里面有客戶端的版本號、支持的密碼套件,還有一個隨機數(Client Random),用於后續生成會話密鑰
Transport Layer Security
TLSv1.2 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: deb646e741a099b49bfef08f38871a4ac8d86b1f265ea6dc…
Session ID Length: 32
Session ID: 559393da63fd63f325d1f52088aa649401260c65f1e499fc…
Cipher Suites Length: 34
Cipher Suites (17 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 401
Extension: Reserved (GREASE) (len=0)
Extension: server_name (len=19)
Extension: extended_master_secret (len=0)
Extension: renegotiation_info (len=1)
Extension: supported_groups (len=10)
Extension: ec_point_formats (len=2)
Extension: session_ticket (len=0)
Extension: application_layer_protocol_negotiation (len=14)
Extension: status_request (len=5)
Extension: signature_algorithms (len=20)
Extension: signed_certificate_timestamp (len=0)
Extension: key_share (len=43)
Extension: psk_key_exchange_modes (len=2)
Extension: supported_versions (len=11)
Extension: compress_certificate (len=3)
Extension: Reserved (GREASE) (len=1)
Extension: padding (len=202)
作為“禮尚往來”,服務器收到“Client Hello”后,會返回一個“Server Hello”消息。
把版本號對一下,也給出一個隨機數(Server Random),
然后從客戶端的列表里選一個作為本次通信使用的密碼套件,在這里它選擇了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 112
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 108
Version: TLS 1.2 (0x0303)
Random: 306ba0a553a042f119e52473b67c6e293f3ee61012fff8ff…
Session ID Length: 32
Session ID: 27e4112dd0e1299b7fd853c7787c16308bf03435a620a8fd…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Compression Method: null (0)
Extensions Length: 36
Extension: renegotiation_info (len=1)
Extension: server_name (len=0)
Extension: ec_point_formats (len=4)
Extension: extended_master_secret (len=0)
Extension: application_layer_protocol_negotiation (len=11)
TLSv1.2 Record Layer: Handshake Protocol: Certificate
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 770
Handshake Protocol: Certificate
Handshake Type: Certificate (11)
Length: 766
Certificates Length: 763
Certificates (763 bytes)
Certificate Length: 760
Certificate: 308202f4308201dca003020102020900fa9c5b27a0c1368d… (id-at-commonName=www.chrono.com)
signedCertificate
algorithmIdentifier (sha256WithRSAEncryption)
Padding: 0
encrypted: 6dd90318e47d1b41728f04802a85eedca00a615feed7a67f…
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 300
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 296
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey Length: 32
Pubkey: 780f25b44d2ebb5df7ead5e9b6b32c2d40bb7094f16ac7c6…
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: RSA (1)
Signature Length: 256
Signature: 6778592c6742cbd45ff120ce72d1a442a8c5cdbb65e792d5…
TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 4
Handshake Protocol: Server Hello Done
Handshake Type: Server Hello Done (14)
Length: 0
然后,服務器為了證明自己的身份,就把證書也發給了客戶端(Server Certificate)。
接下來是一個關鍵的操作,因為服務器選擇了 ECDHE 算法,所以它會在證書后發送“Server Key Exchange”消息,里面是橢圓曲線的公鑰(Server Params),用來實現密鑰交換算法,再加上自己的私鑰簽名認證
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 300
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 296
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey Length: 32
Pubkey: 780f25b44d2ebb5df7ead5e9b6b32c2d40bb7094f16ac7c6…
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: RSA (1)
Signature Length: 256
Signature: 6778592c6742cbd45ff120ce72d1a442a8c5cdbb65e792d5…
-
1.指明自己使用的橢圓曲線(一般根據客戶端的拓展中supported_groups中的選擇橢圓曲線算法)
-
2 服務器本地計算一個大數(BIGNUM),乘上曲線的base point,得到一個新的point,這個point就是公鑰,用04+x+y的格式組織起來。04表示unconpressed point,和客戶端的ec_point_formats有關。
-
3 簽名 。和RSA握手不同,RSA情況下, 只要能值正常協商密鑰,那么必然服務器端有證書對應的私鑰,也間接表明了服務器擁有該證書。
DHE/ECDHE不同,證書對應的私鑰並不參與密鑰協商,如果要證明服務器擁有證書,則必然有簽名的操作(就像雙向認證的情況下,客戶端需要發送certificate verify)。
被簽名數據從curve type起,至point的y為止。
對於TLS1.2,簽名算法使用client hello拓展中提供的摘要算法;TLS1.0和TLS1.1,如果本地證書是ECC證書,即若要使用ECDSA簽名,這種摘要算法為SHA1,其他的情況摘要算法為md5+sha1。
計算摘要之后就調用RSA或者ECDSA進行簽名。注意的是,TLS1.2時報文要帶上2字節的“Signature Hash Algorithm”,如上圖高亮部分,這是TLS1.2協議相較於之前協議不同之處之一,但是這2部分不參與簽名計算
這相當於說:“剛才我選的密碼套件有點復雜,所以再給你個算法的參數,和剛才的隨機數一樣有用,別丟了。為了防止別人冒充,我又蓋了個章。
”之后是“Server Hello Done”消息,服務器說:“我的信息就是這些,打招呼完畢。”這樣第一個消息往返就結束了(兩個 TCP 包),結果是客戶端和服務器通過明文共享了三個信息:Client Random、Server Random 和 Server Params。
客戶端這時也拿到了服務器的證書,那這個證書是不是真實有效的呢?
開始走證書鏈逐級驗證,確認證書的真實性,再用證書公鑰驗證簽名,就確認了服務器的身份:“剛才跟我打招呼的不是騙子,可以接着往下走。”然后,客戶端按照密碼套件的要求,也生成一個橢圓曲線的公鑰(Client Params),用“Client Key Exchange”消息發給服務器。
Handshake Protocol: Client Key Exchange
EC Diffie-Hellman Client Params
Pubkey: 8c674d0e08dc27b5eaa…
現在客戶端和服務器手里都拿到了密鑰交換算法的兩個參數(Client Params、Server Params),就用 ECDHE 算法一陣算,算出了一個新的東西,叫“Pre-Master”,其實也是一個隨機數。
至於具體的計算原理和過程,因為太復雜就不細說了,但算法可以保證即使黑客截獲了之前的參數,也是絕對算不出這個隨機數的。
現在客戶端和服務器手里有了三個隨機數:Client Random、Server Random 和 Pre-Master。
用這三個作為原始材料,就可以生成用於加密會話的主密鑰,叫“Master Secret”。而黑客因為拿不到“Pre-Master”,所以也就得不到主密鑰。為什么非得這么麻煩,非要三個隨機數呢?
這就必須說 TLS 的設計者考慮得非常周到了,他們不信任客戶端或服務器偽隨機數的可靠性,為了保證真正的“完全隨機”“不可預測”,把三個不可靠的隨機數混合起來,那么“隨機”的程度就非常高了,足夠讓黑客難以猜測。
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
這里的“PRF”就是偽隨機數函數,它基於密碼套件里的最后一個參數,比如這次的 SHA384,通過摘要算法來再一次強化“Master Secret”的隨機性。
主密鑰有 48 字節,但它也不是最終用於通信的會話密鑰,還會再用 PRF 擴展出更多的密鑰,比如客戶端發送用的會話密鑰(client_write_key)、服務器發送用的會話密鑰(server_write_key)等等,避免只用一個密鑰帶來的安全隱患。
有了主密鑰和派生的會話密鑰,握手就快結束了。客戶端發一個“Change Cipher Spec”,然后再發一個“Finished”消息,把之前所有發送的數據做個摘要,再加密一下,讓服務器做個驗證。
意思就是告訴服務器:“后面都改用對稱算法加密通信了啊,用的就是打招呼時說的 AES,加密對不對還得你測一下。
”服務器也是同樣的操作,發“Change Cipher Spec”和“Finished”消息,雙方都驗證加密解密 OK,握手正式結束,后面就收發被加密的 HTTP 請求和響應了
- HTTPS 協議會先與服務器執行 TCP 握手,然后執行 TLS 握手,才能建立安全連接
- 握手的目標是安全地交換對稱密鑰,需要三個隨機數,第三個隨機數“Pre-Master”必須加密傳輸,絕對不能讓黑客破解;
- “Hello”消息交換隨機數,“Key Exchange”消息交換“Pre-Master”;
- “Change Cipher Spec”之前傳輸的都是明文,之后都是對稱密鑰加密的密文
TLS協議組成
記錄協議
Record Protocol ,規定了TLS收發數據的基本單位。它有點類似TCP里的segment,所有的其它子協議都需要通過記錄協議發出
但是多個記錄數據可以在1個TCP包里一次性發出,也並不需要像TCP那樣返回ACK
警報協議
Alert protocol 職責是向對方發出警報信息,類似http協議里的狀態碼。
比如protocol_version 就死不支持舊版本 bad_certificate就是證書有問題
收到警報后,另一方可以選擇終止連接,也可以選擇繼續
握手協議
(Handshake Protocol)是 TLS 里最復雜的子協議,要比 TCP 的 SYN/ACK 復雜的多,瀏覽器和服務器會在握手過程中協商
- TLS 版本號、
- 隨機數、
- 密碼套件等信息,
然后交換證書和密鑰參數,最終雙方協商得到會話密鑰,用於后續的混合加密系統
變更密碼規范協議
(Change Cipher Spec Protocol),它非常簡單,就是一個“通知”,告訴對方,后續的數據都將使用加密保護。那么反過來,在它之前,數據都是明文的
第一階段:C/S兩端共享Client Random、Server Random 和 Server Params信息
客戶端--->服務器:
客戶端的版本號、支持的密碼套件,還有一個隨機數(Client Random)
服務端--->客戶端:
客戶端的版本號、選擇的客戶端列表的密碼套件如:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384、隨機數隨機數(Server Random)
服務端--->客戶端:
服務端證書(Server Certificate)
服務端--->客戶端:
發送Server Key Exchange類型的請求,攜帶橢圓曲線的公鑰(Server Params)用以實現密鑰交換算法,另附私鑰簽名
服務端--->客戶端:
發送完畢
第二階段:證書驗證
前驗條件:客戶端證書鏈逐級驗證、證書公鑰驗證簽名,服務端身份驗證成功(證書合法)
客戶端--->服務端
發送Client Key Exchange類型的請求,攜帶橢圓曲線的公鑰(Client Params)用以實現秘鑰交換算法
第三階段:主密鑰生成
客戶端、服務端分別使用Client Params、Server Params通過ECDHE算法計算出隨機值pre-master,然后用
Client Random、Server Random 和 Pre-Master三個值作為原材料,用PRF偽隨機數函數(利用密碼套件的摘要算法再次強化結果
值maser secert的隨機性)計算出主密鑰Master Secret,
主密鑰並不是會話秘鑰,還會再用PRF擴展出更多的密鑰,比如客戶端發送用的會話密鑰(client_write_key)、服務器發送用的會話密鑰(server_write_key)
客戶端--->服務端:
客戶端發一個“Change Cipher Spec”,然后再發一個“Finished”消息,把之前所有發送的數據做個摘要,再加密一下,讓服務器做個驗證.
服務端--->客戶端:
服務器也是同樣的操作,發“Change Cipher Spec”和“Finished”消息,雙方都驗證加密解密 OK,握手正式結束.
1.client hello 【client->server】
- client發送 隨機數randomC,tls版本,支持的密碼套件,擴展信息到服務端
2.server hello 【server-client】
- server 發送 隨機數randomS,tls版本確認,選擇的密碼套件,擴展信息到client
3.certificate 【server->client】
4.server key exchange 【server->client】
- EC Diffie-Hellman Server Params
- Curve Type(曲線類型)
- Pubkey
- Signature Algorithm和Signature