2.1 記錄協(xié)議
宏觀上,TLS以記錄協(xié)議(record protocol)實(shí)現(xiàn)。記錄協(xié)議負(fù)責(zé)在傳輸連接上交換所有底層消息,并可以配置加密。每一條TLS記錄以一個(gè)短標(biāo)頭起始。標(biāo)頭包含記錄內(nèi)容的類型(或子協(xié)議)、協(xié)議版本和長(zhǎng)度。消息數(shù)據(jù)緊跟在標(biāo)頭之后。每一個(gè)TLS記錄指定有唯一的64位序列號(hào),但不會(huì)在線路上傳輸。任一端都有自身的序列號(hào)并跟蹤來(lái)自另一端記錄的數(shù)量。這些值是對(duì)抗重放攻擊的一部分。
記錄協(xié)議從多個(gè)重要的宏觀角度對(duì)通信進(jìn)行考量,是一個(gè)很有用的協(xié)議抽象。
- 消息傳輸
記錄協(xié)議傳輸有其他協(xié)議層提交給它的不透明數(shù)據(jù)緩沖區(qū)。如果緩沖區(qū)超過(guò)記錄的長(zhǎng)度限制(16384字節(jié)),記錄協(xié)議會(huì)將其切分成更小的片段。反過(guò)來(lái)也是有可能的,屬于同一個(gè)子協(xié)議的小緩沖區(qū)也可以組合成一個(gè)單獨(dú)的記錄。 - 加密以及完整性驗(yàn)證
在一個(gè)剛建立起來(lái)的連接上,最初的消息傳輸沒有受到任何保護(hù)(從技術(shù)上講,就是使用了TLS_NULL_WITH_NULL_NULL密碼套件)。這是必需的,否則第一次協(xié)商就無(wú)法進(jìn)行。但是,一旦握手完成,記錄層就開始按照協(xié)商取得的連接參數(shù)進(jìn)行加密和完整性驗(yàn)證。 - 壓縮
理論上,在加密之前透明地進(jìn)行壓縮非常好,可是在實(shí)踐中幾乎沒有人重要做。這主要是因?yàn)槊總€(gè)應(yīng)用在HTTP層就以及對(duì)它們的出口流量進(jìn)行過(guò)壓縮。這個(gè)特性在2012年遭受過(guò)一次嚴(yán)重打擊,當(dāng)時(shí)CRMIE攻擊使壓縮成為最不安全的特性,所以限制也不會(huì)再被使用。 - 擴(kuò)展性
記錄協(xié)議只關(guān)注傳輸和加密,而將所有其他特性轉(zhuǎn)交給子協(xié)議。這個(gè)方法使TLS可以擴(kuò)展,因?yàn)榭梢院芊奖愕奶砑幼訁f(xié)議。伴隨著記錄協(xié)議而被加密,所有子協(xié)議都會(huì)以協(xié)商取得的連接參數(shù)自動(dòng)得到保護(hù)。
TLS的主要規(guī)格說(shuō)明書定義了四個(gè)核心子協(xié)議:握手協(xié)議(handshake protocol)、密鑰規(guī)格變更協(xié)議(change cipher spec protoco)、應(yīng)用數(shù)據(jù)協(xié)議(application data protocol)和警報(bào)協(xié)議(alter protocol)。
2.2.1 完整的握手
每一個(gè)TLS連接都會(huì)以握手開始。如果客戶端此前并未與服務(wù)器建立會(huì)話,那么雙方會(huì)執(zhí)行一次完整的握手流出來(lái)協(xié)商TLS會(huì)話。握手過(guò)程中,客戶端和服務(wù)器將進(jìn)行一下四個(gè)主要步驟。
(1)交換各自支持的功能,對(duì)需要的連接參數(shù)達(dá)成一致。
(2)驗(yàn)證出示的證書,或使用其他方式進(jìn)行身份驗(yàn)證。
(3)對(duì)將用于保護(hù)會(huì)話的共享密鑰達(dá)成一致。
(4)驗(yàn)證握手消息并未被第三方團(tuán)體修改。
- ClienHello
在一次新的握手流程中,ClientHello消息總是第一條消息。這條消息將客戶端的功能和首選項(xiàng)傳送給服務(wù)器??蛻舳藭?huì)在新建連接后,希望重新協(xié)商或者響應(yīng)服務(wù)器發(fā)起的重新協(xié)商請(qǐng)求(由HelloRequest消息指示)時(shí),發(fā)送這條消息。
ClientHello消息結(jié)構(gòu):
- Version
協(xié)議版本(protocol version)指示客戶端支持的最佳協(xié)議版本。 - Random
隨機(jī)送(random)字段包含32字節(jié)的數(shù)據(jù)。當(dāng)然,只有28字節(jié)是隨機(jī)生成的;剩余的4字節(jié)包含額外的信息,受客戶端時(shí)鐘影響。準(zhǔn)確來(lái)說(shuō),客戶端時(shí)間與協(xié)議不相關(guān),而且協(xié)議規(guī)格文檔言及此事也很清楚(“基本的TLS協(xié)議不需要正確設(shè)置時(shí)鐘,更高層或應(yīng)用協(xié)議可以定義額外的需求項(xiàng)。”);該字段是1994年在Netscape Navigator中發(fā)現(xiàn)來(lái)一個(gè)嚴(yán)重故障之后,為了防御弱隨機(jī)數(shù)生成器而引入的。盡管這個(gè)字段曾經(jīng)一直包含由精確時(shí)間的部分,但現(xiàn)在仍然有人擔(dān)心客戶端時(shí)間可能被用于大規(guī)模瀏覽器指紋采集,所以一些瀏覽器會(huì)給它們的時(shí)間添加時(shí)鐘扭曲(正如你在示例中所看到的那樣),或者簡(jiǎn)單地發(fā)送隨機(jī)的4字節(jié)。
在握手時(shí),客戶端和服務(wù)器都會(huì)提供隨機(jī)數(shù)。這種隨機(jī)性對(duì)每次握手都是獨(dú)一無(wú)二的,在身份驗(yàn)證中騎著舉足輕重的作用。它可以防止重放攻擊,并確認(rèn)初始數(shù)據(jù)交換的完整性。 - Session ID
在第一次連接時(shí),會(huì)話ID(Session ID)字段是空的,這表示客戶端并不希望恢復(fù)某個(gè)已存在的會(huì)話。在后續(xù)的連接中,這個(gè)字段可以保持會(huì)話的唯一標(biāo)識(shí)。服務(wù)器可以借助會(huì)話ID在自己的緩存中找到對(duì)應(yīng)的會(huì)話狀態(tài)。典型的會(huì)話ID包含32自己隨機(jī)生成的數(shù)據(jù),這些數(shù)據(jù)本身沒有什么價(jià)值。 - Cipher Suites
密碼套件(cipher suite)塊是由客戶端支持的所有密碼套件組成的列表,該列表是按優(yōu)先級(jí)順序排列的。 - Compression
客戶端可以提交一個(gè)或多個(gè)支持壓縮的方法。默認(rèn)的壓縮是null,代表沒有壓縮。 - Extensions
擴(kuò)展(extension)塊是由任意數(shù)量的擴(kuò)展組成。這些擴(kuò)展會(huì)攜帶額外數(shù)據(jù)。我會(huì)在本章節(jié)后面對(duì)最常見的擴(kuò)展進(jìn)行討論。
- ServerHello
ServerHello消息的意義是將服務(wù)器選擇的連接參數(shù)傳送客戶端。這個(gè)消息的結(jié)構(gòu)與ClientHello類型,只是將每個(gè)字段包含一個(gè)選項(xiàng)。
服務(wù)器無(wú)需支持客戶端支持的最佳版本。如果服務(wù)器不支持與客戶端相同的版本,可以提供某個(gè)其他版本以期客戶端能夠接受。 - Certificate
典型的Certificate消息用于攜帶服務(wù)器X.509證書鏈證書鏈?zhǔn)且訟SN.1DER編碼的一系列證書,一個(gè)接著一個(gè)組合而成。主證書必須第一個(gè)發(fā)送,中間證書按照正確的順序跟在主證書之后。根證書可以并且應(yīng)該省略,因?yàn)樵谶@個(gè)場(chǎng)景中它沒有用處。
服務(wù)器必須保證它發(fā)送的證書與選擇的算法套件一致。比方說(shuō),公鑰算法與套件中使用的必須匹配。除此之外,一些密鑰交換算法依賴嵌入證書的特定數(shù)據(jù),而且要求證書必須以客戶端支持的算法簽名。所有這些都表面服務(wù)器需要配置多個(gè)證書(每個(gè)證書可能會(huì)配備不同的證書鏈)。
Certificate消息是可選的,因?yàn)椴⒎撬刑准际褂蒙矸蒡?yàn)證,也并非所有身份驗(yàn)證方法都需要證書。更進(jìn)一步,雖然消息默認(rèn)使用X.509證書,但是也可以攜帶其他形式的標(biāo)志:一些套件就依賴PGP密鑰。
4.ServerKeyExchange
ServerKeyExchange消息的目的是攜帶密鑰交換的額外數(shù)據(jù)。消息內(nèi)容對(duì)于不同的協(xié)商算法套件都會(huì)存在差異。在某些場(chǎng)景中,服務(wù)器不需要發(fā)送任何內(nèi)容,這意味著這些場(chǎng)景中根本不會(huì)發(fā)送ServerKeyExchange消息。
5.ServerHelloDone
ServerHelloDone消息表明服務(wù)器以及將所有預(yù)計(jì)的握手消息發(fā)送完畢。在此之前,服務(wù)器會(huì)等待客戶端發(fā)送消息。
6.ClientKeyExchange
ClientKeyExchange消息攜帶客戶端為密鑰交換提供的所有消息。這個(gè)消息受協(xié)商的密碼套件的影響,內(nèi)容隨著不同的協(xié)商密鑰套件而不同。
7.ChangeCipherSpec
ChangeCipherSpec消息表明發(fā)送端已取得用以生成連接參數(shù)的足夠信息,已生成加密密鑰,并且將切換到加密模式??蛻舳撕头?wù)端在條件成熟時(shí)都會(huì)發(fā)送這個(gè)消息。
8.Finished
Finished消息意味著握手已經(jīng)完成。消息內(nèi)容將加密,以便雙方可以安全地交換驗(yàn)證整個(gè)握手完整性所需的數(shù)據(jù)。
這個(gè)消息包含verify_data字段,它的值是握手過(guò)程中所有消息的散列值。這個(gè)過(guò)程是通過(guò)一個(gè)為隨機(jī)數(shù)函數(shù)(pseudorandom function,PRF)來(lái)完成,這個(gè)函數(shù)可以生成任意數(shù)量的偽隨機(jī)數(shù)數(shù)據(jù)。我將在本章的后續(xù)部分中對(duì)這其進(jìn)行介紹。散列函數(shù)與PRF一致,除非協(xié)商的套件指定使用其他算法。兩端的計(jì)算方法一致,但會(huì)使用不同的標(biāo)簽:客戶端使用client finished,而服務(wù)器則使用server finished。
verify_data = PRF(master_secret,finished_label,Hash(handshake_messages))
因?yàn)镕inished消息是加密的,并且它們的完整性由協(xié)商MAC算法保證,所以主動(dòng)網(wǎng)絡(luò)攻擊者不能改變握手消息并對(duì)verify_data的值造假。
理論上攻擊者也可以嘗試找到一組偽造的握手消息,得到的值與真正消息計(jì)算出的verify_data的值是完全一致。這種攻擊本身就非常不容易,而且因?yàn)樯⒘兄谢烊雭?lái)主密鑰(攻擊者不知道主密鑰),所以攻擊者根本不會(huì)嘗試。
在TLS1.2版本中,F(xiàn)inished消息的長(zhǎng)度默認(rèn)是12字節(jié)(96位),并且允許密碼套件使用更長(zhǎng)的長(zhǎng)度。在此之前的版本,除了SSL3使用36字節(jié)的定長(zhǎng)消息,其他版本都使用12字節(jié)的定長(zhǎng)消息。
2.2.2 客戶端身份驗(yàn)證
盡管可以選擇對(duì)任意一端進(jìn)行身份驗(yàn)證,但人們幾乎都啟用來(lái)對(duì)服務(wù)器的身份驗(yàn)證。如果服務(wù)器選擇的套件不是匿名的,那么就需要在Certificate消息中跟上自己的證書。
相比之下,服務(wù)器通過(guò)發(fā)送CertificateRequest消息請(qǐng)求對(duì)客戶端進(jìn)行身份驗(yàn)證。消息中列出所有可以接受的客戶端證書。作為回應(yīng),客戶端發(fā)送自己的Certificate消息(使用與服務(wù)器發(fā)送證書相同的格式),并附上證書。此后,客戶端發(fā)送CertificateVerify消息,證明自己擁有對(duì)應(yīng)的私鑰。
只有已經(jīng)過(guò)身份驗(yàn)證的服務(wù)器才被允許請(qǐng)求客戶端身份驗(yàn)證?;谶@個(gè)原因,這個(gè)選項(xiàng)被稱為相互身份驗(yàn)證(mutual authentication)。
1.CertificateRequest
服務(wù)器使用CertificateRequest消息請(qǐng)求對(duì)客戶端進(jìn)行身份驗(yàn)證,并將其接受的證書的公鑰和簽名算法傳送給客戶端。它也可以選擇發(fā)送一份自己接受的證書頒發(fā)機(jī)構(gòu)列表,這些機(jī)構(gòu)都用其可分辨名稱來(lái)標(biāo)示。
2.CertificateVerify
客戶端使用CertificateVerify消息證明自己擁有的私鑰與之前發(fā)送的客戶端證書中的公鑰相對(duì)應(yīng)。消息中包含一條到這一步為止的所有握手消息的簽名。
2.2.3 會(huì)話恢復(fù)
完整的握手協(xié)議非常復(fù)雜,需要很多握手消息和兩次網(wǎng)絡(luò)往返才能發(fā)送客戶端應(yīng)用數(shù)據(jù)。此外,握手執(zhí)行的密鑰學(xué)通常需要密集的CPU處理。身份驗(yàn)證通常需要以客戶端和服務(wù)器證書驗(yàn)證(以及證書吊銷檢查)的形式完成。需要更多的工作。這其中的許多消耗都可以通過(guò)簡(jiǎn)短握手的方式節(jié)約下來(lái)。
最初的會(huì)話恢復(fù)機(jī)制是,在一次完整協(xié)商的連接斷開時(shí),客戶端和服務(wù)器都會(huì)將會(huì)話的安全參數(shù)保存一段時(shí)間。希望使用會(huì)話恢復(fù)的服務(wù)器為會(huì)話指定唯一的標(biāo)識(shí),稱為會(huì)話ID。服務(wù)器在Serverhello消息中將會(huì)話ID發(fā)回客戶端。
希望恢復(fù)早先會(huì)話的客戶端將適當(dāng)?shù)臅?huì)話ID放入ClientHello消息,然后提交。服務(wù)器如果愿意恢復(fù)會(huì)話,就將相同的會(huì)話ID放入ServerHello消息返回,接著使用之前協(xié)商的主密鑰生成一套新的密鑰,再切換到加密模式,發(fā)送Finished消息??蛻舳耸盏綍?huì)話已恢復(fù)的消息以后,也進(jìn)行相同的操作。這樣的結(jié)果是握手只需要進(jìn)行一次網(wǎng)絡(luò)往返。
用來(lái)替代服務(wù)器會(huì)話緩存和恢復(fù)的方案是使用會(huì)話票證(session ticket)。它是2006年引入的(參考RFC4507),隨后在2008年進(jìn)行來(lái)更新(參照RFC5077)。使用這種方式,除了所有的狀態(tài)都保持在客戶端(與HTTP Cookie的原理類似)之外,其消息與服務(wù)器會(huì)話緩存是一樣的。