自從 Let's Encrypt
上線以後,HTTPS
網站數量佔比愈來愈高,相信不久的將來就能夠實現全網 HTTPS
,大部分主流瀏覽器也對 HTTP
網頁給出明顯的 不安全
標誌。html
SSL
是在 TCP
層之上爲客戶端服務端之間數據傳輸運用複雜的加密算法,swoole
使用 SSL
加密只須要兩個步驟:node
$serv = new swoole_server("0.0.0.0", 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $key_dir = dirname(dirname(__DIR__)).'/tests/ssl'; $serv->set(array( 'worker_num' => 4, 'ssl_cert_file' => $key_dir.'/ssl.crt', 'ssl_key_file' => $key_dir.'/ssl.key', ));
SSL
/TLS
安全通訊因爲 HTTPS
的推出受到了不少人的歡迎,在 SSL
更新到 3.0 時,IETF
對 SSL3.0
進行了標準化,並添加了少數機制(可是幾乎和 SSL3.0
無差別),標準化後的 IETF
改名爲 TLS1.0
(Transport Layer Security
安全傳輸層協議),能夠說 TLS
就是 SSL
的新版本 3.1,並同時發佈「 RFC2246-TLS
加密協議詳解」。git
首先咱們先來了解一下 SSL
/TLS
的原理。github
如下內容來源於參考文獻:TLS和安全通訊算法
TLS
依賴兩種加密技術:數據庫
symmetric encryption
)asymmetric encryption
)對稱加密的一方(好比小紅)用祕鑰 K 給文本 M 加密;另外一方(好比小明)用
同一個祕鑰解密:segmentfault
小紅 : C = E(M, K) 小明 : M = D(C, K)
這有一個問題:當一方生成了祕鑰 K
以後得把 K
分享給另外一方。可是穿越 `Sin
City 的道路危險中途極可能有人竊聽到
K`,竊聽者就能夠假扮雙方中的任何一
方與另外一方通訊。這叫中間人攻擊。api
非對稱加密利用成對的兩個祕鑰:K1
和 K2
。小紅用其中一個加密文本,小明可
以用另外一個解密文本。好比,小紅用 K1
加密,小明用 K2
解密:瀏覽器
小紅 : C = E(M, K1) 小明 : M = D(C, K2)
這樣一來,雙方中的一方(好比小紅)能夠生成 K1
和 K2
,而後把其中一個祕鑰(好比 K1
)私藏,稱爲私鑰;另外一個(好比K2)公開,稱爲公鑰。另外一方(好比小明)獲得公鑰以後,雙方就能夠通訊。安全
然而,中間人仍是可能截獲公鑰 K2
,而後本身弄一對祕鑰(κ1
, κ2
),而後告訴小明說 κ2
是小紅的公鑰。這樣中間人每次能夠用截獲的 K2
解密小紅髮給小明的文本(甚至可能修改文本),再用 κ1
加密了發出去;小明用 κ2
解密接收。
爲了幫小明肯定獲得的公鑰確實是小紅的 K2
,而不是中間人僞造的 κ2
,牛人們發明了數字簽名(digital signature)技術。
數字簽名的作法是:
ID
(身份證號碼,或者域名)合爲身份證申請(certificate signing request,CSR),CSR
發給一個德高望重的人(被稱爲 certificate authority
,CA
),好比小亮,CSR
,獲得的密文被稱爲數字簽名(digital signature),signature
和 CSR
的明文合在一塊兒稱爲 CA簽署的身份證(CA
signed certificate
,CRT
),發給小紅,小紅:CSR = 小紅公鑰+小紅域名 signature = E(CSR, 小亮的私鑰) CRT = CSR + signature
每當其餘人(好比小明)找小紅聊天(創建 HTTPS
鏈接)的時候,小紅出示本身的小亮簽署的身份證。拿到這個身份證的人,只要他是相信小亮的——在本身機器上安裝了小亮的身份證,就能夠
CSR
裏提取小亮的公鑰;signature
,獲得一個小紅的 CSR
;CSR
和小紅身份證中的 CSR
明文一致,則說明「這個小紅的身份證是小亮確認過而且簽名的」。小明:小亮的公鑰 = 小亮的CRT.CSR.小亮的公鑰 CSR' = D(CRT.signature, 小亮的公鑰) if CSR' == CRT.CSR then OK
由此過程能夠看出來:隨便誰均可以當 CA
——只要願意公開本身的公鑰,便可用本身的私鑰去加密別人的認證。那咱們要是信錯了 CA
,被他擺一道怎麼辦?答案是:沒辦法。咱們選擇信任社會,要相信若是 CA
說謊,萬一被識破,就沒有人
再相信他了。現實中,不少操做系統(Windows
、Mac OS X
)和瀏覽器(Chrome
、Firefox
、IE
)會內置一些靠譜的 CA
的身份證。
小亮若是擔憂沒有人信任本身是個好 CA
(就像沒人信CNNIC同樣),能夠找一個你們都信的 CA
,好比老王,用老王的私鑰在小亮的身份證上簽名:
小亮:CSR = 小亮的公鑰+小亮域名 signature = E(CSR, 老王的私鑰) CRT = CSR + signature
若是瀏覽器或者操做系統裏安裝了老王的公鑰則能夠驗證「小亮的身份證是老王確認而且簽名過的」。
這樣,小亮在簽署小紅的身份證的時候,能夠在小紅身份證後面附上本身的身份證。這樣小紅的身份證就有「兩頁」了。
當小明和小紅通訊的時候:
要是怕小明連本身也也不信任,老王能夠再找一個小明信任的人來簽名確認本身的身份證。這個過程能夠不斷遞推,從而造成了一條信任鏈(trust of chain
)
信任鏈總會有個頂端,被稱爲根身份證(root CA)。那麼根身份證是誰簽名的呢?答案是:本身簽名。實際上,咱們每一個人均可以本身簽名認證本身的身份證,獲得自簽名的身份證(self-signed certificate)。具體過程是:
K2
和私鑰 K1
,CSR
,CSR
獲得 signature
,而後把 CSR
明文和 signature
一塊兒發佈。任何人只要信任咱們自簽名的身份證 CRT
,也就能夠用 CRT.CSR.K2
做爲公鑰加密要傳遞給咱們的文本。咱們能夠用本身的私鑰 K1
來解密文本。
通常來講,自簽名的根身份證用於公司內部使用。
若是老王就是根 CA
了,那麼上述各位的身份證的信任鏈以下:
小紅:CSR = 小紅公鑰+小紅域名 signature = E(CSR, 小亮的私鑰) CRT = CSR + signature 小亮:CSR = 小亮的公鑰+小亮域名 signature = E(小亮的CSR, 老王的私鑰) CRT = 小亮的CSR + signature 老王:CSR = 老王的公鑰+老王的域名 signature = E(老王的CSR, 老王本身的私鑰) CRT = 老王的CSR + signature
上述例子解釋了通訊的一方如何驗證另外一方的身份。這種狀況的一個常見應用是:咱們經過瀏覽器訪問銀行的網頁。這裏的關鍵是,咱們要能驗證銀行的身份證,而後才勇於在網頁裏輸入帳號和密碼。瀏覽器驗證銀行的身份證的過程以下:
瀏覽器驗證了銀行的 HTTPS
服務的身份以後,就輪到銀行驗證瀏覽器的用戶的身份了:
在這個過程當中,銀行 HTTPS
服務器的身份是經過 TLS
身份證來驗證的。而咱們(用戶)的身份是經過咱們輸入的帳號和密碼來驗證的。
有時通訊的雙方都是程序(而不是人)。此時,讓一方輸入帳號和密碼,不如讓雙方都經過 TLS
身份證來互相驗證方便。尤爲是在不少分佈式系統裏,有多種類型的程序互相通訊,而不僅是兩方通訊。
好比在 Kubernetes
機羣裏,不光操做機羣的客戶端程序 kubectl
要能驗證 Kubernetes master node
(具體的說是 apiserver
)的身份,才能放心地把包括敏感信息(好比數據庫密碼)的計算做業提交給 apiserver
。相似的,apiserver
也要能驗證 kubectl
的身份,以確認提交做業的是公司的合法僱員,而不是外賊。
爲此,通訊各方都須要有各自的身份證。一個公司能夠自簽名一個 CA
身份證,而且用它來給每一個僱員以及每一個程序簽署身份證。這樣,只要每臺電腦上都預先安裝好公司本身的 CA
身份證,就能夠用這個身份證驗證每一個僱員和程序的身份了。
這是目前不少公司的經常使用作法。
由於 TLS
模式下全部傳輸的數據都是加密的,你們會關注加密和解密的性能。客觀的說,非對稱加密技術的加密和解密比較慢,相對來講,對稱加密技術的加密解密過程更快。因此實際的鏈接和握手過程當中,通訊雙方會協商一個對稱加密祕鑰,以後的數據通訊過程當中的加密都是利用對稱加密技術來實現的。
具體的作法是:握手的時候,雙方各自生成一個隨機數,而且以非對稱加密的方式分享給對方。而後每一方都把本身的隨機數和對方的隨機數拼起來,就是接下來通訊時候使用的對稱加密方法的祕鑰了。
OpenSSL
操做指南接下來,咱們來看看如何生成 HTTPS
所須要的證書。
如下內容來源於參考文獻:openssl的介紹和使用
openssl
簡介OpenSSL
是一個開源項目,其組成主要包括一下三個組件:
openssl
能夠實現:祕鑰證書管理、對稱加密和非對稱加密更多簡介和官網。
平時咱們使用 openssl
最多的莫過於使用指令了,而最爲常見的幾個指令以下:
genrsa
生成RSA參數req
x509
rsa
ca
genrsa
簡介平時主要用來生成私鑰,選擇使用的算法、對稱加密密碼和私鑰長度來生成私鑰。也就是生成 key
文件。
基本用法:
openssl genrsa [args] [numbits]
其中常見的參數:【更多參數查看:openssl genrsa -help
】
args1 對生成的私鑰文件是否要使用加密算法進行對稱加密: -des : CBC模式的DES加密 -des3 : CBC模式的3DES加密 -aes128 : CBC模式的AES128加密 -aes192 : CBC模式的AES192加密 -aes256 : CBC模式的AES256加密 args2 對稱加密密碼 -passout passwords 其中passwords爲對稱加密(des、3des、aes)的密碼(使用這個參數就省去了console交互提示輸入密碼的環節) args3 輸出文件 -out file : 輸出證書私鑰文件 [numbits]: 密鑰長度,理解爲私鑰長度
生成一個 2048 位的 RSA
私鑰,並用 des3
加密(密碼爲 123456
),保存爲 server.key
文件
openssl genrsa -des3 -passout pass:123456 -out server.key 2048 // -des3 是第一個參數args1; // -passout pass:123456 是第二個參數寫法 args2 // -out server.key 第三個參數args3; // 2048 最後一個[numbits]參數
生成的 key
文件是 PEM
格式的
-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,DB98A9512DD7CBCF yKTM+eoxBvptGrkEixhljqHSuE+ucTh3VqYQsgO6+8Wbh1docbFUKzLKHrferJBH ... -----END RSA PRIVATE KEY-----
雖然說文件頭尾都標註着 RSA PRIVATE KEY
,但實際上這個文件裏既包括公鑰也包括私鑰。
req
命令req
的基本功能主要有兩個:生成證書請求和生成自簽名證書,也就是 csr
文件,或者自簽名的 crt
文件。固然這並非其所有功能,可是這兩個最爲常見;
常見使用方法:
openssl req [args] outfile
主要參數:【更多參數查看:openssl req -help
】
args1 是輸入輸入文件格式: -inform arg -inform DER 使用輸入文件格式爲DER -inform PEM 使用輸入文件格式爲PEM args2 輸出文件格式: -outform arg -outform DER 使用輸出文件格式爲DER -outform PEM 使用輸出文件格式爲PEM args3 是待處理文件 -in inputfilepath args4 待輸出文件 -out outputfilepath args5 用於簽名待生成的請求證書的私鑰文件的解密密碼 -passin passwords args6 用於簽名待生成的請求證書的私鑰文件 -key file args7 指定輸入密鑰的編碼格式 -keyform arg -keyform DER -keyform NET -keyform PEM args8 生成新的證書請求 -new args9 輸出一個X509格式的證書,簽名證書時使用 -x509 args10 使用 X509 簽名證書的有效時間 -days // -days 3650 有效期10年 args11 生成一個bits長度的RSA私鑰文件,用於簽發【生成私鑰、並生成自簽名證書】 -newkey rsa:bits args12設置HASH算法-[digest]【生成私鑰指定的hash摘要算法】 -md5 -sha1 // 高版本瀏覽器開始不信任這種算法 -md2 -mdc2 -md4 args13指定openssl配置文件,不少內容不容易經過參數配置,能夠指定配置文件 -config filepath args14 顯示格式txt【用於查看證書、私鑰信息】 -text
使用的案例:利用私鑰生成證書請求 csr
openssl req -new -key server.key -out server.csr
server.csr
文件也是 PEM
格式的,文件頭尾標註爲 CERTIFICATE REQUEST
:
-----BEGIN CERTIFICATE REQUEST----- MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTERMA8GA1UE ... -----END CERTIFICATE REQUEST-----
使用案例:利用私鑰生成自簽名證書
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
x509
命令x509
是一個功能很豐富的證書處理工具。能夠用來顯示證書的內容,轉換其格式,給 CSR
簽名等 X.509
證書的管理工做;
用法以下:
openssl x509 [args]
參數以下:【更多參數查看:openssl x509 -help
】
args1 是輸入輸入文件格式: -inform arg -inform DER 使用輸入文件格式爲DER -inform PEM 使用輸入文件格式爲PEM args2 輸出文件格式: -outform arg -outform DER 使用輸出文件格式爲DER -outform PEM 使用輸出文件格式爲PEM args3 是待處理X509證書文件 -in inputfilepath args4 待輸出X509證書文件 -out outputfilepath args5代表輸入文件是一個"請求籤發證書文件(CSR)",等待進行簽發 -req args6簽名證書的有效時間 -days // -days 3650 有效期10年 args7 指定用於簽發請求證書的根CA證書 -CA arg args8 根CA證書格式(默認是PEM) -CAform arg args9 指定用於簽發請求證書的CA私鑰證書文件 -CAkey arg args10 指定根CA私鑰證書文件格式(默認爲PEM格式) -CAkeyform arg args11 指定序列號文件(serial number file) -CAserial arg args12 若是序列號文件(serial number file)沒有指定,則自動建立它 -CAcreateserial args13設置HASH算法-[digest]【生成私鑰指定的hash摘要算法】 -md5 -sha1 // 高版本瀏覽器開始不信任這種算法 -md2 -mdc2 -md4
使用實例: 使用根 CA
證書[ ca.crt
]和私鑰[ ca.key
]對"請求籤發證書"[ server.csr
]進行簽發,生成 x509
格式證書
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out serverx509.crt
server.crt
也是 PEM
格式的。文件頭尾的標記爲 CERTIFICATE
:
-----BEGIN CERTIFICATE----- MIIDlDCCAnwCCQDQ1UvQyFD7jDANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMC ... -----END CERTIFICATE-----
DER
、CRT
、CER
、PEM
、KEY
、PFX/P12
格式下文參考文獻:DER、CRT、CER、PEM格式的證書及轉換
X.509
證書,其核心是根據 RFC 5280
編碼或數字簽名的數字文檔。
實際上,術語 X.509
證書一般指的是 IETF
的 PKIX
證書和 X.509 v3
證書標準的 CRL
文件,即如 RFC 5280
(一般稱爲PKIX for Public Key Infrastructure(X.509)
)中規定的。
咱們首先要了解的是每種類型的文件擴展名。 不少人不清楚DER,PEM,CRT和CER結尾的文件是什麼,更有甚者錯誤地說是能夠互換的。 在某些狀況下,某些能夠互換,最佳作法是識別證書的編碼方式,而後正確標記。 正確標籤的證書將更容易操縱
.DER
擴展名.DER
= DER
擴展用於二進制 DER
編碼證書。
這些文件也可能承載 CER
或 CRT
擴展。 正確的說法是「我有一個 DER
編碼的證書」不是「我有一個 DER
證書」。
.PEM
擴展名.PEM = PEM
擴展用於不一樣類型的 X.509v3
文件,是以「 - BEGIN ...」前綴的 ASCII(Base64)
數據。
.CRT
擴展名.CRT = CRT
擴展用於證書。 證書能夠被編碼爲二進制 DER
或 ASCII PEM
。 CER
和 CRT
擴展幾乎是同義詞。 最多見的於 Unix
或類 Unix
系統。
.CER
擴展名CER = .crt
的替代形式(Microsoft Convention
)您能夠在微軟系統環境下將 .crt
轉換爲 .cer
( .DER
編碼的 .cer
,或 base64 [PEM]
編碼的 .cer
)。
.KEY
擴展名.KEY = KEY
擴展名用於公鑰和私鑰 PKCS#8
。 鍵能夠被編碼爲二進制 DER
或 ASCII PEM
。
PFX/P12
擴展名predecessor of PKCS#12
,對 *nix
服務器來講,通常 CRT
和 KEY
是分開存放在不一樣文件中的,但 Windows
的 IIS
則將它們存在一個 PFX
文件中,(所以這個文件包含了證書及私鑰)這樣會不會不安全?應該不會,PFX
一般會有一個"提取密碼", 你想把裏面的東西讀取出來的話,它就要求你提供提取密碼,PFX
使用的時 DER
編碼,如何把 PFX
轉換爲 PEM
編碼?
openssl pkcs12 -in for-iis.pfx -out for-iis.pem -nodes
這個時候會提示你輸入提取代碼. for-iis.pem
就是可讀的文本.
生成 pfx
的命令相似這樣:
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt
其中 CACert.crt
是 CA
(權威證書頒發機構)的根證書,有的話也經過 -certfile
參數一塊兒帶進去.這麼看來,PFX
實際上是個證書密鑰庫.
OpenSSL
證書操做證書操做有四種基本類型。查看,轉換,組合和提取。
即便PEM編碼的證書是 ASCII
,它們也是不可讀的。這裏有一些命令可讓你以可讀的形式輸出證書的內容;
PEM
編碼證書openssl x509 -in cert.pem -text –noout openssl x509 -in cert.cer -text –noout openssl x509 -in cert.crt -text –noout 若是您遇到這個錯誤,這意味着您正在嘗試查看DER編碼的證書,並須要使用「查看DER編碼證書」中的命令。 unable to load certificate 12626:error:0906D06C:PEMroutines:PEM_read_bio:no start line:pem_lib.c:647:Expecting: TRUSTEDCERTIFICATE
DER
編碼證書openssl x509 -in certificate.der -inform der -text -noout 若是您遇到如下錯誤,則表示您嘗試使用DER編碼證書的命令查看PEM編碼證書。在「查看PEM編碼的證書」中使用命令 unable to load certificate 13978:error:0D0680A8:asn1 encodingroutines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1306: 13978:error:0D07803A:asn1 encodingroutines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:380:Type=X509
轉換能夠將一種類型的編碼證書存入另外一種。(即PEM到DER轉換)
openssl x509 -in cert.crt -outform der-out cert.der openssl x509 -in cert.crt -inform der -outform pem -out cert.pem
在某些狀況下,將多個 X.509
基礎設施組合到單個文件中是有利的。一個常見的例子是將私鑰和公鑰二者結合到相同的證書中。
組合密鑰和鏈的最簡單的方法是將每一個文件轉換爲 PEM
編碼的證書,而後將每一個文件的內容簡單地複製到一個新文件中。這適用於組合文件以在 Apache
中使用的應用程序
一些證書將以組合形式出現。 一個文件能夠包含如下任何一個:證書,私鑰,公鑰,簽名證書,證書頒發機構(CA)和/或權限鏈。
SSL
/TLS
握手流程Client random
),以及客戶端支持的加密方法。Server random
)。Premaster secret
),並使用數字證書中的公鑰,加密這個隨機數,發給鮑勃。Premaster secret
)。session key
),對話密鑰被切片生成兩個對稱密鑰和 MAC
密鑰,用來加密接下來的整個對話過程。注意這個例子只是傳統的 RSA
模式密鑰交換的 SSL
握手流程。
如下內容參考文獻:
ClientHello
)握手第一步是客戶端向服務端發送 Client Hello
消息,這個消息裏包含了
Client random
、Support Ciphers
)SSL Version
,好比TLS 1.0版。Session id
用於會話複用Extension
拓展字段的存在,是由於 SSL
協議起草之初有些功能沒有考慮到,後續這些功能被加進 RFC
,而爲了兼容 SSL
,把這些功能的描述放到 Extension
中。
Server_name(SNI)
: 客戶端在 client hello
中帶上 server name
拓展(若是使用 ip
地址進行訪問,那麼就不會有 server name
拓展),它會捎帶上域名地址,服務器解析到 server name
後,就會根據 server name
中的域名,選擇合適的證書。Elliptic_curves/ec_point_formats
: 使用橢圓曲線密鑰交換算法的時候用到,裏面列舉了本身支持的橢圓曲線算法,供服務器選擇。SessionTicket TLS
: 會話複用時使用。status_request
: 請求 OCSP
,服務器能夠發送 cettificate status
到客戶端,裏面帶上 ocsp
的信息。signature_algorithms
: 表示本身支持的簽名算法,服務器收到這個拓展,在進行例如 server key exchange
簽名等操做時,須要參考客戶端這個拓展。application_layer_negotiation(ALPN)
: 用以描述支持的上層協議,h二、http/1.一、spdy等。能夠把他想象成IP頭中的protocol,描述了上層是TCP仍是UDP仍是ICMP。目前主流瀏覽器,若要使用HTTP2,則必須使用這個拓展。Renegotiation info
: 若是是從新協商(即加密的 hello
),那麼會帶上上次協商的 12 字節的 finished
,若是是新 hello
請求,那麼裏面字段爲0。切記及時服務器端不支持renegotiation,在server hello響應時也須要帶上這個拓展
這裏須要注意的是,客戶端發送的信息之中不包括服務器的域名。也就是說,理論上服務器只能包含一個網站,不然會分不清應該向客戶端提供哪個網站的數字證書。這就是爲何一般一臺服務器只能有一張數字證書的緣由。
對於虛擬主機的用戶來講,這固然很不方便。2006年,TLS協議加入了一個 Server Name Indication
擴展,容許客戶端向服務器提供它所請求的域名。
SeverHello
)服務器收到客戶端請求後,向客戶端發出迴應,這叫作SeverHello。服務器的迴應包含如下內容。
TLS 1.0
版本。若是瀏覽器與服務器支持的版本不一致,服務器關閉加密通訊。Client Hello
傳過來的 Support Ciphers
裏肯定一份加密套件,這個套件決定了後續加密和生成摘要時具體使用哪些算法,好比 RSA
公鑰加密。session id
(sessionid
會話複用須要帶上,固然命中 session ticket
時也須要帶上)renegotiation info
(前提是客戶端提供了 EMPTY_SCSV
加密套件或者有renegotiation info
拓展),以表示本身支持 secure renegotiation
。除了上面這些信息,若是服務器須要確認客戶端的身份,就會再包含一項請求,要求客戶端提供"客戶端證書"
Server Certificate
)發送服務器證書,將服務器配置的證書(鏈)發送到客戶端。
注意證書鏈的順序,最下層證書在前(用戶證書在前,上級證書在後)。發送的證書是二進制格式,並不是 base64
以後的格式。
server key exchange
)對於使用DHE/ECDHE非對稱密鑰協商算法的SSL握手,將發送該類型握手。
RSA
算法不會繼續該握手流程(DH
、ECDH
也不會發送 server key exchange
)。
ECDHE
下主要有幾點重要的信息:
supported_groups
中的選擇橢圓曲線算法)。BIGNUM
),乘上曲線的 base point
,獲得一個新的 point
,這個 point
就是公鑰,用 04+x+y
的格式組織起來。04
表示 unconpressed point
,和客戶端的 ec_point_formats
有關。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
」。
ECDSA
)是使用橢圓曲線密碼(ECC
)對數字簽名算法(DSA
)的模擬。DSA
算法是 RSA
算法的反向算法,服務端利用私鑰加密,客戶端用公鑰進行驗證,速度更快,可是不能加密,只能用於簽名驗證,由於公鑰是公開的。DHE
下主要有幾點重要的信息:
DH
參數,p
和 q
。Xb
,計算 q^Xb mod p
,將結果 Pb
發給客戶端,本身僅且本身保存 Xb
。certificate request
)雙向認證時,服務器會發送 certificate request
,代表本身想要收到客戶端的證書。
這個類型的握手主要包含了 ca
證書的 subject
,用以告訴客戶端本身須要哪些證書,不是由這些 ca
簽發的證書「我」不要。
客戶端,例如瀏覽器在收到這個請求時,若是不存在對應的證書,則發送一個空的 certificate
至服務器,若是存在一個證書,則發送該 certificate
至服務器。若是存在多個對應的證書,則會彈出一個彈出框讓你選擇。
Certificate request
還包含了想要證書的簽名的類型,RSA
仍是 ECDSA
,對於 TLS1.2
還會包括摘要信息。
Server hello done
)服務器告訴客戶完成它的初始化流通消息。
client certificate
)若是服務器端請求了客戶端的證書,客戶端即便沒有證書,也須要發送該類型的握手報文,只是這種狀況下,裏面的內容爲0。
若是瀏覽器有對應的證書,則會發送證書,固然,也有可能發送上級證書(即發送證書鏈),這個徹底取決於瀏覽器。
client key exchange
)ECDH/ECDHE
下比較簡單了,和 server key exchange
處理同樣,客戶端隨機生成一個大數,而後乘上 base point
,獲得的結果就是 public key
。DHE
下客戶端計算隨機數 Xa
,而後該報文中的 Pubkey
就是 q^Xa mop p
RSA
下客戶端隨機生成 48 字節的預主密鑰,而後使用 pkcs1
規則填充至公鑰同樣的長度,隨後調用 RSA
進行運算,獲得 Encrypted PreMaster
。填充規則以下:00 + 02 + non_zero + 0 + pre_master
。"無論是客戶端仍是服務器,都須要隨機數,這樣生成的密鑰纔不會每次都同樣。因爲SSL協議中證書是靜態的,所以十分有必要引入一種隨機因素來保證協商出來的密鑰的隨機性。 對於RSA密鑰交換算法來講,pre-master-key自己就是一個隨機數,再加上hello消息中的隨機,三個隨機數經過一個密鑰導出器最終導出一個對稱密鑰。 pre master的存在在於SSL協議不信任每一個主機都能產生徹底隨機的隨機數,若是隨機數不隨機,那麼pre master secret就有可能被猜出來,那麼僅適用pre master secret做爲密鑰就不合適了,所以必須引入新的隨機因素,那麼客戶端和服務器加上pre master secret三個隨機數一同生成的密鑰就不容易被猜出了,一個僞隨機可能徹底不隨機,但是是三個僞隨機就十分接近隨機了,每增長一個自由度,隨機性增長的可不是一。"
Certificate verify
)發送這個類型的握手須要2個前提條件
此時,客戶端想要證實本身擁有該證書,必然須要私鑰簽名一段數據發給服務器驗證。
簽名的數據是客戶端發送 certificate verify
前,全部收到和發送的握手信息(不包括5字節的 record
)。其實這個流程和簽名 server key exchange
基本同樣。計算摘要,而後簽名運算。
Change cipher
)指示 Server
從如今開始發送的消息都是加密過的。
Encrypted handshake message
)其實這個報文的目的就是告訴對端本身在整個握手過程當中收到了什麼數據,發送了什麼數據。來保證中間沒人篡改報文。
其次,這個報文做用就是確認祕鑰的正確性。由於 Encrypted handshake message
是使用對稱祕鑰進行加密的第一個報文,若是這個報文加解密校驗成功,那麼就說明對稱祕鑰是正確的。
計算方法也比較簡單,將以前全部的握手數據(包括接受、發送),計算 md
哈希運算,而後計算 prf
,而後就是使用協商好的對稱密鑰進行加密了。
MD運算:
TLS1.2
,摘要算法是 sha256
,即 md_result = sha256(all_handshake)
;TLS1.0 1.1
,摘要算法是 md5
和 sha1
結果的拼接,即 md_result = md5(all_handshake) + sha1(all_handshake)
。sha384
算法,例如 RSA_WITH_AES256_CBC_SHA384
加密套件,則不管協商使用 tls
哪一個版本,都用 sha384
,即 md_result = sha384(all_handshake)
。PRF運算:
計算完哈希後,客戶端按這種格式:「client finished」+ md_result
,做爲 prf
的輸入。PRF
的輸出指定爲 12字節。12 字節的數據前填充 4 字節 message
頭部信息,就能夠送入對稱加密流程進行加密了。
PRF
運算其實就是 P_HASH
運算,P_HASH
就是不斷 hmac
運算,直到計算出預約指定長度的值爲止。
HTTPS
經過 TLS
層和證書機制提供了內容加密、身份認證和數據完整性三大功能,能夠有效防止數據被監聽或篡改,還能抵禦 MITM
(中間人)攻擊。TLS
在實施加密過程當中,須要用到非對稱密鑰交換和對稱內容加密兩大算法。
對稱內容加密強度很是高,加解密速度也很快,只是沒法安全地生成和保管密鑰。在 TLS
協議中,應用數據都是通過對稱加密後傳輸的,傳輸中所使用的對稱密鑰,則是在握手階段經過非對稱密鑰交換而來。常見的 AES-GCM
、ChaCha20-Poly1305
,都是對稱加密算法。
非對稱密鑰交換能在不安全的數據通道中,產生只有通訊雙方纔知道的對稱加密密鑰。目前最經常使用的密鑰交換算法有 RSA
和 ECDHE
:RSA
歷史悠久,支持度好,但不支持 PFS
(Perfect Forward Secrecy
);而 ECDHE
是使用了 ECC
(橢圓曲線)的 DH
(Diffie-Hellman
)算法,計算速度快,支持 PFS
。
只有非對稱密鑰交換,依然沒法抵禦 MITM
攻擊,還得引入身份認證機制。對於大部分 HTTPS
網站來講,服務端通常經過 HTTP
應用層的賬號體系就能完成客戶端身份認證;而瀏覽器想要驗證服務端身份,須要用到服務端提供的證書。
在 RSA
密鑰交換中,瀏覽器使用證書提供的 RSA
公鑰加密相關信息,若是服務端能解密,意味着服務端擁有證書對應的私鑰,同時也能算出對稱加密所需密鑰。密鑰交換和服務端認證合併在一塊兒。
在 ECDHE
密鑰交換中,服務端使用證書私鑰對相關信息進行簽名,若是瀏覽器能用證書公鑰驗證簽名,就說明服務端確實擁有對應私鑰,從而完成了服務端認證。密鑰交換和服務端認證是徹底分開的。
可用於數字簽名的算法主要有 RSA
和 ECDSA
,也就是目前密鑰交換 + 簽名主流選擇:
RSA
密鑰交換(無需簽名);ECDHE
密鑰交換、RSA
簽名;ECDHE
密鑰交換、ECDSA
簽名;ECDH
密鑰交換、RSA
簽名;ECDH
密鑰交換、ECDSA
簽名;內置 ECDSA
公鑰的證書通常被稱之爲 ECC
證書,內置 RSA
公鑰的證書就是 RSA
證書。因爲 256 位 ECC Key
在安全性上等同於 3072 位 RSA Key
,加上 ECC
運算速度更快,ECDHE
密鑰交換 + ECDSA
數字簽名無疑是最好的選擇。因爲同等安全條件下,ECC
算法所需的 Key
更短,因此 ECC
證書文件體積比 RSA
證書要小一些。
RSA
證書能夠用於 RSA
密鑰交換(RSA
非對稱加密)或 ECDHE
密鑰交換(RSA
非對稱簽名);而 ECC
證書只能用於 ECDHE
密鑰交換(ECDSA
非對稱簽名)。
並非全部瀏覽器都支持 ECDHE
密鑰交換,也就是說 ECC
證書的兼容性要差一些。
"Forward Secrecy
" 或 "Perfect Forward Secrecy
-徹底正向保密" 協議描述了祕鑰協商(好比祕鑰交換)方法的特色。實際上這意味着及時你的服務器的祕鑰有危險,通信僅有可能被一類人竊聽,他們必須設法獲的每次會話都會生成的祕鑰對。
徹底正向保密是經過每次握手時爲祕鑰協商隨機生成密鑰對來完成(和全部會話一個 key
相反)。實現這個技術(提供徹底正向保密-Perfect Forward Secrecy
)的方法被稱爲 "ephemeral
"。
一般目前有2個方法用於完成徹底正向保密(Perfect Forward Secrecy
):
DHE
- 一個迪菲-赫爾曼密鑰交換密鑰協議(Diffie Hellman key-agreement protocol
)短暫(ephemeral
)版本。
ECDHE
- 一個橢圓曲線密鑰交換密鑰協議( Elliptic Curve Diffie Hellman key-agreement protocol
)短暫(ephemeral
)版本。
短暫(ephemeral
)方法有性能缺點,由於生成 key
很是耗費資源。
如下內容參考文獻:TLS/SSL 協議詳解 (30) SSL中的RSA、DHE、ECDHE、ECDH流程與區別
RSA
密鑰交換算法咱們先來看看傳統的祕鑰交換算法—— RSA
祕鑰交換算法。它是不符合徹底正向保密的,可是是咱們上面講的經典祕鑰交換。
RSA的核心涉及公鑰私鑰的概念
咱們構建這麼一種場景,服務器配置有公鑰+私鑰,客戶端是離散的。
RSA算法流程文字描述以下:
S
,使用收到的公鑰進行加密,生成 C
,把 C
發送到服務器。C
,使用公鑰對應的私鑰進行解密,獲得 S
。S
,S
爲密鑰(預主密鑰)。咱們來看看上述過程當中,爲什麼第三方沒法獲得 S
。首先第一步後,客戶端有公鑰,服務器有公鑰和私鑰。因爲公鑰是明文傳輸的,因此能夠假設第三方也有公鑰。
第二步後,客戶端發送 C
,服務器可以使用本身的私鑰進行解密,而第三方只有公鑰,沒法解密。即第三方沒法計算獲得 S
。
上述中,服務器發送的公鑰在 SSL
中是經過 certificate
報文發送的,certificate
中的包含了公鑰。C
是經過 Client key exchange
報文發送的。
其實,在實際 SSL
實際設計中,S
其實並無直接被當成密鑰加密,這裏爲了描述原理,省去了對 S
後續進行 KDF
哈希等操做,並不影響實際理解 RSA
。
RSA
有一個問題,就是若是私鑰泄漏,即私鑰被第三方知道,那麼第三方就能從 C
中解密獲得 S
,即只要保存全部的 A
和 B
的報文,等到私鑰被泄漏的那一天,或者有辦法快從 C
中計算 S
的方法出現(量子計算機分解大素數),那麼 A
和 B
就沒有什麼私密性可言了。
這就是所謂的前向不安全,私鑰參與了密鑰交換,安全性取決於私鑰是否安全保存。
DHE
密鑰交換算法DHE算法流程文字描述以下:
Xa
,使用 Xa
做爲指數,即計算 Pa = q^Xa mod p
,其中 q
和 p
是全世界公認的一對值。客戶端把 Pa
發送至服務器,Xa
做爲本身私鑰,僅且本身知道。Xb
,使用 Xb
做爲指數,計算 Pb = q^Xb mod p
,將結果 Pb
發送至客戶端,Xb
僅本身保存。Pb
後計算 Sa = Pb ^Xa mod p
;服務器收到 Pa
後計算 Sb = Pa^Xb mod p
Sa = Sb = S
,故密鑰交換成功,S
爲密鑰(預主密鑰)。上述途中,Sa
和 Sb
獲得的結果是相同的,即記爲 S
。
上述密鑰交換流程中,和 RSA
密鑰交換有較大不一樣,DHE
密鑰交換時,服務器私鑰沒有參與進來。也就是說,私鑰即便泄漏,也不會致使會話加密密鑰 S
被第三方解密。
實際使用過程當中,私鑰的功能被削弱到用來身份認證。
DHE
參數和 Pb
都是經過 server key exchange
發送給客戶端,Pa
經過 client key exchange
發送給服務器。server key exchange
的結尾處須要使用服務器私鑰對該報文自己進行簽名,以代表本身擁有私鑰。
ECDHE
密鑰交換算法只要理解 DHE
密鑰交換原理,那麼理解 ECDHE
密鑰交換原理其實並不難(若是不想深究的話)。
ECDHE
的運算是把 DHE
中模冪運算替換成了點乘運算,速度更快,可逆更難。
ECDHE
算法流程文字描述以下:
Ra
,計算 Pa(x, y) = Ra * Q(x, y)
,Q(x, y)
爲全世界公認的某個橢圓曲線算法的基點。將 Pa(x, y)
發送至服務器。Pb
,計算 Pb(x,y) = Rb * Q(x, y)
。將 Pb(x, y)
發送至客戶端。Sa(x, y) = Ra * Pb(x, y)
;服務器計算 Sb(x, y) = Rb *Pa(x, y)
Sa = Sb = S
,提取其中的 S
的 x
向量做爲密鑰(預主密鑰)。SSL
協議中,上圖中橢圓曲線名和 Pb
經過 server key exchange
報文發送;Pa
經過 client key exchange
報文發送。
ECDHE
與 ECDH
算法的區別字面少了一個 E
,E
表明了「臨時」,即在握手流程中,做爲服務器端,ECDH
少了一步計算 Pb
的過程,Pb
用證書中的公鑰代替,而證書對應的私鑰就是 Xb
。因而可知,使用 ECDH
密鑰交換算法,服務器必須採用 ECC
證書;服務器不發送 server key exchange
報文,由於發送 certificate
報文時,證書自己就包含了 Pb
信息。
ECDHE
與 RSA
的區別ECDHE(DHE)
算法屬於 DH
類密鑰交換算法, 私鑰不參與密鑰的協商,故即便私鑰泄漏,客戶端和服務器之間加密的報文都沒法被解密。因爲 ECDHE
每條會話都從新計算一個密鑰(Ra
、Rb
),故一條會話被解密後,其餘會話仍舊安全。
然而,ECDH
算法服務器端的私鑰是固定的,即證書的私鑰做爲 Rb
,故 ECDH
不被認爲前向安全,由於私鑰泄漏至關於 Rb
泄漏,Rb
泄漏,致使會話密鑰可被第三方計算。
如下內容來源:SSL協議中的加密套件
加密套件(CipherList
)是指在 ssl
通訊中,服務器和客戶端所使用的加密算法的組合。在 ssl
握手初期,客戶端將自身支持的加密套件列表發送給服務器;在握手階段,服務器根據本身的配置從中儘量的選出一個套件,做爲以後所要使用的加密方式。這些算法包括:認證算法、密鑰交換算法、對稱算法和摘要算法等。
每種加密套件的名字裏包含了四部分信息,分別是:
RSA
,Diffie-Hellman
,ECDH
,PSK
等DES 56/56
, RC2 56/128
, RC4 128/128
, AES 128/128
, AES 256/256
MAC
)算法,爲了防止握手自己被竄改(這裏極容易和證書籤名算法混淆)。算法包括MD5
,SHA
等。PRF
(僞隨機數函數),用於生成「master secret
」。例如 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
從其名字可知,它是:
TLS
協議的;ECDHE
、RSA
做爲密鑰交換算法與證書籤名類型;AES
(密鑰和初始向量的長度都是 256);MAC
算法(這裏就是哈希算法)是 SHA
。在客戶端和服務器端創建安全鏈接以前,雙方都必須指定適合本身的加密套件。加密套件的選擇能夠經過組合的字符串來控制。
服務器在選擇算法時,會有優先級,是以客戶端提供的的爲最優,仍是服務器端配置的爲最優。所謂的客戶端最優,就是根據客戶端提供的加密套件,從上到下,看是否有本地支持的,有的話則使用。所謂服務器端最優,就是服務器端根據自身配置的加密套件順序,一個個在 client hello
中找,找到了就使用。
其次,當服務器配置 ECC
證書時,加密套件只能選擇 XXX_ECDSA_XXX
或者 ECDH_XXX
。當服務器配置 RSA
證書時,只能選擇 RSA_XXX
或者 ECDHE_RSA_XXX
形式的加密套件。
須要注意的是,若是加密套件選擇 ECDH_RSA
或者 ECDH_ECDSA
時,因爲 ECDH
加密套件默認代表了握手須要 ECC
公鑰(即 ECC
證書的公鑰充當握手中 server key exchange
中的公鑰,證書的私鑰一樣也是握手過程當中的私鑰,握手過程不須要 server key exchange
),因此第二部分 _RSA
和 _ECDSA
代表的是想要的服務器證書籤名類型。
換句話說,CA
頒發的證書能夠是用 CA
本身的 RSA
私鑰進行簽名的 RSA
證書,也能夠用 CA
的 ECC
私鑰進行簽名的 ECC
證書。具體是哪一個,取決於 CA
部門和申請證書的類別。
證書內部存儲的 CSR
的公鑰是具體申請證書人員生成的,能夠是 RSA
算法的公鑰,也能夠是 ECC
算法的公鑰。取決於提交給 CA
以前生成的 CSR
方法。
好比說服務器選擇了 ECDH_RSA
加密套件,可是發送的證書倒是 ECDSA
簽名的證書,雖說證書籤名類型不影響整個握手,可是對於校驗嚴格的客戶端,這種狀況可能會致使客戶端斷開連接。
加密套件也能夠用字符串的形式,舉例:ALL:!ADH:RC4+RSA:+SSLv2:@STRENGTH
Openssl
定義了 4 中選擇符號:「+」,「-」,「!」,「@」。其中,「+」表示取交集;「-」表示臨時刪除一個算法;「!」表示永久刪除一個算法;「@「表示了排序方法。
多個描述之間能夠用「:」、「,」、「 」、「;」來分開。選擇加密套件的時候按照從左到的順序構成雙向鏈表,存放與內存中。
ALL:!ADH:RC4+RSA:+SSLv2:@STRENGTH
表示的意義是:
首先選擇全部的加密套件(不包含eNULL,即空對稱加密算法),而後在獲得的雙向鏈表之中去掉身份驗證採用 DH
的加密套件;加入包含 RC4
算法並將包含 RSA
的加密套件放在雙向鏈表的尾部;再將支持 SSLV2
的加密套件放在尾部;最後獲得的結果按照安全強度進行排序。
可使用命令 : openssl ciphers -V 'ALL:!ADH:RC4+RSA:+SSLv2:@STRENGTH' | column -t
來查看具體加密套件。