一、TLS 定義
SSL(Secure Sockets Layer) 安全套接層,是一種安全協(xié)議,經(jīng)歷了 SSL 1.0、2.0、3.0 版本后發(fā)展成了標(biāo)準(zhǔn)安全協(xié)議 - TLS(Transport Layer Security) 傳輸層安全性協(xié)議。TLS 有 1.0 (RFC 2246)、1.1(RFC 4346)、1.2(RFC 5246)、1.3(RFC 8446) 版本。
TLS 在實(shí)現(xiàn)上分為 記錄層 和 握手層 兩層,其中握手層又含四個(gè)子協(xié)議: 握手協(xié)議 (handshake protocol)、更改加密規(guī)范協(xié)議 (change cipher spec protocol)、應(yīng)用數(shù)據(jù)協(xié)議 (application data protocol) 和警告協(xié)議 (alert protocol)。
二、HTTPS = HTTP over TLS
只需配置瀏覽器和服務(wù)器相關(guān)設(shè)置開啟 TLS,即可實(shí)現(xiàn) HTTPS,TLS 高度解耦,可裝可卸,與上層高級(jí)應(yīng)用層協(xié)議相互協(xié)作又相互獨(dú)立。
三、加密
TLS/SSL 的功能實(shí)現(xiàn)主要依賴于三類基本算法:散列函數(shù) Hash、對(duì)稱加密和非對(duì)稱加密,其利用非對(duì)稱加密實(shí)現(xiàn)身份認(rèn)證和密鑰協(xié)商,對(duì)稱加密算法采用協(xié)商的密鑰對(duì)數(shù)據(jù)加密,基于散列函數(shù)驗(yàn)證信息的完整性。
TLS 的基本工作方式是,客戶端使用非對(duì)稱加密與服務(wù)器進(jìn)行通信,實(shí)現(xiàn)身份驗(yàn)證并協(xié)商對(duì)稱加密使用的密鑰,然后對(duì)稱加密算法采用協(xié)商密鑰對(duì)信息以及信息摘要進(jìn)行加密通信,不同的節(jié)點(diǎn)之間采用的對(duì)稱密鑰不同,從而可以保證信息只能通信雙方獲取。
例如,在 HTTPS 協(xié)議中,客戶端發(fā)出請(qǐng)求,服務(wù)端會(huì)將公鑰發(fā)給客戶端,客戶端驗(yàn)證過后生成一個(gè)密鑰再用公鑰加密后發(fā)送給服務(wù)端(非對(duì)稱加密),雙方會(huì)在 TLS 握手過程中生成一個(gè)協(xié)商密鑰(對(duì)稱密鑰),成功后建立加密連接。通信過程中客戶端將請(qǐng)求數(shù)據(jù)用協(xié)商密鑰加密后發(fā)送,服務(wù)端也用協(xié)商密鑰解密,響應(yīng)也用相同的協(xié)商密鑰。后續(xù)的通信使用對(duì)稱加密是因?yàn)閷?duì)稱加解密快,而握手過程中非對(duì)稱加密可以保證加密的有效性,但是過程復(fù)雜,計(jì)算量相對(duì)來說也大。
四、 記錄層
記錄協(xié)議負(fù)責(zé)在傳輸連接上交換的所有底層消息,并且可以配置加密。每一條 TLS 記錄以一個(gè)短標(biāo)頭開始。標(biāo)頭包含記錄內(nèi)容的類型 (或子協(xié)議)、協(xié)議版本和長(zhǎng)度。原始消息經(jīng)過分段 (或者合并)、壓縮、添加認(rèn)證碼、加密轉(zhuǎn)為 TLS 記錄的數(shù)據(jù)部分。
分片 (Fragmentation)
記錄層將信息塊分割成攜帶 2^14 字節(jié) (16KB) 或更小塊的數(shù)據(jù)的 TLSPlaintext 記錄。
記錄協(xié)議傳輸由其他協(xié)議層提交給它的不透明數(shù)據(jù)緩沖區(qū)。如果緩沖區(qū)超過記錄的長(zhǎng)度限制(2^14),記錄協(xié)議會(huì)將其切分成更小的片段。反過來也是可能的,屬于同一個(gè)子協(xié)議的小緩沖區(qū)也可以組合成一個(gè)單獨(dú)的記錄。struct {uint8 major, minor;} ProtocolVersion;enum {change_cipher_spec(20),alert(21),handshake(22),application_data(23), (255)} ContentType;struct {ContentType type; // 用于處理封閉片段的較高級(jí)協(xié)議ProtocolVersion version; // 使用的安全協(xié)議版本uint16 length; // TLSPlaintext.fragment 的長(zhǎng)度(以字節(jié)為單位),不超過 2^14opaque fragment[TLSPlaintext.length]; // 透明的應(yīng)用數(shù)據(jù),被視為獨(dú)立的塊,由類型字段指定的較高級(jí)協(xié)議處理} TLSPlaintext;
記錄壓縮和解壓縮 (Record compression and decompression)
壓縮算法將 TLSPlaintext 結(jié)構(gòu)轉(zhuǎn)換為 TLSCompressed 結(jié)構(gòu)。如果定義 CompressionMethod 為 null 表示不壓縮struct {ContentType type; // same as TLSPlaintext.typeProtocolVersion version; // same as TLSPlaintext.versionuint16 length; // TLSCompressed.fragment 的長(zhǎng)度,不超過 2^14 + 1024opaque fragment[TLSCompressed.length];} TLSCompressed;
空或標(biāo)準(zhǔn)流加密 (Null or standard stream cipher)
流加密(BulkCipherAlgorithm)將 TLSCompressed.fragment 結(jié)構(gòu)轉(zhuǎn)換為流 TLSCiphertext.fragment 結(jié)構(gòu)stream-ciphered struct {opaque content[TLSCompressed.length];opaque MAC[CipherSpec.hash_size];} GenericStreamCipher;
MAC 產(chǎn)生方法如下:HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment));
seq_num(記錄的序列號(hào))、hash(SecurityParameters.mac_algorithm 指定的哈希算法)
MAC(Message authentication code) - 消息認(rèn)證碼
注意,MAC 是在加密之前計(jì)算的。流加密加密整個(gè)塊,包括 MAC。對(duì)于不使用同步向量 (例如 RC4) 的流加密,從一個(gè)記錄結(jié)尾處的流加密狀態(tài)僅用于后續(xù)數(shù)據(jù)包。如果 CipherSuite 是 TLS_NULL_WITH_NULL_NULL,則加密由身份操作 (數(shù)據(jù)未加密,MAC 大小為零,暗示不使用 MAC) 組成。TLSCiphertext.length 是 TLSCompressed.length 加上 CipherSpec.hash_size。
CBC 塊加密 (分組加密)
塊加密(如 RC2 或 DES),將 TLSCompressed.fragment 結(jié)構(gòu)轉(zhuǎn)換為塊 TLSCiphertext.fragment 結(jié)構(gòu)block-ciphered struct {opaque content[TLSCompressed.length];opaque MAC[CipherSpec.hash_size];uint8 padding[GenericBlockCipher.padding_length];uint8 padding_length;} GenericBlockCipher;
padding: 添加的填充將明文長(zhǎng)度強(qiáng)制為塊密碼塊長(zhǎng)度的整數(shù)倍。填充可以是長(zhǎng)達(dá) 255 字節(jié)的任何長(zhǎng)度,只要滿足 TLSCiphertext.length 是塊長(zhǎng)度的整數(shù)倍。長(zhǎng)度大于需要的值可以阻止基于分析交換信息長(zhǎng)度的協(xié)議攻擊。填充數(shù)據(jù)向量中的每個(gè) uint8 必須填入填充長(zhǎng)度值 (即 padding_length)。
padding_length: 填充長(zhǎng)度應(yīng)該使得 GenericBlockCipher 結(jié)構(gòu)的總大小是加密塊長(zhǎng)度的倍數(shù)。合法值范圍從零到 255(含)。該長(zhǎng)度指定 padding_length 字段本身除外的填充字段的長(zhǎng)度
加密塊的數(shù)據(jù)長(zhǎng)度(TLSCiphertext.length)是 TLSCompressed.length,CipherSpec.hash_size 和 padding_length 的總和加一
示例: 如果塊長(zhǎng)度為 8 字節(jié),壓縮內(nèi)容長(zhǎng)度(TLSCompressed.length)為 61 字節(jié),MAC 長(zhǎng)度為 20 字節(jié),則填充前的長(zhǎng)度為 82 字節(jié)(padding_length 占 1 字節(jié))。 因此,為了使總長(zhǎng)度為塊長(zhǎng)度 (8 字節(jié)) 的偶數(shù)倍,模 8 的填充長(zhǎng)度必須等于 6,所以填充長(zhǎng)度可以為 6,14,22 等。如果填充長(zhǎng)度是需要的最小值,比如 6,填充將為 6 字節(jié),每個(gè)塊都包含值 6。因此,塊加密之前的 GenericBlockCipher 的最后 8 個(gè)八位字節(jié)將為 xx 06 06 06 06 06 06 06,其中 xx 是 MAC 的最后一個(gè)八位字節(jié)。XX - 06 06 06 06 06 06 - 06MAC - padding[6] - padding_length
記錄有效載荷保護(hù) (Record payload protection)
加密和 MAC 功能將 TLSCompressed 結(jié)構(gòu)轉(zhuǎn)換為 TLSCiphertext。記錄的 MAC 還包括序列號(hào),以便可以檢測(cè)到丟失,額外或重復(fù)的消息。struct {ContentType type; // sameProtocolVersion version; // sameuint16 length; // TLSCiphertext.fragment 的長(zhǎng)度,不超過 2^14 + 2048select (CipherSpec.cipher_type) {case stream: GenericStreamCipher;case block: GenericBlockCipher;} fragment; // TLSCompressed.fragment 的加密形式,帶有 MAC} TLSCiphertext;
注意 這里提到的都是先 MAC 再加密,是基于 RFC 2246 的方案 (TLS 1.0) 寫的。但新的方案選擇先加密再 MAC,這種替代方案中,首先對(duì)明文和填充進(jìn)行加密,再將結(jié)果交給 MAC 算法。這可以保證主動(dòng)網(wǎng)絡(luò)攻擊者不能操縱任何加密數(shù)據(jù)。
密鑰計(jì)算 (Key calculation)
記錄協(xié)議需要一種算法,從握手協(xié)議提供的安全性參數(shù)生成密鑰、IV 和 MAC secret.
主密鑰 (Master secret): 在連接中雙方共享的一個(gè) 48 字節(jié)的密鑰
客戶隨機(jī)數(shù) (client random): 由客戶端提供的 32 字節(jié)值
服務(wù)器隨機(jī)數(shù) (server random): 由服務(wù)器提供的 32 字節(jié)值
五、握手層
握手協(xié)議的職責(zé)是生成通信過程所需的共享密鑰和進(jìn)行身份認(rèn)證。這部分使用無密碼套件,為防止數(shù)據(jù)被竊聽,通過公鑰密碼或 Diffie-Hellman 密鑰交換技術(shù)通信。
密碼規(guī)格變更協(xié)議,用于密碼切換的同步,是在握手協(xié)議之后的協(xié)議。握手協(xié)議過程中使用的協(xié)議是“不加密”這一密碼套件,握手協(xié)議完成后則使用協(xié)商好的密碼套件。
警告協(xié)議,當(dāng)發(fā)生錯(cuò)誤時(shí)使用該協(xié)議通知通信對(duì)方,如握手過程中發(fā)生異常、消息認(rèn)證碼錯(cuò)誤、數(shù)據(jù)無法解壓縮等。
應(yīng)用數(shù)據(jù)協(xié)議,通信雙方真正進(jìn)行應(yīng)用數(shù)據(jù)傳輸?shù)膮f(xié)議,傳送過程通過 TLS 應(yīng)用數(shù)據(jù)協(xié)議和 TLS 記錄協(xié)議來進(jìn)行傳輸。
握手是 TLS 協(xié)議中最精密復(fù)雜的部分。在這個(gè)過程中,通信雙方協(xié)商連接參數(shù),并且完成身 份驗(yàn)證。根據(jù)使用的功能的不同,整個(gè)過程通常需要交換 6~10 條消息。根據(jù)配置和支持的協(xié)議擴(kuò)展的不同,交換過程可能有許多變種。在使用中經(jīng)??梢杂^察到以下三種流程:(1) 完整的握手, 對(duì)服務(wù)器進(jìn)行身份驗(yàn)證;(2) 恢復(fù)之前的會(huì)話采用的簡(jiǎn)短握手;(3) 對(duì)客戶端和服務(wù)器都進(jìn)行身份驗(yàn)證的握手。
握手協(xié)議消息的標(biāo)頭信息包含消息類型(1 字節(jié))和長(zhǎng)度(3 字節(jié)),余下的信息則取決于消息類型:struct {HandshakeType msg_type;uint24 length;HandshakeMessage message;} Handshake;
1、完整握手
每一個(gè) TLS 連接都會(huì)以握手開始。如果客戶端此前并未與服務(wù)器建立會(huì)話,那么雙方會(huì)執(zhí)行一次完整的握手流程來協(xié)商 TLS 會(huì)話。握手過程中,客戶端和服務(wù)器將進(jìn)行以下四個(gè)主要步驟:
交換各自支持的功能,對(duì)需要的連接參數(shù)達(dá)成一致
驗(yàn)證出示的證書,或使用其他方式進(jìn)行身份驗(yàn)證
對(duì)將用于保護(hù)會(huì)話的共享主密鑰達(dá)成一致
驗(yàn)證握手消息并未被第三方團(tuán)體修改
下面介紹最常見的握手規(guī)則,一種不需要驗(yàn)證客戶端身份但需要驗(yàn)證服務(wù)器身份的握手:
a、ClientHello
這條消息將客戶端的功能和首選項(xiàng)傳送給服務(wù)器。
Version: 協(xié)議版本(protocol version)指示客戶端支持的最佳協(xié)議版本
Random: 一個(gè) 32 字節(jié)數(shù)據(jù),28 字節(jié)是隨機(jī)生成的 (圖中的 Random Bytes);剩余的 4 字節(jié)包含額外的信息,與客戶端時(shí)鐘有關(guān) (圖中使用的是 GMT Unix Time)。在握手時(shí),客戶端和服務(wù)器都會(huì)提供隨機(jī)數(shù),客戶端的暫記作 random_C (用于后續(xù)的密鑰的生成)。這種隨機(jī)性對(duì)每次握手都是獨(dú)一無二的,在身份驗(yàn)證中起著舉足輕重的作用。它可以防止重放攻擊,并確認(rèn)初始數(shù)據(jù)交換的完整性。
Session ID: 在第一次連接時(shí),會(huì)話 ID(session ID)字段是空的,這表示客戶端并不希望恢復(fù)某個(gè)已存在的會(huì)話。典型的會(huì)話 ID 包含 32 字節(jié)隨機(jī)生成的數(shù)據(jù),一般由服務(wù)端生成通過 ServerHello 返回給客戶端。
Cipher Suites: 密碼套件(cipher suite)塊是由客戶端支持的所有密碼套件組成的列表,該列表是按優(yōu)先級(jí)順序排列的
Compression: 客戶端可以提交一個(gè)或多個(gè)支持壓縮的方法。默認(rèn)的壓縮方法是 null,代表沒有壓縮
Extensions: 擴(kuò)展(extension)塊由任意數(shù)量的擴(kuò)展組成。這些擴(kuò)展會(huì)攜帶額外數(shù)據(jù)
b、ServerHello
是將服務(wù)器選擇的連接參數(shù)傳回客戶端。
這個(gè)消息的結(jié)構(gòu)與 ClientHello 類似,只是每個(gè)字段只包含一個(gè)選項(xiàng),其中包含服務(wù)端的 random_S 參數(shù) (用于后續(xù)的密鑰協(xié)商)。服務(wù)器無需支持客戶端支持的最佳版本。如果服務(wù)器不支持與客戶端相同的版本,可以提供某個(gè)其他版本以期待客戶端能夠接受。
圖中的 Cipher Suite 是后續(xù)密鑰協(xié)商和身份驗(yàn)證要用的加密套件,此處選擇的密鑰交換與簽名算法是 ECDHE_RSA,對(duì)稱加密算法是 AES-GCM,后面會(huì)講到這個(gè)。
還有一點(diǎn)默認(rèn)情況下 TLS 壓縮都是關(guān)閉的,因?yàn)镃RIME攻擊會(huì)利用 TLS 壓縮恢復(fù)加密認(rèn)證 cookie,實(shí)現(xiàn)會(huì)話劫持,而且一般配置 gzip 等內(nèi)容壓縮后再壓縮 TLS 分片效益不大又額外占用資源,所以一般都關(guān)閉 TLS 壓縮。
c、Certificate
典型的 Certificate 消息用于攜帶服務(wù)器 X.509證書鏈。 服務(wù)器必須保證它發(fā)送的證書與選擇的算法套件一致。比方說,公鑰算法與套件中使用的必須匹配。除此以外,一些密鑰交換算法依賴嵌入證書的特定數(shù)據(jù),而且要求證書必須以客戶端支持的算法簽名。所有這些都表明服務(wù)器需要配置多個(gè)證書(每個(gè)證書可能會(huì)配備不同的證書鏈)。
Certificate 消息是可選的,因?yàn)椴⒎撬刑准际褂蒙矸蒡?yàn)證,也并非所有身份驗(yàn)證方法都需要證書。更進(jìn)一步說,雖然消息默認(rèn)使用 X.509 證書,但是也可以攜帶其他形式的標(biāo)志;一些套件就依賴PCG密鑰。
d、ServerKeyExchange
攜帶密鑰交換需要的額外數(shù)據(jù)。ServerKeyExchange 是可選的,消息內(nèi)容對(duì)于不同的協(xié)商算法套件會(huì)存在差異。部分場(chǎng)景下,比如使用 RSA 算法時(shí),服務(wù)器不需要發(fā)送此消息。
ServerKeyExchange 僅在服務(wù)器證書消息(也就是上述 Certificate 消息)不包含足夠的數(shù)據(jù)以允許客戶端交換預(yù)主密鑰(premaster secret)時(shí)才由服務(wù)器發(fā)送。
比如基于 DH 算法的握手過程中,需要單獨(dú)發(fā)送一條 ServerKeyExchange 消息帶上 DH 參數(shù):
e、ServerHelloDone
表明服務(wù)器已經(jīng)將所有預(yù)計(jì)的握手消息發(fā)送完畢。在此之后,服務(wù)器會(huì)等待客戶端發(fā)送消息。
f、verify certificate
客戶端驗(yàn)證證書的合法性,如果驗(yàn)證通過才會(huì)進(jìn)行后續(xù)通信,否則根據(jù)錯(cuò)誤情況不同做出提示和操作,合法性驗(yàn)證內(nèi)容包括如下:
證書鏈的可信性 trusted certificate path;
證書是否吊銷 revocation,有兩類方式 - 離線 CRL 與在線 OCSP,不同的客戶端行為會(huì)不同;
有效期 expiry date,證書是否在有效時(shí)間范圍;
域名 domain,核查證書域名是否與當(dāng)前的訪問域名匹配;
由 PKI 體系 的內(nèi)容可知,對(duì)端發(fā)來的證書簽名是 CA 私鑰加密的,接收到證書后,先讀取證書中的相關(guān)的明文信息,采用相同的散列函數(shù)計(jì)算得到信息摘要,然后利用對(duì)應(yīng) CA 的公鑰解密簽名數(shù)據(jù),對(duì)比證書的信息摘要,如果一致,則可以確認(rèn)證書的合法性;然后去查詢證書的吊銷情況等。
g、 ClientKeyExchange
合法性驗(yàn)證通過之后,客戶端計(jì)算產(chǎn)生隨機(jī)數(shù)字的預(yù)主密鑰(Pre-master),并用證書公鑰加密,發(fā)送給服務(wù)器并攜帶客戶端為密鑰交換提供的所有信息。這個(gè)消息受協(xié)商的密碼套件的影響,內(nèi)容隨著不同的協(xié)商密碼套件而不同。
此時(shí)客戶端已經(jīng)獲取全部的計(jì)算協(xié)商密鑰需要的信息: 兩個(gè)明文隨機(jī)數(shù) random_C 和 random_S 與自己計(jì)算產(chǎn)生的 Pre-master,然后得到協(xié)商密鑰(用于之后的消息加密)。enc_key = PRF(Pre_master, 'master secret', random_C + random_S)
圖中使用的是 ECDHE 算法,ClientKeyExchange 傳遞的是 DH 算法的客戶端參數(shù),如果使用的是 RSA 算法則此處應(yīng)該傳遞加密的預(yù)主密鑰。
h、ChangeCipherSpec
通知服務(wù)器后續(xù)的通信都采用協(xié)商的通信密鑰和加密算法進(jìn)行加密通信。
注意:ChangeCipherSpec 不屬于握手消息,它是另一種協(xié)議,只有一條消息,作為它的子協(xié)議進(jìn)行實(shí)現(xiàn)。
i、Finished (Encrypted Handshake Message)
Finished 消息意味著握手已經(jīng)完成。消息內(nèi)容將加密,以便雙方可以安全地交換驗(yàn)證整個(gè)握手完整性所需的數(shù)據(jù)。
這個(gè)消息包含 verify_data 字段,它的值是握手過程中所有消息的散列值。這些消息在連接兩端都按照各自所見的順序排列,并以協(xié)商得到的主密鑰 (enc_key) 計(jì)算散列。這個(gè)過程是通過一個(gè)偽隨機(jī)函數(shù)(pseudorandom function,PRF)來完成的,這個(gè)函數(shù)可以生成任意數(shù)量的偽隨機(jī)數(shù)據(jù)。 兩端的計(jì)算方法一致,但會(huì)使用不同的標(biāo)簽(finished_label):客戶端使用 client finished,而服務(wù)器則使用 server finished。verify_data = PRF(master_secret, finished_label, Hash(handshake_messages))
因?yàn)?Finished 消息是加密的,并且它們的完整性由協(xié)商 MAC 算法保證,所以主動(dòng)網(wǎng)絡(luò)攻擊者不能改變握手消息并對(duì) vertify_data 的值造假。在 TLS 1.2 版本中,F(xiàn)inished 消息的長(zhǎng)度默認(rèn)是 12 字節(jié)(96 位),并且允許密碼套件使用更長(zhǎng)的長(zhǎng)度。在此之前的版本,除了 SSL 3 使用 36 字節(jié)的定長(zhǎng)消息,其他版本都使用 12 字節(jié)的定長(zhǎng)消息。
j、Server
服務(wù)器用私鑰解密加密的 Pre-master 數(shù)據(jù),基于之前交換的兩個(gè)明文隨機(jī)數(shù) random_C 和 random_S,同樣計(jì)算得到協(xié)商密鑰: enc_key = PRF(Pre_master, 'master secret', random_C + random_S);
同樣計(jì)算之前所有收發(fā)信息的 hash 值,然后用協(xié)商密鑰解密客戶端發(fā)送的 verify_data_C,驗(yàn)證消息正確性;
k、change_cipher_spec
服務(wù)端驗(yàn)證通過之后,服務(wù)器同樣發(fā)送 change_cipher_spec 以告知客戶端后續(xù)的通信都采用協(xié)商的密鑰與算法進(jìn)行加密通信(圖中多了一步 New Session Ticket,此為會(huì)話票證,會(huì)在會(huì)話恢復(fù)中解釋);
l、Finished (Encrypted Handshake Message)
服務(wù)器也結(jié)合所有當(dāng)前的通信參數(shù)信息生成一段數(shù)據(jù) (verify_data_S) 并采用協(xié)商密鑰 session secret (enc_key) 與算法加密并發(fā)送到客戶端;
m、握手結(jié)束
客戶端計(jì)算所有接收信息的 hash 值,并采用協(xié)商密鑰解密 verify_data_S,驗(yàn)證服務(wù)器發(fā)送的數(shù)據(jù)和密鑰,驗(yàn)證通過則握手完成;
n、 加密通信
開始使用協(xié)商密鑰與算法進(jìn)行加密通信。
2、密鑰交換和簽名算法
常用的密鑰交換和簽名算法
HTTPS 通過 TLS 層和證書機(jī)制提供了內(nèi)容加密、身份認(rèn)證和數(shù)據(jù)完整性三大功能。加密過程中,需要用到非對(duì)稱密鑰交換和對(duì)稱內(nèi)容加密兩大算法。
對(duì)稱內(nèi)容加密強(qiáng)度非常高,加解密速度也很快,只是無法安全地生成和保管密鑰。在 TLS 協(xié)議中,最后的應(yīng)用數(shù)據(jù)都是經(jīng)過對(duì)稱加密后傳輸?shù)?,傳輸中所使用的?duì)稱協(xié)商密鑰(上文中的 enc_key),則是在握手階段通過非對(duì)稱密鑰交換而來。常見的 AES-GCM、ChaCha20-Poly1305,都是對(duì)稱加密算法。
非對(duì)稱密鑰交換能在不安全的數(shù)據(jù)通道中,產(chǎn)生只有通信雙方才知道的對(duì)稱加密密鑰。目前最常用的密鑰交換算法有 RSA 和 ECDHE。
RSA 歷史悠久,支持度好,但不支持完美前向安全 - PFS(Perfect Forward Secrecy);而 ECDHE 是使用了 ECC(橢圓曲線)的 DH(Diffie-Hellman)算法,計(jì)算速度快,且支持 PFS。
在 PKI 體系 一節(jié)中說明了僅有非對(duì)稱密鑰交換還是無法抵御 MITM 攻擊的,所以需要引入了 PKI 體系的證書來進(jìn)行身份驗(yàn)證,其中服務(wù)端非對(duì)稱加密產(chǎn)生的公鑰會(huì)放在證書中傳給客戶端。
在 RSA 密鑰交換中,瀏覽器使用證書提供的 RSA 公鑰加密相關(guān)信息,如果服務(wù)端能解密,意味著服務(wù)端擁有與公鑰對(duì)應(yīng)的私鑰,同時(shí)也能算出對(duì)稱加密所需密鑰。密鑰交換和服務(wù)端認(rèn)證合并在一起。
在 ECDH 密鑰交換中,服務(wù)端使用私鑰 (RSA 或 ECDSA) 對(duì)相關(guān)信息進(jìn)行簽名,如果瀏覽器能用證書公鑰驗(yàn)證簽名,就說明服務(wù)端確實(shí)擁有對(duì)應(yīng)私鑰,從而完成了服務(wù)端認(rèn)證。密鑰交換則是各自發(fā)送 DH 參數(shù)完成的,密鑰交換和服務(wù)端認(rèn)證是完全分開的。
可用于 ECDHE 數(shù)字簽名的算法主要有 RSA 和 ECDSA - 橢圓曲線數(shù)字簽名算法,也就是目前密鑰交換 + 簽名有三種主流選擇:
RSA - RSA 密鑰交換(無需簽名)
ECDHE_RSA - ECDHE 密鑰交換、RSA 簽名
ECDHE_ECDSA - ECDHE 密鑰交換、ECDSA 簽名
比如我的網(wǎng)站使用的加密套件是 ECDHE_RSA,可以看到數(shù)字簽名算法是 sha256 哈希加 RSA 加密,在 PKI 體系 一節(jié)中講了簽名是服務(wù)器信息摘要的哈希值加密生成的。
內(nèi)置 ECDSA 公鑰的證書一般被稱之為 ECC 證書,內(nèi)置 RSA 公鑰的證書就是 RSA 證書。因?yàn)?256 位 ECC Key 在安全性上等同于 3072 位 RSA Key,所以 ECC 證書體積比 RSA 證書小,而且 ECC 運(yùn)算速度更快,ECDHE 密鑰交換 + ECDSA 數(shù)字簽名是目前最好的加密套件。
RSA 密鑰交換和 DH 密鑰交換的區(qū)別
使用 RSA 進(jìn)行密鑰交換的握手過程與前面說明的基本一致,只是沒有 ServerKeyExchange 消息,其中協(xié)商密鑰涉及到三個(gè)參數(shù) (客戶端隨機(jī)數(shù) random_C、服務(wù)端隨機(jī)數(shù) random_S、預(yù)主密鑰 Premaster secret), 其中前兩個(gè)隨機(jī)數(shù)和協(xié)商使用的算法是明文的很容易獲取,最后一個(gè) Premaster secret 會(huì)用服務(wù)器提供的公鑰加密后傳輸給服務(wù)器 (密鑰交換),如果這個(gè)預(yù)主密鑰被截取并破解則協(xié)商密鑰也可以被破解。
RSA 的算法核心思想:是利用了極大整數(shù) 因數(shù)分解 的計(jì)算復(fù)雜性。
而使用 DH(Diffie-Hellman) 算法 進(jìn)行密鑰交換,雙方只要交換各自的 DH 參數(shù)(在 ServerKeyExchange 發(fā)送 Server params,在 ClientKeyExchange 發(fā)送 Client params),不需要傳遞 Premaster secret,就可以各自算出這個(gè)預(yù)主密鑰。
DH 的握手過程如下,大致過程與 RSA 類似,圖中只表達(dá)如何生成預(yù)主密鑰:
服務(wù)器通過私鑰將客戶端隨機(jī)數(shù) random_C,服務(wù)端隨機(jī)數(shù) random_S,服務(wù)端 DH 參數(shù) Server params 簽名生成 signature,然后在 ServerKeyExchange 消息中發(fā)送服務(wù)端 DH 參數(shù)和該簽名;
客戶端收到后用服務(wù)器給的公鑰解密驗(yàn)證簽名,并在 ClientKeyExchange 消息中發(fā)送客戶端 DH 參數(shù) Client params;
服務(wù)端收到后,雙方都有這兩個(gè)參數(shù),再各自使用這兩個(gè)參數(shù)生成預(yù)主密鑰 Premaster secret,之后的協(xié)商密鑰等步驟與 RSA 基本一致。
基于 RSA 算法與 DH 算法的握手最大的區(qū)別就在于密鑰交換與身份認(rèn)證。前者客戶端使用公鑰加密預(yù)主密鑰并發(fā)送給服務(wù)端完成密鑰交換,服務(wù)端利用私鑰解密完成身份認(rèn)證。后者利用各自發(fā)送的 DH 參數(shù)完成密鑰交換,服務(wù)器私鑰簽名數(shù)據(jù),客戶端公鑰驗(yàn)簽完成身份認(rèn)證。
其核心思想是利用了 離散對(duì)數(shù)問題 的計(jì)算復(fù)雜性
原根:假設(shè)一個(gè)整數(shù) g 對(duì)于質(zhì)數(shù) P 來說是原根,那么 g^i mod P (1 ≦ i < P) 的結(jié)果各不相同,且其結(jié)果按一定順序排列后是 1 到 P-1 的所有整數(shù)
離散對(duì)數(shù):如果對(duì)于一個(gè)整數(shù) n 和質(zhì)數(shù) P 的一個(gè)原根 g,可以找到一個(gè)唯一的指數(shù) i,使得 n = g^i mod P (0 ≦ i < P),那么指數(shù) i 稱為 n 的以 g 為基數(shù)的模 P 的離散對(duì)數(shù)
Diffie-Hellman 算法的有效性依賴于計(jì)算離散對(duì)數(shù)的難度,其含義是:當(dāng)已知大素?cái)?shù) P 和它的一個(gè)原根 g 后,對(duì)給定的 n,要計(jì)算 i,被認(rèn)為是很困難的,而給定 i 計(jì)算 n 卻相對(duì)容易
算法過程可以抽象成下圖:
雙方預(yù)先商定好了一對(duì) P g 值 (公開的),而 Alice 有一個(gè)私密數(shù) a(非公開,對(duì)應(yīng)一個(gè)私鑰),Bob 有一個(gè)私密數(shù) b(非公開,對(duì)應(yīng)一個(gè)私鑰)
Alice 計(jì)算 A = g^a mod P,并把 A(公開,對(duì)應(yīng)一個(gè)公鑰) 發(fā)給 Bob
Bob 計(jì)算 B = g^b mod P,并把 B(公開,對(duì)應(yīng)一個(gè)公鑰) 發(fā)給 Alice
雙方計(jì)算出共享密鑰,K = B^a mod P = A^b mod P (= g^ab mod P)
對(duì)于 Alice 和 Bob 來說通過對(duì)方發(fā)過來的公鑰參數(shù)和自己手中的私鑰可以得到最終相同的密鑰
而第三方最多知道 P g A B,想得到私鑰和最后的密鑰很困難,當(dāng)然前提是 a b P 足夠大 (RFC3526 文檔中有幾個(gè)常用的大素?cái)?shù)可供使用),否則暴力破解也有可能試出答案,至于 g 一般取個(gè)較小值就可以
如下幾張圖是實(shí)際 DH 握手發(fā)送的內(nèi)容:
可以看到雙方發(fā)給對(duì)方的參數(shù)中攜帶了一個(gè)公鑰值,對(duì)應(yīng)上述的 A 和 B
而且實(shí)際用的加密套件是橢圓曲線 DH 密鑰交換 (ECDH),利用由橢圓曲線加密建立公鑰與私鑰對(duì)可以更進(jìn)一步加強(qiáng) DH 的安全性,因?yàn)槟壳敖鉀Q橢圓曲線離散對(duì)數(shù)問題要比因式分解困難的多,而且 ECC 使用的密鑰長(zhǎng)度比 RSA 密鑰短得多(目前 RSA 密鑰需要 2048 位以上才能保證安全,而 ECC 密鑰 256 位就足夠)
3、客戶端身份驗(yàn)證
盡管可以選擇對(duì)任意一端進(jìn)行身份驗(yàn)證,但人們幾乎都啟用了對(duì)服務(wù)器的身份驗(yàn)證。如果服務(wù)器選擇的套件不是匿名的,那么就需要在 Certificate 消息中跟上自己的證書。
相比之下,服務(wù)器通過發(fā)送 CertificateRequest 消息請(qǐng)求對(duì)客戶端進(jìn)行身份驗(yàn)證。消息中列出所有可接受的客戶端證書。作為響應(yīng),客戶端發(fā)送自己的 Certificate 消息(使用與服務(wù)器發(fā)送證書相同的格式),并附上證書。此后,客戶端發(fā)送 CertificateVerify 消息,證明自己擁有對(duì)應(yīng)的私鑰。
只有已經(jīng)過身份驗(yàn)證的服務(wù)器才被允許請(qǐng)求客戶端身份驗(yàn)證?;谶@個(gè)原因,這個(gè)選項(xiàng)也被稱為相互身份驗(yàn)證(mutual authentication)。
a、CertificateRequest
在 ServerHello 的過程中發(fā)出,請(qǐng)求對(duì)客戶端進(jìn)行身份驗(yàn)證,并將其接受的證書的公鑰和簽名算法傳送給客戶端。
它也可以選擇發(fā)送一份自己接受的證書頒發(fā)機(jī)構(gòu)列表,這些機(jī)構(gòu)都用其可分辨名稱來表示:struct {ClientCertificateType certificate_types;SignatureAndHashAlgorithm supported_signature_algorithms;DistinguishedName certificate_authorities;} CertificateRequest;
b、CertificateVerify
在 ClientKeyExchange 的過程中發(fā)出,證明自己擁有的私鑰與之前發(fā)送的客戶端證書中的公鑰匹配。消息中包含一條到這一步為止的所有握手消息的簽名:struct {Signature handshake_messages_signature;} CertificateVerify;
4、會(huì)話恢復(fù)
最初的會(huì)話恢復(fù)機(jī)制是,在一次完整協(xié)商的連接斷開時(shí),客戶端和服務(wù)器都會(huì)將會(huì)話的安全參數(shù)保存一段時(shí)間。希望使用會(huì)話恢復(fù)的服務(wù)器為會(huì)話指定唯一的標(biāo)識(shí),稱為會(huì)話 ID(Session ID)。服務(wù)器在 ServerHello 消息中將會(huì)話 ID 發(fā)回客戶端。
希望恢復(fù)早先會(huì)話的客戶端將適當(dāng)?shù)?Session ID 放入 ClientHello 消息,然后提交。服務(wù)器如果同意恢復(fù)會(huì)話,就將相同的 Session ID 放入 ServerHello 消息返回,接著使用之前協(xié)商的主密鑰生成一套新的密鑰,再切換到加密模式,發(fā)送 Finished 消息。 客戶端收到會(huì)話已恢復(fù)的消息以后,也進(jìn)行相同的操作。這樣的結(jié)果是握手只需要一次網(wǎng)絡(luò)往返。
Session ID 由服務(wù)器端支持,協(xié)議中的標(biāo)準(zhǔn)字段,因此基本所有服務(wù)器都支持,服務(wù)器端保存會(huì)話 ID 以及協(xié)商的通信信息,占用服務(wù)器資源較多。
用來替代服務(wù)器會(huì)話緩存和恢復(fù)的方案是使用會(huì)話票證(Session ticket)。使用這種方式,除了所有的狀態(tài)都保存在客戶端(與 HTTP Cookie 的原理類似)之外,其消息流與服務(wù)器會(huì)話緩存是一樣的。
其思想是服務(wù)器取出它的所有會(huì)話數(shù)據(jù)(狀態(tài))并進(jìn)行加密 (密鑰只有服務(wù)器知道),再以票證的方式發(fā)回客戶端。在接下來的連接中,客戶端恢復(fù)會(huì)話時(shí)在 ClientHello 的擴(kuò)展字段 session_ticket 中攜帶加密信息將票證提交回服務(wù)器,由服務(wù)器檢查票證的完整性,解密其內(nèi)容,再使用其中的信息恢復(fù)會(huì)話。
這種方法有可能使擴(kuò)展服務(wù)器集群更為簡(jiǎn)單,因?yàn)槿绻皇褂眠@種方式,就需要在服務(wù)集群的各個(gè)節(jié)點(diǎn)之間同步會(huì)話。 Session ticket 需要服務(wù)器和客戶端都支持,屬于一個(gè)擴(kuò)展字段,占用服務(wù)器資源很少。
警告:會(huì)話票證破壞了 TLS 安全模型。它使用票證密鑰加密的會(huì)話狀態(tài)并將其暴露在線路上。有些實(shí)現(xiàn)中的票證密鑰可能會(huì)比連接使用的密碼要弱。如果票證密鑰被暴露,就可以解密連接上的全部數(shù)據(jù)。因此,使用會(huì)話票證時(shí),票證密鑰需要頻繁輪換。
聯(lián)系客服