一、HTTPS 協議
HTTPS協議其實就是HTTP over TSL,TSL(Transport Layer Security) 傳輸層安全協議是https協議的核心。
TSL可以理解為SSL (Secure Socket Layer)安全套接字層的后續版本。
TSL握手協議如下圖所示

(注:圖片來源於google圖片)
在建立TCP連接后,開始建立TLS連接。下面抓包分析TLS握手過程,抓包圖片來源於傳輸層安全協議抓包分析之SSL/TLS (自己沒抓到這么完整的包,只能搬運過來了,摔)
(1) client端發起握手請求,會向服務器發送一個ClientHello消息,該消息包括其所支持的SSL/TLS版本、Cipher Suite加密算法列表(告知服務器自己支持哪些加密算法)、sessionID、隨機數等內容。

(2) 服務器收到請求后會向client端發送ServerHello消息,其中包括:
SSL/TLS版本;
session ID,因為是首次連接會新生成一個session id發給client;
Cipher Suite,sever端從Client Hello消息中的Cipher Suite加密算法列表中選擇使用的加密算法;
Radmon 隨機數。

(3) 經過ServerHello消息確定TLS協議版本和選擇加密算法之后,就可以開始發送證書給client端了。證書中包含公鑰、簽名、證書機構等信息。

(4) 服務器向client發送ServerKeyExchange消息,消息中包含了服務器這邊的EC Diffie-Hellman算法相關參數。此消息一般只在選擇使用DHE 和DH_anon等加密算法組合時才會由服務器發出。
(5) server端發送ServerHelloDone消息,表明服務器端握手消息已經發送完成了。

(6) client端收到server發來的證書,會去驗證證書,當認為證書可信之后,會向server發送ClientKeyExchange消息,消息中包含客戶端這邊的EC Diffie-Hellman算法相關參數,然后服務器和客戶端都可根據接收到的對方參數和自身參數運算出Premaster secret,為生成會話密鑰做准備。

(7) 此時client端和server端都可以根據之前通信內容計算出Master Secret(加密傳輸所使用的對稱加密秘鑰),client端通過發送此消息告知server端開始使用加密方式發送消息。

(8) 客戶端使用之前握手過程中獲得的服務器隨機數、客戶端隨機數、Premaster secret計算生成會話密鑰master secret,然后使用該會話密鑰加密之前所有收發握手消息的Hash和MAC值,發送給服務器,以驗證加密通信是否可用。服務器將使用相同的方法生成相同的會話密鑰以解密此消息,校驗其中的Hash和MAC值。

(9) 服務器發送ChangeCipherSpec消息,通知客戶端此消息以后服務器會以加密方式發送數據。

(10) sever端使用會話密鑰加密(生成方式與客戶端相同,使用握手過程中獲得的服務器隨機數、客戶端隨機數、Premaster secret計算生成)之前所有收發握手消息的Hash和MAC值,發送給客戶端去校驗。若客戶端服務器都校驗成功,握手階段完成,雙方將按照SSL記錄協議的規范使用協商生成的會話密鑰加密發送數據。

二、session ID的復用
根據rfc5246,client和server建立TLS握手過程如下所示:
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
Figure 1. Message flow for a full handshake
* Indicates optional or situation-dependent messages that are not always sent.
client在向server發送ClientHello消息的時候,會傳送Session ID給server端,server端收到session Id后會去session緩存中查找是否有相同值。如果找到相同值,則server直接發送一個具有相同session ID的ServerHello消息給client端(此時不必新建Session ID),然后雙方各發一次ChangeCipherSpec消息后直接進入Finished消息互發階段,具體如下圖所示:
Client Server
ClientHello -------->
ServerHello
[ChangeCipherSpec]
<-------- Finished
[ChangeCipherSpec]
Finished -------->
Application Data <-------> Application Data
Figure 2. Message flow for an abbreviated handshake
client和server通過緩存Session ID可以快速建立TLS握手,但是這么做也有一些弊端,例如:1)負載均衡中,多機之間往往沒有同步 Session 信息,如果客戶端兩次請求沒有落在同一台機器上就無法找到匹配的信息;2)服務端存儲 Session ID 對應的信息不好控制失效時間,太短起不到作用,太長又占用服務端大量資源。而 Session Ticket(會話記錄單)可以解決這些問題,Session Ticket 是用只有服務端知道的安全密鑰加密過的會話信息,最終保存在瀏覽器端。瀏覽器如果在 ClientHello 時帶上了 Session Ticket,只要服務器能成功解密就可以完成快速握手。
三、數字證書的驗證
client端收到server端發過來的證書,首先必須要做的事驗證證書是否可信。如何驗證證書是否可信呢?為了解決這個問題,我們先來了解下證書的組成

從這里面我們能看到證書包含以下內容:
(1) Validity也即有效期,有效期包含生效時間和失效時間,是一個時間區間;
(2) 公鑰信息Subject Public Key Info,包括公鑰的加密算法和公鑰內容;
(3) Fingerprints信息,fingerprints用於驗證證書的完整性,也就是說確保證書沒有被修改過。 其原理就是在發布證書時,發布者根據指紋算法(此處證書使用了SHA-1和SHA-256算法)計算整個證書的hash值(指紋)並和證書放在一起,client在打開證書時,自己也根據指紋算法計算一下證書的hash值(指紋),如果和剛開始的值相同,則說明證書未被修改過;如果hash值不一致,則表明證書內容被篡改過;
(4) 證書的簽名Certificate Signature Value和Certificate Signature Algorithm,對證書簽名所使用的Hash算法和Hash值;
(5) 簽發該證書的CA機構Issuer;
(6) 該證書是簽發給哪個組織/公司信息Subject;
(7) 證書版本Version、證書序列號Serial Number以及Extensions擴展信息等。

如上圖所示,為簽名過程和client端驗證過程。中間方框為一個數字證書,在制作數字證書時,會將證書中的部分內容進行一次Hash得到一個Hash值,然后證書認證機構簡稱CA會使用私鑰將該Hash值加密為Certificate Signature。
當證書發送給Client端之后,Client端首先會使用同樣的Hash算法獲得一個證書的hash值H1。通常瀏覽器和操作系統中集成了CA機構的公鑰信息,瀏覽器收到證書后可以使用這些公鑰解密Certificate Signature內容,得到一個hash值H2。
比較H1和H2,如果值相同,則為可信賴的證書,否則則認為證書不可信。
自己通過openssl生成的自簽發證書只是使用自己的私鑰去加密上圖左側計算出的Hash Value,這個時候client端得到server端發過來的證書之后,仍然會嘗試使用瀏覽器或系統內置的CA機構的公鑰去解密,解密出來的hash值H2當然不可能與H1相同,因此瀏覽器認為該證書不受信任。但是如果我們選擇相信該證書並且繼續訪問該web,訪問並不會出現任何問題,這是因為證書中的公鑰並未加密,使用該公鑰也確實能和server端的私鑰進行TLS握手。
四、證書信任鏈
在證書認證過程中還存在一個證書信任鏈的問題,因為我們從CA機構申請到的證書基本不可能是根證書簽發。還是以百度的證書為例,如下圖所示,百度證書層級為三層,認證過程如下:

(1) 當client端訪問baidu.com的時候,baidu的server會將baidu.com證書發送給client端。
(2) client端的操作系統或者瀏覽器中內置了根證書,但是client端收到baidu.com這個證書后,發現這個證書不是根證書簽發,無法根據本地已有的根證書中的公鑰去驗證baidu.com證書是否可信。
於是client端根據baidu.com證書中的Issuer找到該證書的頒發機構GlobalSign Organization Validation CA - SHA256 - G2,去CA請求baidu.com證書的頒發機構GlobalSign Organization Validation CA - SHA256 - G2的證書。
(3) 請求到證書后發現GlobalSign Organization Validation CA - SHA256 - G2證書是由根證書簽發,而本地剛好有根證書,於是可以利用根證書中的公鑰去驗證(驗證方法見上一節)GlobalSign Organization Validation CA - SHA256 - G2證書,發現驗證通過,於是信任GlobalSign Organization Validation CA - SHA256 - G2證書。
(4) GlobalSign Organization Validation CA - SHA256 - G2證書被信任后,可以使用GlobalSign Organization Validation CA - SHA256 - G2證書中的公鑰去驗證baidu.com證書的可信性。驗證通過,於是信任baidu.com證書。
在這四個步驟中,最開始client端只信任根證書GlobalSign Root CA證書的,然后GlobalSign Root CA證書信任GlobalSign Organization Validation CA - SHA256 - G2證書,而GlobalSign Organization Validation CA - SHA256 - G2證書又信任baidu.com證書,於是client端也信任baidu.com證書。這樣的一個過程就構成了一條信任鏈路,整個證書信任鏈驗證流程如下圖所示。

五、非對稱加解密
非對稱加密包含一個密鑰對:公鑰和私鑰。公鑰可以公開,私鑰必須安全保存。

如上圖所示,數據可以被公鑰加密,加密后的數據只有持有私鑰才能進行解密。同理私鑰加密的數據,也只有對應的公鑰才能解密。
建立HTTPS連接以后,client(瀏覽器)已經獲得server段的公鑰,並且經過TLS協議在握手過程中協商出一個只有雙方知道的對稱密鑰,在后續的數據傳輸過程中都將使用該密鑰進行數據加密傳輸。因為公鑰是公開的,可以下發給所有client端的,這個時候即使其他擁有公鑰的client端截獲到server端發給client端的消息,也無法僅僅憑借公鑰去解密這個消息,因為這個消息加密密鑰是由client和server通過相同算法計算出來,並且添加了隨機數,理論上這個對稱密鑰只有client和server才能知道。
參考文檔:
https://tools.ietf.org/html/rfc5246#page-6
http://www.freebuf.com/articles/network/116497.html
https://imququ.com/post/optimize-tls-handshake.html
http://blog.csdn.net/wzzvictory/article/details/9015155
