Https理論
在說HTTPS之前先說說什么是HTTP,HTTP就是我們平時瀏覽網頁時候使用的一種協議。HTTP協議傳輸的數據都是未加密的,也就是明文的,因此使用HTTP協議傳輸隱私信息非常不安全。為了保證這些隱私數據能加密傳輸,于是網景公司設計了SSL(Secure Sockets Layer)協議用于對HTTP協議傳輸的數據進行加密,從而就誕生了HTTPS。SSL目前的版本是3.0,被IETF(Internet Engineering Task Force)定義在RFC 6101中,之后IETF對SSL 3.0進行了升級,于是出現了TLS(Transport Layer Security) 1.0,定義在RFC 2246。實際上我們現在的HTTPS都是用的TLS協議,但是由于SSL出現的時間比較早,并且依舊被現在瀏覽器所支持,因此SSL依然是HTTPS的代名詞,但無論是TLS還是SSL都是上個世紀的事情,SSL最后一個版本是3.0,今后TLS將會繼承SSL優良血統繼續為我們進行加密服務。目前TLS的版本是1.2。
Https的工作原理
HTTPS在傳輸數據之前需要客戶端(瀏覽器)與服務端(網站)之間進行一次握手,在握手過程中將確立雙方加密傳輸數據的密碼信息。TLS/SSL協議不僅僅是一套加密傳輸的協議,更是一件經過藝術家精心設計的藝術品,TLS/SSL中使用了非對稱加密,對稱加密以及HASH算法
TCP、Https、SSL/TLS、https的關系
上圖截自維基百科,對協議進行了明確的劃分。
TCP
傳輸控制協議,屬于傳輸層協議,提供可靠數據傳輸。它為http等應用層協議提供服務。Http
超文本傳輸協議,屬于應用層協議。依賴于TCP協議。SSL/TLS
安全傳輸層協議,用于在兩個通信應用程序之間提供保密性和數據完整性。位于某個可靠的傳輸協議(例如 TCP)上面,屬于應用層協議。Https
在Http和TCP中間加入了SSL/TLS,保證數據傳輸的安全性
Https抓包演示
上面是我使用Avanced REST client請求我的https接口,整個請求是沒問題的,后面我們就會抓下這個請求的包進行分析。
為了方便演示整個流程,我使用了自己的云服務器和已備案的域名,證書直接在阿里云上申請的免費證書。
環境配置
這里web服務器我們使用Nginx。
首先將阿里下發的證書放到nginx的conf下(新建一個cert文件夾)
配置nginx
當然我們的后臺服務得啟動起來。端口9000。這里我用的Springboot。
@ApiOperation(value = "測試POST", notes = "測試POST")
@RequestMapping(value = "post", method = RequestMethod.POST)
public Result<TestEntity> testPost(@RequestBody TestBody testBody) throws Exception {
Result<TestEntity> result = new Result<TestEntity>();
TestEntity testEntity = new TestEntity();
testEntity.setId(2);
testEntity.setName(testBody.toString());
result.setData(testEntity);
return result;
}
Https握手
我們將前面發起的請求進行抓包,這里使用wireshark。
這是一個完整的請求抓包。這樣看,不是很清晰,我來畫個圖來表達下這個過程發生了什么。
三次握手和四次揮手我們就不說了,這個才講TCP的時候有詳細介紹。
我們一步一步的看
第一步 Client Hello
先看一下抓出來包的內容,我們直接看到SSL層
這一步干啥了呢?
- TLS的版本
- 隨機數:這個是用來生成最后加密密鑰的影響因子之一,包含兩部分:時間戳(4-Bytes)和隨機數(28-Bytes)
- session-id:用來表明一次會話,第一次建立沒有。如果以前建立過,可以直接帶過去。后面的擴展內容會詳細講到。
- 加密算法套裝列表:客戶端支持的加密-簽名算法的列表,讓服務器去選擇。
- 壓縮算法:似乎一般都不用
- 擴展字段:比如密碼交換算法的參數、請求主機的名字等等
這里要注意一個隨機數,很重要。我們先記為Random1。
第二步 Server Hello
- 據客戶端支持的SSL/TLS協議版本,和自己的比較確定使用的SSL/TLS協議版本
- 確定加密套件,壓縮算法
- 產生了一個隨機數Random2。注意,至此客戶端和服務端都擁有了兩個隨機數(Random1+ Random2),這兩個隨機數會在后續生成對稱秘鑰時用到
第三步 Server => Client
這次傳輸包含三部分內容
- Certificate
- Server Key Exchange
- ServerHello Done
這里也是一個優化,三個部分一起發送。我們一個個分析
Certificate
這里主要就是把證書發送給Client。圖中可以看到我的證書和證書發放機構。客戶端拿到證書后就可以進行驗證,同時獲取到公鑰,用于后面Random3的加密。
證書一般采用X.509標準。
Server Key Exchange
這個消息是用來發送密鑰交換算法相關參數和數據的。這里要提前提一下,就是根據密鑰交換算法的不同,傳遞的參數也是不同的。
常用的密鑰交換算法:RSA、DH(Diffie-Hellman)、ECDH(Ellipticcurve Diffie–Hellman)。
這里看到使用的ECDH。
Server Hello Done
這個就是Server來表示自己說完了。類似電影里別人拿對講機說完話最后會有一個“完畢!”。
第四步 Client => Server
這次傳輸也包含三部分內容,也是做了一個優化
- Client Key Exchange
- Change Cipher Spec
- Encrypted Handshake Message
我們依次解讀
Client Key Exchange
這個也是交換秘鑰參數。
這里客戶端會再生成一個隨機數Random3。然后使用服務端傳來的公鑰進行加密得到密文PreMaster Key。服務端收到這個值后,使用私鑰進行解密,得到Random3。這樣客戶端和服務端就都擁有了Random1、Random2和Random3。這樣兩邊的秘鑰就協商好了。后面數據傳輸就可以用協商好的秘鑰進行加密和解密。
Change Cipher Spec
編碼改變通知。這一步是客戶端通知服務端后面再發送的消息都會使用前面協商出來的秘鑰加密了,是一條事件消息。
Encrypted Handshake Message
這一步對應的是 Client Finish 消息,客戶端將前面的握手消息生成摘要再用協商好的秘鑰加密,這是客戶端發出的第一條加密消息。服務端接收后會用秘鑰解密,能解出來說明前面協商出來的秘鑰是一致的。
第五步 Server => Client
包括三部分
- New Session Ticket
- Change Cipher Spec
- Encrypted Handshake Message
New Session Ticket
包含了一個加密通信所需要的信息,這些數據采用一個只有服務器知道的密鑰進行加密。目標是消除服務器需要維護每個客戶端的會話狀態緩存的要求。這部分內容在后面的擴展部分會講到
Change Cipher Spec
編碼改變通知。這一步是服務端通知客戶端后面再發送的消息都會使用加密,也是一條事件消息。
Encrypted Handshake Message
這一步對應的是 Server Finish 消息,服務端也會將握手過程的消息生成摘要再用秘鑰加密,這是服務端發出的第一條加密消息。客戶端接收后會用秘鑰解密,能解出來說明協商的秘鑰是一致的。
到這里雙方SSL/TLS握手完成。
Https數據傳輸
接下來就真正的到了接口請求的階段。TLS的Content-Type為Application Data。 傳輸的內容也是加密的。
常見問題:
這里對整個過程中出現的常見問題做一個匯總
第四步出現多個Hello Request消息
我們看到上面最后出現了兩個Hello Request,估計很多人用wireshark打開就是這樣的。而Hello Request消息是個啥東西呢?我們看下權威文檔:
這寫的很清楚嘛,這是消息應該是Server可能隨時發給Client的。到我們這來怎么變成Client發給Server了,而且和消息的解析牛頭不對馬腳。
我們再重溫下我們的流程
客戶端發送了Change Cipher Spec后,后面的內容就是加密的,而加密后的內容wireshark無法解析識別。而這些二進制數據就別誤解析為Hello Request。我們看下Hello Request的類型枚舉值:
hello_request(0)
所以我覺得應該是wireshake誤認為是hello request消息
既然知道問題,那怎么解決呢?
這里需要我們把私鑰添加到wireshark中:
【編輯】-》【首選項】-》【SSL】-》【Edit】
然后將私鑰添加進來
這樣就搞定了。
拓展內容
這部分對Https做一個深入的了解
TLS會話恢復
完整的TLS握手需要額外延遲和計算,為所有需要安全通信的應用帶來了嚴重的性能損耗。為了幫助減少一些性能損耗,TLS提供恢復機制,或多個連接之間共享相同的協商密鑰數據。
傳輸層安全(TLS)
會話標識
“會話標識符”(RFC 5246)恢復機制在SSL 2.0中首次被引入,它允許服務器在“ServerHello消息”中構建和發送一個32字節的會話標識符,作為“ServerHello”消息的一部分。
在服務器內部,服務器維護一個會話ID和其對應的協商參數的緩存。反過來,客戶端也同時存儲會話ID信息,在后續的會話中,可以在“ClientHello”消息中攜帶session ID信息,指示服務器它保存了session ID對應的密鑰和加密算法等信息,并且可以重用這些信息。假設在客戶端和服務器都能在它們各自的緩存中找到共享的會話ID參數,那么縮減的握手就可以進行了。否則,開始一個新的會話協商,這將產生一個新的會話ID。
我們演示一下,在前面抓包的的基礎上,我們再發一次請求。
最外面的紅框顯示了整個https訪問流程,內部的框是除去TCP握手和分手的的流程。我們將流程形象畫出來:
借助會話標識符,我們能夠減少一個完整的往返,以及用于協商的共享密鑰的公鑰加密算法開銷。這讓我們能快速的建立安全連接,而不損失安全性。我們看一下這個會話標識:
第二次的請求中的Client Hello的消息中Session Ticket有值了。
那這個Session Ticket是啥時候獲取的呢。我們繼續重溫下https握手流程,在最后一部。Server最后發給Client中,第一個消息就是New Session Ticket。我們看一下:
在實際應用中,大多數Web應用程序試圖通過建立到同一個主機的多個連接并行獲取資源,這使得會話恢復成為必不可少的一個優化項,其可以減少延??遲,計算成本。
大多數現代瀏覽器都會有意的等待第一TLS連接完成后,再打開到同一臺服務器的新連接:后續TLS連接,可以重復使用的SSL會話參數,以避免握手的延遲和損耗。
然而,“會話標識符”機制的一個限制就是要求服務器為每個客戶端創建和維護一個會話緩存。這會為服務器上帶來幾個問題,對于一些每天同時幾萬,甚至幾百萬的單獨連接的服務器來說:由于緩存session ID所需要的內存消耗將非常大,同時還有session ID清除策略的問題。這對一些流量大的網站來說不是一個簡單的任務,理想的情況下,使用一個共享的TLS會話緩存可以獲得最佳性能。
上述問題沒有是不可能解決的,許多高流量的網站成功的使用了會話標識符。但是,對任何多服務主機的部署,會話標識符方案需要一些認真的思考和好的系統架構,以確保良好的的會話緩存。
Session Tickets
為了解決上面的會話緩存帶來的服務器部署問題,“Sesion Ticket”(RFC 5077)取代機制被引入,目標是消除服務器需要維護每個客戶端的會話狀態緩存的要求。相反,如果客戶指示它支持Session Ticket,在TLS握手的最后一步中服務器將包含一個“New Session Ticket”信息,包含了一個加密通信所需要的信息,這些數據采用一個只有服務器知道的密鑰進行加密。
這個Session Ticket由客戶端進行存儲,并可以在隨后的一次會話中添加到 ClientHello消息的SessionTicket擴展中-因此,所有的會話信息只存儲在客戶端上,Session Ticket仍然是安全的,因為它是由只有服務器知道的密鑰加密的。
Session Identifiers和Session Ticket機制通常分別被稱為“會話緩存”和“無狀態恢復”機制。無狀態恢復的主要改進是消除服務器端的會話緩存,從而簡化了部署,它要求客戶在每一個新的會話開始時提供Session Ticket 直到Ticket過期。
在實際應用中,在一組負載平衡服務器中部署Session Ticket,也需要仔細考慮:所有的服務器都必須用相同的會話密鑰,或者可能需要額外的機制,定期輪流在所有服務器上的共享密鑰。
SessionId和Session Ticket的區別
Session ID的思想就是服務器端為每一次的會話生成并記錄一個ID號并發送給客戶端,在重新連接的時候(多次短連接場景),客戶端向服務器發送該ID號,服務器查找自己的會話記錄,匹配之后,重用之前的加密參數信息。
而Sessionticket的思想類似于cookie,是由服務器將ticket數據結構發由客戶端管理,ticket中是包含了加密參數等連接信息。當需要重連的時候,客戶端將ticket發送給服務器。這樣雙方就得到了重用的加密參數。
Session ticket較之Session ID優勢在于服務器使用了負載均衡等技術的時候。Session ID往往是存儲在一臺服務器上,當我向不同的服務器請求的時候,就無法復用之前的加密參數信息,而Session ticket可以較好的解決此類問題,因為相關的加密參數信息交由客戶端管理,服務器只要確認即可。
證書
證書種類
證書格式
一般來說,主流的 Web 服務軟件,通常都基于 OpenSSL 和 Java 兩種基礎密碼庫。
Tomcat、Weblogic、JBoss 等 Web 服務軟件,一般使用 Java 提供的密碼庫。通過 Java Development Kit (JDK)工具包中的 Keytool 工具,生成 Java Keystore(JKS)格式的證書文件。
Apache、Nginx 等 Web 服務軟件,一般使用 OpenSSL 工具提供的密碼庫,生成 PEM、KEY、CRT 等格式的證書文件。
IBM 的 Web 服務產品,如 Websphere、IBM Http Server(IHS)等,一般使用 IBM 產品自帶的 iKeyman 工具,生成 KDB 格式的證書文件。
微軟 Windows Server 中的 Internet Information Services(IIS)服務,使用 Windows 自帶的證書庫生成 PFX 格式的證書文件。
證書類型
您可以使用以下方法簡單區分帶有后綴擴展名的證書文件:
- *.DER 或 *.CER 文件: 這樣的證書文件是二進制格式,只含有證書信息,不包含私鑰。
- *.CRT 文件: 這樣的證書文件可以是二進制格式,也可以是文本格式,一般均為文本格式,功能與 *.DER 及 *.CER 證書文件相同。
- *.PEM 文件: 這樣的證書文件一般是文本格式,可以存放證書或私鑰,或者兩者都包含。 *.PEM 文件如果只包含私鑰,一般用 *.KEY 文件代替。
- *.PFX 或 *.P12 文件: 這樣的證書文件是二進制格式,同時包含證書和私鑰,且一般有密碼保護。
您也可以使用記事本直接打開證書文件。如果顯示的是規則的數字字母,例如:
—–BEGIN CERTIFICATE—–
MIIE5zCCA8+gAwIBAgIQN+whYc2BgzAogau0dc3PtzANBgkqh......
—–END CERTIFICATE—–
那么,該證書文件是文本格式的。
如果存在——BEGIN CERTIFICATE——,則說明這是一個證書文件。
如果存在—–BEGIN RSA PRIVATE KEY—–,則說明這是一個私鑰文件。
更多內容參考主流數字證書都有哪些格式?
常見加密算法
- 非對稱加密算法:RSA,DSA/DSS
- 對稱加密算法:AES,RC4,3DES
- HASH算法:MD5,SHA1,SHA256
為啥數據傳輸時候用對稱加密?
RSA性能是非常低的,原因在于尋找大素數、大數計算、數據分割需要耗費很多的CPU周期,所以一般的HTTPS連接只在第一次握手時使用非對稱加密,通過握手交換對稱加密密鑰,在之后的通信走對稱加密。