TCP和TLS/SSL會話細節
TCP數據段格式說明
TCP建立連接和斷開連接細節
Https如何保證通信安全
一次Https網絡請求通信細節
網絡數據包分析工具wireshark的使用
問題:
SYN、ACK、FIN具體含義是什麽?
TCP建立連接超時的表現?
為什麽需要證書來下發服務端公鑰?
客戶端是如何驗證證書合法性的?
對稱秘鑰是如何協商出來的?
為什麽不直接讓客戶端自己生成一個秘鑰發送給服務端使用?
TLS如何避免重放攻擊?
TCP數據包格式說明
TCP數據段分為首部+數據兩部分。
首部又分為固定首部和可選項首部。
通過對TCP數據包格式的分析,就是了解TCP協議定義的過程。
來源連接端口(16比特位)-辨識發送連接端口
目的連接端口(16比特位)-辨識接收連接端口
無懼傳輸時的亂序或丟包
建立連接時發送和接收方第一次數據段的seq均為隨機生成(TCP序號預測攻擊),之後是上次序列號加1
發送數據時seq為上一次發送的數據長度加1,如果數據長度為0則seq不變
確認號(ack,32比特位)-表示接收方期望下次收到的數據包的序列號的值,也是當前收到的數據的字節長度加1
數據偏移(4比特位)-以4字節為單位計算出的數據段開始地址的偏移值,例1111 -> 15 -> 60字節
保留位(3比特位)-需置為0
標誌符(9比特位)
NS:Nonce Sum 隨機和,
CWR:Congestion Window Reduced 擁塞窗口減少標誌被發送主機設置,用來表明它接收到了設置ECE標誌的TCP包。擁塞窗口是被TCP維護的一個內部變量,用來管理發送窗口大小
URG:為1表示高優先級數據包,緊急指針字段有效
ACK:為1表示確認號字段有效
PSH:為1表示是帶有PUSH標誌的數據,指示接收方應該盡快將這個報文段交給應用層而不用等待緩沖區裝滿
RST:為1表示出現嚴重差錯,可能需要重新創建TCP連接,還可以用於拒絕分發的報文段和拒絕連接請求
SYN:為1表示這是連接請求或是連接接受請求,用於創建連接和使順序號同步
FIN:為1表示發送方沒有數據要傳輸了,要求釋放連接
校驗和(Checksum 16比特位) -對整個TCP報文段,包括TCP首部和TCP數據,計算出來的16位值,這是一個強制字段。校驗方式為:將TCP報文段的頭部和數據部分的和計算出來,再對其求反碼
緊急指針(16比特位)-本報文段中的緊急數據的最後一個字節的序號
選項字段 -最多40字節,每個選項的開始是1字節的kind字段,說明選項的類型。下面具體說明支持的選項字段類型
0:(1字節)選項表結束
1:無操作(1字節)用於選項字段之間的字節邊界對齊和分割不同的選項數據
2:最大報文長度(4字節,Maximum segment size,MSS) -在握手階段告知接收方,發送方支持的最大報文數據段的長度,以太網一般為1460。只能出現在同步報文段中,否則將被忽略。通常將MSS設置為MTU-40字節(Maximum Transmission Unit 最大傳輸單元),這樣攜帶TCP報文段的IP數據報的長度就不會超過MTU,從而避免本機發生IP分片。
3:窗口擴大因子(3字節,wscale)-取值0-14,用來把TCP的窗口的值左移的位數。只能出現在同步報文中,否則將被忽略。這是因為現在的TCP接收數據緩沖區(接收窗口)的長度通常大於65535字節
4:sackOK -發送端支持並同意使用SACK(Selective Acknowledgements,選擇確認)選項
5:SACK實際工作的選項,存放丟包的邊界信息,最多存放4個包的邊界信息。例如123,丟了2,那麽SACK存放的就是2號包的開始和結束的字節序列號
8:時間戳(10字節,TCP Timestamps Option,TSopt)
發送端的時間戳(Timestamp Value field, TSval,4字節)
時間戳回顯應答(Timestamp Echo Reply field,TSecr,4字節)
時間戳的功能有兩個:
用來計算往返時間RTT(Round-Trip Time),發送方在收到確認報文後,可以準確計算出RTT。
防止回繞的序列號,通過時間戳可以判斷出相同序列號的數據報,哪個是最近發送的,哪個是以前發送的。
滑動窗口
簡單一句話就是接收方可以動態控制發送方下次發送的TCP包數據段的大小。
當接收方處理數據較慢時,就可以通過WIN字段,在ACK包中告知發送方:“以後的數據段少發一些,我處理不過來了。”
在極端情況下,接收方連1字節的數據也不能處理了,那麽WIN字段就設置為0,發送方就會停止發送後續的TCP包。
那麽,當接收方緩過氣來,可以處理更多數據時,發送方是怎麽知道的呢?答案是Zero Window Probe(零窗口探針)技術。
發送方會在一定時間間隔內重復發送ZWP包,這時接收方就有機會告知發送方最新的窗口大小。
又有極端情況,接收方一直返回WIN為0,那麽發送方在發送一定次數的ZWP後,就會發送RST包來斷開連接(不同的系統有不同的實現)。
另外一種極端情況,接收方返回的WIN值特別小,相對於TCP的首部來說,發送較少的數據時一種浪費。這個時候接收方就會使用David D Clark’s 方案。接收方直接返回WIN為0,知道接收方有足夠的能力處理新數據時再把WIN打開。
如果是由於發送方發送的數據特別少引起的,那麽發送方就會使用Nagle’s algorithm。將多個小的數據包緩存起來,直到滿足發送條件。
ECE和CWR
當兩個支持ECN的TCP端進行TCP連接時,對於支持ECN的TCP端來說,SYN包的ECE和CWR標誌都被設置了。SYN-ACK只設置ECE標誌。
一個支持ECN的TCP主機在支持ECN的TCP連接上發送設置了IP頭部為10或者01的TCP包。支持ECN的路由器在經歷擁塞時設置IP頭部的ECN域為11。當一個TCP接收端發送針對收到的一個設置ECN位為11的TCP包的響應時,它設置TCP包頭中的ECE,並且在接下來的ACK中也做同樣設置。
當發送主機接收到設置了ECE標誌的ACK時,它就像感知到包丟失一樣,開始減少發送窗口,運行慢啟動過程和擁塞避免算法。在下一個數據包中,發送者設置CWR標誌。在接收到新的設置CWR標誌的包時,接受者停止在接下來的ACK中設置ECE標誌。
超時重傳
建立連接時:
如果發送方在發送一個SYN包後,在超時時間內沒有收到確認包,則發送方會重新發送,稱為超時重發。
默認Linux重試次數為5次,重試時間間隔由1s開始每次翻倍,即1s,2s,4s,8s,16s。如果經過1+2+4+8+16+32=63s後,仍沒有收到確認包,則發送方認為接收方已掉線,會主動斷開當前連接。
數據傳輸時:
為了網絡整體的穩定,需要動態的根據往返時間設置數據包的超時時間。這裏就不展開說具體算法過程了。
RTO(Retransmission Timeout)重傳超時時間
RTT(Round-Trip Time)往返時間
擁塞控制
慢啟動
擁塞避免
快速重傳
快速恢復
三次握手 和 四次揮手
三次握手
這是簡單的三次握手流程示意圖,三次握手意思是需要在發送方和接收方之間傳遞三個數據包。通過設置不同的標識位,來告知對方當前數據包的意圖。
第一次:C發送一個數據包P1給S,並將標識位的SYN置為1,表明“我要和你建立連接”。
第二次:S如果可以接受C的請求,會給C回發一個數據包P2,並將標識位SYN置為1,表明“我同意和你建立連接”。同時將ACK位置為1,表明“確認號ack”字段有效,其值為P1數據包序列號+1。
第三次:C接到P2後,會再次向S發送數據包P3,將ACK為置為1,其值為P2數據包的序列號+1,表明“我知道了你同意了”。
至此,連接就被建立完成了,雙方就可以任意發送數據了。
但是,在三次握手過程中,除了要協商連接的建立,還有其他通訊參數的設置。下面以一個真實請求在三次握手過程中發生的數據交換作說明:
第一次:
第二次:
第三次:
四次揮手
這是TCP連接斷開時四次揮手的示意圖。劃重點:
每一側的連接都單獨的被終止。
主動終止連接的一方在接收到ACK後,不能再發送數據,但可以接收數據,也就是半雙工狀態。
首先終止連接的一方,在給對方響應了ACK後,就會等待2*MSL(Max Segment Lifetime 報文最大生存時間)時間,然後關閉連接。RFC793定義了MSL為2分鐘,Linux設置成了30s。
四次揮手也可以通過三次握手實現,即主機A發出FIN,主機B回復FIN&ACK,主機A回復ACK。
下面是三次握手實現的連接斷開的報文傳輸細節:
第一次:
第二次:
第三次:
Https如何保證通信安全
簡介
Https 超文本傳輸安全協議(Hypertext Transfer Protocol Secure),1994年由網景公司提出,Https經由Http進行通信,但利用SSL/TLS來加密數據,即在Http協議與TCP協議之間添加SSL/TLS層。
安全防護:
身份認證,防止中間人攻擊
消息加密,防止被竊聽
消息校驗,防止被篡改
安全前提:
系統或瀏覽器正確的實現了Https並安裝了正確的證書頒發機構
安全通信流程
對稱加密
算法公開(AES)
一個秘鑰,秘鑰不公開
加解密速度快
非對稱加密
算法公開(RSA)
兩個秘鑰,公鑰公開,私鑰不公開
公鑰加密的數據,私鑰可以解密。私鑰加密的數據,公鑰可以解密
加解密速度慢
非對稱加密算法除了可以直接將隱私數據加密外,還可以實現對非隱私數據的防篡改校驗功能,也就是數字簽名。
Hash/散列/摘要算法
以任意長度的數據為輸入,輸出固定長度的數字“指紋”。
MAC,消息認證碼,是帶秘鑰的Hash算法,即在對數據計算散列值時將秘鑰和數據同時作為輸入,並采用二次散列叠代的方式。
Alice和Bob
在沒有SSL/TLS的世界裏,Alice和Bob的通訊是這樣的。
Alice(i love you)–明文(i love you)–>Bob(i love you)
一些壞人可以在明文的傳輸過程中,對數據進行更改。
Alice(i love you)–>壞人(i hate you)–>Bob(i hate you)
Bob(i still love you )–>壞人(i hate you too)–>Alice(i hate you too)
當兩人見面後,發現對方誤會了自己,就想到這個世界還是有壞人,然後雙方約定將通信內容加密後再發送給對方。約定的加密算法為AES,秘鑰為“Alice/Bob”。
加密後就會出現兩種情況:
Alice(i love you)–加密(123abc)–>壞人(123abc)–>Bob(123abc)–>解密(i love you)
Alice(i love you)–加密(123abc)–>壞人(!!!)–>Bob(!!!)–>解密(???)
單純使用對稱加密會有兩個問題:
壞人雖然不能竊聽內容,但是仍能篡改。
對稱加密的秘鑰只能Alice和Bob兩個人知道,如果想再和更多的人加密通信的話,就無能為力了。
Alice和Bob必須在通信之前就約定好秘鑰(在互聯網世界中辦不到),中途無法更新。
所以,我們要解決這些問題,需要做到:
為了過濾掉被篡改的數據,通信過程需要有內容校驗機制
為了可以和無限多人通信,需要能動態生成和更新秘鑰
Alice和Bob非常聰明,他們想到了非對稱加密算法RSA,Bob拿著私鑰,然後把公鑰給Alice,當Alice想要和Bob通信時,利用手中的公鑰將對稱加密算法的秘鑰加密後發送給Bob,Bob拿著自己的私鑰將對稱加密的秘鑰解密後,雙方就可以繼續用對稱加密算法將數據加密後通信了。當然,其他人也可以拿到Bob的公鑰,與Bob通信。
Alice(我想和你說話,給我公鑰)------->Bob(我想和你說話,給我公鑰)
Bob(給你公鑰(Pubkey)) --------->Alice(拿到公鑰PubKey)
Alice(秘鑰:123)–>公鑰PubKey加密(321)–>Bob(321)–>私鑰解密(123)
Alice(i love you)–>秘鑰123加密(123abc+摘要簽名)---->Bob(123abc+摘要簽名)–>正確性驗證–>秘鑰123解密(i love you)
上述第二個過程是無懈可擊的,但是第一個過程中,如果存在壞人的話,就變成下面的情形
Bob(給你公鑰(PubKey))---->壞人(PubKey換成自己的PubKey1)--------->Alice(拿到公鑰PubKey1)
Alice(秘鑰:123)–>公鑰PubKey1加密(abc)–>壞人(abc),自己的私鑰解密123,再用PubKey加密321–>Bob(321)–>私鑰解密(123)
完犢子了,對稱加密的秘鑰被壞人竊取了,通信數據又相當於裸奔了。其中核心問題就是:
接收方無法確定公鑰的合法性
那如果把公鑰事先告知Alice可以麽?也就是內置在系統中。理論上是沒問題的,但當Alice需要和更多的人通信時,她需要記住很多很多的公鑰,這是不可行的。
所以Alice和Bob需要商量出一套方案,能保證公鑰在網絡上安全的傳輸,如果受到篡改,接收方能感知到。這時,他們想到數字簽名的方式。
Bob(給你公鑰+簽名(摘要的私鑰加密))------>Alice(公鑰+簽名,對簽名解密,並再次計算摘要,然後比對)
上述方式可以做到防篡改麽?可以,但是做不到防替換。中間人可以把簽名連同公鑰全部換成自己的。
接下來就到了數字簽名證書出場的時刻了。
Bob找到了一個非常權威的機構,“人民政府”。
Bob向“人民政府”證明“我是真Bob”,並提供自己的公鑰。
“人民政府”根據Bob的信息和公鑰頒發給Bob一個證書文件.cer,裏面寫了頒發機構的信息、Bob的信息和公鑰、摘要算法以及最重要的頒發機構的簽名。
公鑰下發過程變成了數字證書下發過程。
同時,Alice也是非常相信“人民政府”的,只要是“人民政府”簽名的證書,Alice就認為證書上面的公鑰就是Bob的。
但是,Alice並不能無腦的相信,她需要判斷了兩點:
接收到的證書是否是”人民政府“簽發的。
證書上面的信息是否被篡改。
那麽具體的判斷流程是怎樣的呢?
由於Alice信任”人民政府“這個機構,所以Alice可以內置一份”人民政府“自簽名的證書,上面有”人民政府“生成的公私鑰中的公鑰,私鑰由”人民政府“自己保管。
在下發證書過程中,Alice拿到Bob發過來的由”人民政府“私鑰簽名過得證書cert0。
Alice首先根據cert0上的證書頒發機構信息判斷自己是否信任這個機構頒發的證書,cert0是由”人民政府“簽發的,而Alice是信任”人民政府“的,所以Alice信任cert0。
Alice通過查找內置的”人民政府“的自簽名證書拿到”人民政府“的公鑰
用此公鑰驗證cert0上的信息是否被篡改
問題:Bob的公鑰不是由”人民政府“簽發的,而是由其下屬的”地方政府“簽發的。而Alice只有”人民政府“的自簽名政府,如何判斷證書合法性呢?
答:在證書下發過程中,實際是下發的一個證書鏈,類似於”Bob的證書“–>“地方政府的證書”–>”人民政府的證書“,Alice可以逐級查找,直到根證書。
證書內容
Https證書格式遵循的是X.509標準。X.509是ITU-T標準化部門基於他們之前的ASN.1定義的一套證書標準。
在瀏覽器中隨便下載一個證書,通過以下命令獲取到其文本格式。
openssl x509 -in *.xxx.com.cer -inform der -text -noout >> cer.txt
Certificate: Data: Version: 3 (0x2) Serial Number: 09:3e:8a:aa:5a:f8:14:de:9d:d9:4d:28:2e:97:a8:16 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=GeoTrust RSA CA 2018 //頒發者信息 Validity Not Before: Aug 22 00:00:00 2018 GMT Not After : Nov 12 12:00:00 2020 GMT Subject: C=CN, ST=\xE5\x8C\x97\xE4\xBA\xAC, L=\xE5\x8C\x97\xE4\xBA\xAC, O=Beijing Qfpay Technology Co., ltd., CN=*.qfpay.com //公鑰主題信息,公鑰所有者的信息 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:e6:c0:8b:f0:12:1e:f0:92:9c:17:7d:7e:d5:69: 47:fc:dd:0b:51:5a:0e:79:df:b1:0e:b4:d6:7d:d0: 5a:bc:f9:93:f6:3c:e3:40:a8:66:9f:d0:ae:3c:e1: f8:9a:55:a2:84:0e:9c:1d:65:f9:d2:63:51:48:b2: 88:a5:09:a6:be:92:80:f8:3b:eb:b8:78:1b:35:58: 47:ac:eb:47:cd:3d:7f:36:74:30:7a:01:86:48:96: b3:7b:14:82:b6:63:0b:b6:43:20:98:3f:07:9d:1a: 56:76:25:cf:cd:d5:49:fd:6e:dc:86:f0:7f:15:f3: 7d:58:98:75:a5:7f:f9:ab:b2:c4:ec:fc:30:bd:75: 27:b3:0e:72:3d:44:d1:04:42:52:65:9b:3e:53:9b: a5:c2:eb:ac:c5:01:b6:1d:0d:2f:75:79:7d:98:d4: 2b:b6:c0:28:ea:c7:dc:14:04:b6:4d:a3:dc:01:2c: f0:14:13:b9:d2:29:31:00:37:af:17:d6:82:a6:f9: 57:9e:4c:2f:27:27:08:50:16:e3:ca:fa:58:32:c7: f5:04:43:b4:5d:0e:97:81:e9:c3:01:36:f9:b7:c8: 14:ec:98:27:e9:31:86:ab:f5:c4:ff:50:aa:c4:df: cc:6e:7d:1c:5a:fa:b8:47:c9:fa:78:b4:de:6d:15: 8d:27 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: //授權秘鑰標識符 keyid:90:58:FF:B0:9C:75:A8:51:54:77:B1:ED:F2:A3:43:16:38:9E:6C:C5 X509v3 Subject Key Identifier: //主題秘鑰標識符 EC:99:74:D5:FF:C6:1B:4F:FB:39:88:8C:E2:C1:7B:8D:90:59:AB:1F X509v3 Subject Alternative Name: DNS:*.xxx.com, DNS:xxx.com X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 CRL Distribution Points: Full Name: URI:http://cdp1.digicert.com/GeoTrustRSACA2018.crl X509v3 Certificate Policies: Policy: 2.16.840.1.114412.1.1 CPS: https://www.digicert.com/CPS Policy: 2.23.140.1.2.2 Authority Information Access: OCSP - URI:http://ocsp1.digicert.com CA Issuers - URI:http://cacerts.geotrust.com/GeoTrustRSACA2018.crt Signature Algorithm: sha256WithRSAEncryption 07:8d:b9:34:62:e8:4b:83:00:af:ab:38:d4:b1:24:12:a4:37: 5e:8f:e7:ff:d9:96:48:ae:72:6f:d2:0b:41:6a:55:92:2a:06: 39:86:a9:78:18:cd:0d:5f:33:fa:22:81:50:b7:67:2f:dc:a1: b4:ee:0f:6c:f8:73:87:0d:65:e7:19:9a:55:07:49:a4:2d:09: 11:8b:5f:1c:c1:46:ce:94:22:fa:b0:1b:88:f0:f0:6f:63:11: e4:56:f4:51:3c:12:90:db:44:63:8b:fd:17:d2:e5:7a:66:5e: f5:d8:90:70:5c:d6:c2:74:d9:74:b3:75:ce:83:e3:db:57:bb: b6:3a:81:e7:ca:7a:48:82:6c:0b:01:a8:ed:a2:8e:d0:b0:ed: 25:15:a2:2a:7f:6f:a5:6d:da:5a:ac:91:f4:dc:23:d8:9f:9d: d3:0a:f4:c7:8f:b0:c2:18:54:97:f5:00:30:36:65:e1:aa:25: 9c:f1:b8:77:d6:7d:33:79:39:0e:41:86:1d:79:47:0e:34:cc: fd:e8:63:83:9d:f5:86:d6:e2:0c:fa:58:d5:d2:81:c1:92:da: e7:41:45:bc:a0:91:d5:40:6e:c8:22:76:69:e4:67:9a:d4:03: ca:8d:28:d5:ca:98:09:e0:d6:dd:ae:c2:6f:08:82:1b:89:79: 14:d6:ca:b7
SSL/TLS握手細節
SSL/TLS報文格式
SSL協議屬於分層協議,一個SSL報文可以包含多個記錄層,每個記錄層分為兩部分:頭部 + 協議數據。
Content Type:協議類型
Version:TLS版本
Length:報文長度
協議數據,不同的協議包含數據字段不同
報文支持的協議類型
Handshake Protocol
握手協議是最主要的協議,負責協商會話的安全屬性。按照不同的功能區分為不同握手類型。
1 enum { 2 hello_request(0), client_hello(1), server_hello(2), 3 certificate(11), server_key_exchange (12), 4 certificate_request(13), server_hello_done(14), 5 certificate_verify(15), client_key_exchange(16), 6 finished(20), (255) 7 } HandshakeType; 8 9 struct { 10 HandshakeType msg_type; /* handshake type */ 11 uint24 length; /* bytes in message */ 12 select (HandshakeType) { 13 case hello_request: HelloRequest; 14 case client_hello: ClientHello; 15 case server_hello: ServerHello; 16 case certificate: Certificate; 17 case server_key_exchange: ServerKeyExchange; 18 case certificate_request: CertificateRequest; 19 case server_hello_done: ServerHelloDone; 20 case certificate_verify: CertificateVerify; 21 case client_key_exchange: ClientKeyExchange; 22 case finished: Finished; 23 } body; 24 } Handshake;
每種握手協議類型,也都包含一些通用字段,如
HandshakeType
Version
Length
下面具體說明每種握手類型的作用:
Hello Request
”你好,請求“,該類型的握手協議作為一個簡單的通知,由服務端發送,告知客戶端重新開始協商過程。作為響應,客戶端應該在方便時發送ClientHello消息。
如果客戶端不希望重新協商會話,可以選擇忽略此消息,或者返回一個no_renegotitation消息。
如果服務端發送HelloRequest後沒有收到ClientHello作為響應,它可能會通過致命警報關閉連接。
Client Hello
”你好,我是客戶端“,該類型的消息用於首次連接服務器時,或響應服務端發送的HelloRequest,或主動發送,以便重新協商連接中的安全參數。
Random 由安全隨機數生成器生成的28字節隨機數,用於對稱秘鑰計算
Session ID 會話標識,可變長,用於會話恢復時重用其安全參數
Cipher Suites 加密算法套件,客戶端提供支持的密碼算法套件列表,按照客戶端的偏好順序排列,一般由四個算法組成,格式為秘鑰交換算法+身份認證算法+批量加密算法+摘要算法
例:Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
ECDHE 秘鑰交換算法,由於對稱秘鑰並不是客戶端直接生成發送給服務端的,而是雙方又各自生成了一對"公私鑰",然後互相交換“公鑰”,這裏生成“公私鑰”的算法就是ECDHE
ECDSA 身份認證算法,用於加密握手消息,確認未被篡改
AES_256_GCM 批量加密算法,用於應用數據加密,它還包括秘鑰大小及顯示和隱式初始化向量的長度
SHA384 散列算法,用於計算主秘鑰和創建會話消息摘要
Compression Methods 壓縮算法列表
這四個屬於固定字段,如果還需要協商其他內容,可以通過下面的擴展字段。
renegotiation_info 重新協商信息,
signature_algorithms 簽名算法,列出客戶端支持的哈希和簽名算法,用於後續消息簽名時的參考。
server_name 服務器域名,用於解決一臺機器部署多個不同域名的站點,而引起證書沖突的情況。
aplication_layer_negotiation 用來確定後續的應用層協議,例http/1.1 spdy h2
…
Server Hello
”你好,我是服務端“,該類型的消息用於響應ClientHello消息。如果服務器找到一組可以接受的算法套件時,則回復此消息,否則響應握手失敗的警報。
Random 由服務端獨立生成的隨機數,28字節,用於對稱秘鑰的計算。
Session ID 當前的會話標識,如果ClientHello的參數SessionID不為空,則服務器將在其會話告訴緩存中查找匹配項,找到並且願意是定指定的會話狀態建立連接,則服務器使用與客戶端提供的SessionID作為響應,然後恢復會話並進入Finished消息流程。否則將重新生成該值以標識新會話。如果SessionID為空表明服務端不緩存此會話,無法恢復。
Cipher Suite 服務端從ClientHello.cipher_suites列表中選擇的單個密碼套件。如果是恢復會話,此字段為正在恢復的會話狀態的值。
例:Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
ECDHE 橢圓曲線離散對數算法
RSA 非對稱加密算法
AES_256_GCM 高級加密算法,對稱加密,32字節的秘鑰 GCM為加密模式
SHA384 安全散列算法
Compress Method 服務端從ClientHello.CompressionMethods列表中選擇的壓縮算法
Server Hello消息也可以包含擴展,但是這些擴展必須在ClientHello中有相同的擴展類型,服務端不能擅自添加。如果客戶端在ServerHello中收到它未在ClientHello請求中關聯的擴展類型,客戶端必須使用unsupported_extension致命警告來終止握手。
Certificate
”服務器證書“,只要商定的秘鑰交換方法使用證書進行身份驗證,則服務端必須發送該證書消息。該消息緊跟在Server Hello消息後,用於向客戶端下發證書。
什麽時候服務端不需要下發證書呢?就是在秘鑰協商過程中使用DH_anon(Diffie-Hellman ANON)匿名Diffie-Hellman算法,即不配合其他身份驗證算法,而單獨只讀使用DH算法,這樣無法保證數據被”篡改“。
Certificates 證書鏈,服務端的證書必須放在第一位,以後的證書必須直接證明其前面的證書。
證書限制
證書類型必須是X.509v3,除非另行協商。
證書必須適用於協商密碼套件的秘鑰交換算法和任何協商擴展。
如果客戶端提供了”signature_algorithms“擴展,那麽服務端提供的所有證書必須由該擴展中出現的散列/簽名算法進行簽名。
Server Key Exchange
”服務器秘鑰交換“,用於發送服務端的預主秘鑰。此消息在Certificates消息後立即發送。
當且僅當與秘鑰交換算法相關聯的證書類型不能為客戶端提供足夠的信息來交換預主秘鑰時,才會發送服務器秘鑰交換信息。
該消息主要描述了使用協商的秘鑰交換算法後,服務端計算出來的公開秘鑰。
本示例中使用的是ECDHE算法。其原理簡單理解如下所示:
服務端生成隨機整數a,作為私鑰
計算出A = f1(a),其中a為私鑰,A為公鑰,並將A發送給客戶端
客戶端重復相同的步驟,先生成隨機整數b,計算 B = f1(b),並將B發送給服務端
客戶端持有A和b,服務端持有a和B
服務端和客戶端計算出相同的預主秘鑰 K = f2(a, B) = f2(b, A)
之後再根據預主秘鑰和兩個隨機數計算出主秘鑰
上述計算過程由ECDHE提供
服務端為了證明此消息是真實可靠的,需要用自己證書私鑰和ClientHello提供的擴展signature_algorithms裏選擇合適摘要和簽名算法對參數進行簽名。
Certificate Request 可選
”要求證書“,如果服務端需要客戶端提供證書以驗證客戶端身份,則使用此消息。消息格式與服務端下發給客戶端的證書消息格式相同。
Server Hello Done
“服務器Hello完成”,表示服務端已經把支持秘鑰交換的消息發送完成。發送此消息後,服務端等待客戶端響應。客戶端應該檢查服務器是否提供了有效的證書(如果需要),並檢查服務端Hello消息參數是否可接受。
Client Certificate
“客戶端證書”,用於將客戶端證書發送給服務端。
這是客戶端在收到ServerHelloDone消息後可以發送的第一條消息。僅當服務器請求證書時才會發送此消息。如果沒有合適的證書,客戶端必須發送不包含證書的證書消息。也就是說,certificate_list結構的長度為零。如果客戶端沒有發送任何證書,服務器可以自行決定是否在沒有客戶端身份驗證的情況下繼續握手,或者使用致命的handshake_failure警報進行響應。此外,如果證書鏈的某些方面是不可接受的(例如,它沒有由已知的,可信的CA簽名),服務器可以自行決定是繼續握手(考慮客戶端未經身份驗證)還是發送致命警報。
Client Key Exchange
“客戶端秘鑰交換”,用於發送客戶端計算的預主秘鑰。該消息是在客戶端收到ServerHelloDone消息後發送的第一條消息。或者是跟在Client Certificate消息後發送。
這個消息內容比較簡單,就是用服務端證書公鑰加密的預主秘鑰。
Certificate Verify
“證書驗證”,此消息用於客戶端證書的顯示驗證。發送這個消息的前提有兩個:
服務端請求了客戶端證書
客戶端發送了非0長的證書
此時,客戶端需要證明自己擁有該證書,需要用自己的私鑰簽名一段數據發送給服務端做驗證。
簽名的數據為:此消息之前的所有接收和發送過的握手消息。
Finished
“協商完成”,此消息是第一個使用上述握手過程中協商的算法和秘鑰來加密的消息。接收方必須驗證內容是否正確。
該消息在Change Cipher Spec消息之後被發送,以驗證密鑰交換和身份驗證過程是否成功。
發送的數據為:PRF(master_secret, finished_label, Hash(handshake_messages))
finished_label為“client finished”/“server finished”
handshake_messages包含除去Hello Request消息之外的之前所有發送和接收的握手消息。
Change Cipher Spec Protocol
更改密碼規範協議,該協議在TSL1.3被移除。
Change Cipher Spec
“變更秘鑰規範”,用於通知接收方後續的通訊數據將在新協商的秘鑰規範保護下交換。
此消息在Finished消息之前被發送。
客戶端在交換預主秘鑰後,就立即發送了“Change Cipher Spec”消息。
之後,服務端也發送一個“Change Cipher Spec”消息。
Application Data Protocol
應用數據協議
如果握手過程結束一切正常,Alice和Bob就用這條加密信道放心的發送數據了,這些數據都是被加密過的。
http-over-tls
Alert Protocol
警報消息傳達消息的嚴重性(警告或致命)以及警報的描述。 具有致命級別的警報消息導致連接立即終止。在這種情況下,對應於會話的其他連接可以繼續,但是會話標識符必須無效,防止失敗的會話被用於建立新連接。與其他消息一樣,警報消息按當前連接狀態的指定進行加密和壓縮。
警報協議包含很多異常場景,如證書過期、沒有符合密碼套件、未知的ca等,這裏就不一一列出了。
TLS握手簡化流程圖
Client Server
ClientHello ----------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<----------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished ----------->
[ChangeCipherSpec]
<----------- Finished
Application Data <-----------> ApplicationData
中括號號標識的協議不屬於握手協議
星號標識的不是必須的握手流程
至此TLS握手過程說完了,但貌似沒有說到**對稱秘鑰(會話秘鑰)**是如何被計算出來的?
我們知道,通過前面的握手流程,客戶端和服務端都知道了以下信息:
預主秘鑰(計算出來的)
客戶端隨機數
服務端隨機數
通過這三個參數,客戶端和服務端就可以分別計算出主秘鑰:
master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random)
master_secret 長度為48個字節。Master secret是有系列的hash值組成的
PRF (pseudo random function)偽隨機函數,也就是選擇的加密套件中的第四個算法SHA384。計算完主秘鑰,就應當把預主秘鑰從內存中刪除。
PRF算法需要一個“秘鑰”、一個“種子”和一個“文本標識”作為輸入,然後產生一個不定長的輸出。
其中的“秘鑰”就是預主秘鑰,“種子”就是兩個隨機數的和,“文本標識”就是 master secret。
因為客戶端和服務端的輸入參數都是一樣的,所以計算出來的主秘鑰也是一致的。
主秘鑰是用來做對稱加密的秘鑰的麽?
不是,需要由主秘鑰再次計算,主秘鑰在創建會話秘鑰時作為一個熵來源。最終計算出來的結果如下:
客戶端寫入MAC密鑰
服務器寫入MAC密鑰
客戶端寫入加密密鑰
服務器寫入加密密鑰
客戶端寫入IV
服務端寫入IV
實際上是生成了兩個會話秘鑰:
MAC(Message Authentication Code),是一個數字簽名,用來驗證數據的完整性,可以檢測到數據是否被串改。
當客戶端向服務端發送消息時,使用“客戶端寫入MAC秘鑰”生成消息摘要附加在消息結尾,使用“客戶端寫入秘鑰”加密;服務端接收到消息後,使用“客戶端寫入秘鑰”解密,使用“客戶端寫入MAC秘鑰”做消息摘要並對比兩次摘要結果。
反過來則是,服務端向客戶端發送消息時,使用“服務端寫入MAC秘鑰”生成消息摘要附加在消息結尾,使用“服務端寫入秘鑰”加密;客戶端接收到消息後,使用“服務端寫入秘鑰”解密,使用“服務端寫入MAC秘鑰”做消息摘要並對比兩次摘要結果。
如果需要在TLS層壓縮數據,則在加密之前先壓縮。
一些AEAD(認證加密)秘鑰套件可能額外需要一個客戶端寫入向量和服務端寫入向量
那麽這些值是怎麽計算出來的呢?
key_block = PRF(master_secret,“key expansion”,server_random + client_random); = P_hash(master_secret, “key expansion"+server_random + client_random) P_hash定義為: P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + HMAC_hash(secret, A(2) + seed) + HMAC_hash(secret, A(3) + seed) + ... A(0) = seed; A(i) = HMAC_hash(secret, A(i-1)); ... HMAC: 基於消息認證碼的哈希算法,也就是對消息進行哈希運算時添加一個秘鑰。
以TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384為例,AES256會話加密算法需要32字節的會話秘鑰,而我們需要2個會話秘鑰(2 * 32)和2個MAC秘鑰(2 * 48),就是2 * 32 + 2 * 48= 160字節。而摘要算法SHA384每次的運算輸出為48字節,為了能滿足160個字節,至少需要計算4次,拼接後得到172字節的輸出。
然後按照上述順序:
0~47字節為“客戶端寫入MAC秘鑰”
48~95字節為“服務器寫入MAC秘鑰”
96~127字節為“客戶端寫入加密秘鑰”
128~159字節為“服務端寫入加密秘鑰”
剩下的12字節則舍棄。
總結:
握手過程中,客戶端和服務端交換了哪些東西?
兩個隨機數
DH算法的公鑰
證書
握手過程中,哪些消息是經過加密的?加密的目的是什麽?
Server/Client Key Exchange 使用服務器私鑰加密,防止中間人篡改
Finished 使用主秘鑰加密並簽名,驗證主秘鑰是否正確生成和握手消息是否被篡改
如果服務端的私鑰泄漏,壞人能否解密會話數據?
RSA 可以
DH 不可以
PSK 不可以
參考:TLS握手協議分析與理解——某HTTPS請求流量包分析
解密應用數據方式:
啟動Chrome並制定SSL的日誌文件
sudo /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ssl-key-log-file=/Users/joye/Downloads/sslkeylog.log
在WireShark中Preferences->Protocols->SSL-(Pre)-Master-Secret log filename設置日誌文件
TCP和TLS/SSL會話細節