文章分享

開(kāi)放、平等、協(xié)作、快速、分享

當(dāng)前位置:首頁(yè)>文章分享

借助mbedTLS了解DTLS握手協(xié)議

摘錄:HCTech 無(wú)錫和控電子   時(shí)間:2020-08-07   訪問(wèn)量:4023

借助mbedTLS了解DTLS握手協(xié)議

借助mbedTLS了解DTLS握手協(xié)議

https://zhuanlan.zhihu.com/p/24028871


引子

本文將會(huì)利用mbedTLS協(xié)議棧,通過(guò)dump協(xié)議棧調(diào)試信息,抓包,代碼分析等方式來(lái)對(duì)DTLS的握手協(xié)議進(jìn)行介紹。

DTLS簡(jiǎn)介

簡(jiǎn)單說(shuō),DTLS(Datagram Transport Layer Security)實(shí)現(xiàn)了在UDP協(xié)議之上的TLS安全層。由于基于TCP的SSL/TLS沒(méi)有辦法處理UDP報(bào)文的丟包及重排序(這些問(wèn)題一般交給UDP的上層應(yīng)用解決),DTLS在原本TLS的基礎(chǔ)上做了一些小改動(dòng)(復(fù)用大部分TLS的代碼)來(lái)解決如下UDP上實(shí)現(xiàn)TLS的問(wèn)題:

  1. TLS記錄層內(nèi)記錄的強(qiáng)關(guān)聯(lián)性及無(wú)序號(hào)

  2. 握手協(xié)議的可靠性

    • 包丟失重傳機(jī)制(UDP無(wú)重傳機(jī)制)

    • 無(wú)法按序接收(握手需要對(duì)包按順序處理,而UDP包的到達(dá)并非按序,包頭沒(méi)有TCP那樣的Seq/Ack number)

    • 握手協(xié)議包長(zhǎng)(證書(shū)之類(lèi)傳輸可能達(dá)到KB級(jí)別)導(dǎo)致的UDP分包組包(類(lèi)似于UDP在IP層的分包)

  3. 重復(fù)包(Replay)檢測(cè)

由于UDP/DTLS相較于TCP/TLS的輕量化及較小的開(kāi)銷(xiāo),目前被更多的運(yùn)用的嵌入式環(huán)境中。例如CoAP使用DTLS來(lái)實(shí)現(xiàn)安全通路,CoAP及其上層的LWM2M則運(yùn)用在物聯(lián)網(wǎng)和云端的通訊上。

如果你已經(jīng)很熟悉TLS,那么看到這里后就請(qǐng)忽略此文:)

如何借助mbedTLS來(lái)分析握手協(xié)議

mbedTLS(前身PolarSSL)是面向嵌入式系統(tǒng),實(shí)現(xiàn)的一套易用的加解密算法和SSL/TLS庫(kù)。mbedTLS系統(tǒng)開(kāi)銷(xiāo)極小,對(duì)于系統(tǒng)資源要求不高。mbedTLS是開(kāi)源項(xiàng)目,并且使用Apache 2.0許可證,使得用戶(hù)既可以講mbedTLS使用在開(kāi)源項(xiàng)目中,也可以應(yīng)用于商業(yè)項(xiàng)目。目前使用mbedTLS的項(xiàng)目很多,例如Monkey HTTP Daemon,LinkSYS路由器。

我們?cè)谶@里簡(jiǎn)單的利用mbedTLS自帶的dtls_client/dtls_server的測(cè)試程序來(lái)分析握手協(xié)議。這里要說(shuō)明的是mbedTLS這個(gè)自帶的DTLS 測(cè)試程序,服務(wù)器只在localhost做bind,客戶(hù)端也只連接到localhost。實(shí)際上是個(gè)loopback測(cè)試。并且使用了TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384的Cipher Suite,也就是用橢圓曲線(EC)的DH算法來(lái)實(shí)現(xiàn)密鑰(Session Key)的協(xié)商,用RSA來(lái)實(shí)現(xiàn)密鑰協(xié)商時(shí)交換的EC類(lèi)型,DH公鑰等數(shù)據(jù)的簽名加密。所以握手的流程和其他一些加密方式會(huì)有所差別。比如和單純的RSA密鑰交換方式比起來(lái),會(huì)多一個(gè)“Server Key Exchange”報(bào)文。

下載mbedTLS

$git clone https://github.com/ARMmbed/mbedtls

打開(kāi)調(diào)試

在program/dtls_client.c, dtls_server.c里,增大DEBUG_LEVEL,然后將my_debug里加上時(shí)間戳信息。使能協(xié)議棧的內(nèi)部調(diào)試信息,方便對(duì)比客戶(hù)端和服務(wù)端的流程:

#define DEBUG_LEVEL 100

static void my_debug( void *ctx, int level,
                      const char *file, int line,
                      const char *str )
{
    struct timeval tv;

    ((void) level);

    gettimeofday(&tv, NULL);
    //strftime(outstr, sizeof(outstr), "%H:%M:%S", tmp);

    mbedtls_fprintf( (FILE *) ctx, "[%06ld.%ld]%s:%04d: %s", tv.tv_sec, tv.tv_usec, file, line, str );
    fflush(  (FILE *) ctx  );
}

編譯

在Ubuntu上可以直接編譯。

$ make

編譯結(jié)果為

  • 服務(wù)器:program/ssl/dtls_server

  • 客戶(hù)端: program/ssl/dtls_client

抓包

打開(kāi)wireshark之類(lèi)的抓包工具,對(duì)本地網(wǎng)卡進(jìn)行抓包。先跑server,后跑client。在抓包結(jié)束后,加dtls的display filter既可以:

Wireshark抓包截圖: (導(dǎo)出pcapng包下載

有了TLS協(xié)議棧的調(diào)試信息,Wireshark的實(shí)際的抓包數(shù)據(jù)再加上源代碼,我們就很容易來(lái)分析DTLS的握手協(xié)議。當(dāng)然gdb拿過(guò)來(lái)直接調(diào)試也行,不過(guò)需要把DEBUG宏在編譯時(shí)打開(kāi):

$ make DEBUG=1

DTLS握手協(xié)議分析

DTLS握手協(xié)議和TLS類(lèi)似。DTLS協(xié)議在UDP之上實(shí)現(xiàn)了客戶(hù)機(jī)與服務(wù)器雙方的握手連接,在握手過(guò)程中驗(yàn)證對(duì)方的身份,并且使用RSA或者DH(Diffie-Hellman)實(shí)現(xiàn)會(huì)話密鑰的建立,以便在后面的數(shù)據(jù)傳輸中對(duì)數(shù)據(jù)加密。它利用cookie驗(yàn)證機(jī)制和證書(shū)實(shí)現(xiàn)了通信雙方的身份認(rèn)證;并且用在報(bào)文段頭部加上序號(hào),緩存亂序到達(dá)的報(bào)文段;還利用重傳機(jī)制實(shí)現(xiàn)了可靠傳送。在握手完成后,通信雙方就可以利用握手階段協(xié)商好的會(huì)話密鑰來(lái)對(duì)應(yīng)用數(shù)據(jù)進(jìn)行加解密。

簡(jiǎn)易握手流程圖:

從流程圖上看,有(1)(3)兩個(gè)“Client Hello”請(qǐng)求,他兩之間的區(qū)別是第二個(gè)包含有(2)”Hello Verify Request”里服務(wù)端發(fā)來(lái)的Cookie。要使得DTLS握手正真開(kāi)始,服務(wù)端必須要判斷發(fā)送請(qǐng)求的客戶(hù)端是否是有效客戶(hù)端。通過(guò)這樣的Cookie交互,可以很大程度上保護(hù)服務(wù)端不受DoS的攻擊。如果利用Cookie,服務(wù)端會(huì)在收到每個(gè)客戶(hù)請(qǐng)求后返回一個(gè)體積大很多的證書(shū)給被攻擊者,超大量證書(shū)有可能造成被攻擊者的癱瘓。當(dāng)首次建立連接時(shí),(1)請(qǐng)求包中的cookie為空,服務(wù)端根據(jù)客戶(hù)端的源IP地址通過(guò)哈希方法隨機(jī)生成一個(gè)cookie,并填入(2)”Hello Verify Request”包中發(fā)送給客戶(hù)端。客戶(hù)端收到Cookie后,再次發(fā)送帶有該Cookie的“Client Hello”包(3),服務(wù)端收到該包后便檢查報(bào)文段里面的cookie值和之前發(fā)給該客戶(hù)端的Cookie值是否完全相同,若是,則通過(guò)Cookie驗(yàn)證,繼續(xù)進(jìn)行握手連接;若不是,則拒絕建立連接。所以說(shuō)(1)(2)步驟只在第一次連接時(shí)發(fā)生,之后在Cookie有效的情況下,DTLS握手從步驟(3)開(kāi)始。

  • 客戶(hù)端的實(shí)現(xiàn)都在ssl_cli.c里,狀態(tài)機(jī)由mbedtls_ssl_handshake_client_step()處理

  • 服務(wù)端的實(shí)現(xiàn)則在ssl_srv.c里,狀態(tài)機(jī)由mbedtls_ssl_handshake_server_step()處理

(3)”Client Hello”由函數(shù)ssl_write_client_hello()實(shí)現(xiàn)報(bào)文填充和發(fā)送,內(nèi)容主要包含:

  1. Random 32字節(jié)隨機(jī)數(shù),前4字節(jié)為當(dāng)前時(shí)間+28字節(jié)隨機(jī)數(shù)

  2. Cookie,從報(bào)文(2)中獲得

  3. Cipher Suite,客戶(hù)端可以支持的密鑰交換,數(shù)據(jù)加密方式

  4. Compression methods,是否壓縮,及壓縮方式

  5. Extension,例如服務(wù)器主機(jī)名;支持的簽名加密方式,EC曲線類(lèi)型等

服務(wù)端收到報(bào)文(3)后,會(huì)調(diào)用函數(shù)ssl_parse_client_hello()做一系列協(xié)商工作:
將Random保存;驗(yàn)證Cookie是否和客戶(hù)端的IP匹配;根據(jù)客戶(hù)端提供的Cipher Suite找最佳匹配的,能提供的Cipher算法集合。在mbedTLS的例子里使用了ECDHE_RSA_WITH_AES_256_GCM_SHA384(ECDHE密鑰協(xié)商算法、RSA簽名、GCM-AES對(duì)稱(chēng)密鑰加密傳輸數(shù)據(jù)、SHA384哈希簽名)。如果協(xié)商沒(méi)有問(wèn)題,服務(wù)端就調(diào)用ssl_write_server_hello()會(huì)發(fā)送報(bào)文(4)”Server Hello”,告訴客戶(hù)端使用什么Cipher Suite做握手,什么壓縮方式,然后利用隨機(jī)數(shù)生成一個(gè)Session ID。并且以客戶(hù)端同樣的方式生成的Random隨機(jī)數(shù),將隨機(jī)數(shù)和Session ID放入報(bào)文中。緊接著服務(wù)端會(huì)接連發(fā)送報(bào)文(5)(6)(7)

(5)”Certification”服務(wù)端會(huì)將他自己的證書(shū)發(fā)送給客戶(hù)端。證書(shū)中的肯定有一個(gè)證書(shū)的Subject是和Server Name相匹配的(commonName=”localhost”,organizationName=”P(pán)olarSSL”)。測(cè)試程序中其實(shí)發(fā)送了3個(gè)證書(shū),subject分別是localhost, PolarSSL Test CA及PolarSSL Test EC CA。PolarSSL Test CA實(shí)際上是localhost的父證書(shū),這里就涉及到一個(gè)CA Chain的概念?,F(xiàn)實(shí)情況中(例如瀏覽器訪問(wèn)HTTPS服務(wù)器),服務(wù)器發(fā)給客戶(hù)端的證書(shū)一般是終端用戶(hù)證書(shū)(end-user CA),客戶(hù)端需要通過(guò)證書(shū)認(rèn)證來(lái)檢查服務(wù)器是否可信。首先客戶(hù)端在自己安裝(瀏覽器安裝時(shí)都會(huì)默認(rèn)安裝可靠證書(shū))的一系列中間證書(shū)(intermediate CA)和根證書(shū)(ROOT CA)中查找該終端用戶(hù)證書(shū)的父證書(shū),以及該父證書(shū)的父證書(shū),直到追溯到根證書(shū),建立起這些證書(shū)的關(guān)系:CA Chain。然后使用該終端用戶(hù)證書(shū)的父證書(shū)的公鑰來(lái)驗(yàn)證終端用戶(hù)證書(shū)的完整性,然后找到父證書(shū)的父證書(shū),以同樣的方式驗(yàn)證父證書(shū)完整性,直到遇到根證書(shū)。一般來(lái)講,終端客戶(hù)證書(shū)的Issuer都會(huì)和某個(gè)中間或者根證書(shū)的Subject相匹配,也就意味著客戶(hù)證書(shū)是由某個(gè)中間或根證書(shū)發(fā)行機(jī)構(gòu)簽發(fā),并且終端客戶(hù)證書(shū)中的簽名是由父證書(shū)擁有者的私鑰加密的。由于根證書(shū)的Subject和Issuer都是自己,所以客戶(hù)端的根證書(shū)一定要保證是Trust CA頒發(fā)的,否則沒(méi)有辦法自己驗(yàn)證自己。

回到我們的例子,客戶(hù)端在初始化的時(shí)候加載了PolarSSL Test CA的根證書(shū)(當(dāng)然只是測(cè)試的根證書(shū)),這個(gè)和現(xiàn)實(shí)的情況類(lèi)似??蛻?hù)端收到(5)”Certification”報(bào)文后,很快就可以查找到”localhost”這個(gè)終端用戶(hù)證書(shū)的根證書(shū)是”P(pán)olarSSL Test CA”,一次便驗(yàn)證通過(guò)了。驗(yàn)證的代碼在:mbedtls_x509_crt_verify_with_profile()。驗(yàn)證通過(guò)后,客戶(hù)端實(shí)際上就獲得了證書(shū)中的公鑰。證書(shū)驗(yàn)證完畢,說(shuō)明服務(wù)端的身份沒(méi)有問(wèn)題,可以進(jìn)行下一步密鑰協(xié)商。

(6)”Server Key Exchange”是Session Key協(xié)商的重要一步ssl_write_server_key_exchange(),測(cè)試程序中使用了ECDHE_RSA的協(xié)商方式。服務(wù)端首先要將在(3)”Client Hello”階段和客戶(hù)端協(xié)商使用的EC曲線類(lèi)型找出來(lái),這里協(xié)商的曲線類(lèi)型是secp512r1(0x0019)。利用該曲線類(lèi)型,加載對(duì)應(yīng)于該曲線的參數(shù)p,b,G(x,y),n (服務(wù)端和客戶(hù)端參數(shù)相同,參考:library/ecp_curves.c)。調(diào)用mbedtls_ecdh_gen_public(),首先生成一個(gè)隨機(jī)數(shù)私鑰a(范圍[1, n-1]),然后計(jì)算Qs=aG,產(chǎn)生ECDHE的Public Key Qs,再用服務(wù)端的私鑰(和localhost證書(shū)中的公鑰配對(duì)的)對(duì)Qs做簽名(SHA512做哈希,RSA加密)。最后將曲線類(lèi)型,公鑰Qs,簽名算法及簽名寫(xiě)入(6)的報(bào)文中,發(fā)送給客戶(hù)端。由于ECDHE(Elliptic curve Diffie–Hellman)算法較為復(fù)雜,我也不是非常理解,在這里就不深入討論了。具體可以參考:
WikiPage
總之,ECDHE算法很快,而且不需要暴露預(yù)主密碼(premaster secret,馬上談到)。后面客戶(hù)端也會(huì)做同樣的操作,生成自己的私鑰b,計(jì)算Qc。雙方交換Q后,可以計(jì)算得到相同的預(yù)主密碼:bQs=baG=abG=aQc。之后雙方就可以用這個(gè)abG預(yù)主密碼和之前(3)(4)報(bào)文中的客戶(hù)端、服務(wù)端的Random來(lái)生成對(duì)稱(chēng)密鑰Session Key(master secret)。預(yù)主密碼如何生成密鑰,可以參考mbedtls_ssl_derive_keys():

master = PRF( premaster, "master secret", randbytes )[0..47]

因?yàn)閜remaster secret不需要做交換,而是在本地計(jì)算產(chǎn)生,所以說(shuō)ECDHE的密鑰協(xié)商方式比RSA更安全。

(7)”Server Hello Done”會(huì)緊接著(6)發(fā)送,內(nèi)容很簡(jiǎn)單,標(biāo)示了handshake type 14,,告訴客戶(hù)端Hello階段結(jié)束。

(8)”Client Key Exchange”客戶(hù)端在收到(6)報(bào)文后,獲取服務(wù)端發(fā)送過(guò)來(lái)的EC曲線類(lèi)型和Qs,使用和服務(wù)端一樣的流程生成私鑰b,計(jì)算公鑰Qc=bG,將Qc放入該報(bào)文,并發(fā)送給服務(wù)端。此時(shí)不需要再做簽名,后面”Finish”報(bào)文會(huì)再次讓雙方驗(yàn)證密鑰的一致性。

(9)”Change Cipher Spec”客戶(hù)端接著發(fā)送該報(bào)文,告訴服務(wù)端Session Key我已經(jīng)生成,雙方可以用密鑰Session Key(master secret)開(kāi)始加密通訊了。

(10)”Finished”該報(bào)文由客戶(hù)端發(fā)送mbedtls_ssl_write_finished(),這是第一個(gè)用Session Key加密的密文,內(nèi)容是一段驗(yàn)證數(shù)據(jù):

valid_data = PRF(master_secret, "client finished", MD5(handshake_messages) + SHA(handshake_messages))[0..11]

handshake_messages實(shí)際上是客戶(hù)端在握手階段發(fā)出的所有報(bào)文(不包含該Finished報(bào)文),服務(wù)端在收到該報(bào)文后,會(huì)以同樣的方式計(jì)算出valid_data,并且做比較。以確認(rèn)雙方協(xié)商的密鑰一致。確認(rèn)完畢后,服務(wù)端以同樣的方式發(fā)送(11)”Change Cipher Spec”和(12)”Finished”給客戶(hù)端。最后完成握手。

完成握手后,雙發(fā)就可以發(fā)送加密的Application Data數(shù)據(jù)包來(lái)進(jìn)行安全通訊,并且報(bào)文中都包含一個(gè)sequence number來(lái)標(biāo)識(shí)順序。

到這里握手協(xié)議部分就介紹完了,如果大家想要深入研究算法,去看mbedTLS的代碼不失為一個(gè)好途徑。

結(jié)尾

記得很早以前,我是看過(guò)SSL/TLS的協(xié)議,握手也是了解過(guò)。沒(méi)想到,最近看CoAP,很多細(xì)節(jié)現(xiàn)在都回想不起來(lái)。好記心不如爛筆頭,寫(xiě)下來(lái)也是分享,今后碰到問(wèn)題也可以做個(gè)參考。

參考文獻(xiàn)


上一篇:SSL和TLS部署注意事項(xiàng)

下一篇:DTLS協(xié)議中client/server的認(rèn)證過(guò)程和密鑰協(xié)商過(guò)程

在線咨詢(xún)

點(diǎn)擊這里給我發(fā)消息 售前咨詢(xún)專(zhuān)員

點(diǎn)擊這里給我發(fā)消息 售后服務(wù)專(zhuān)員

在線咨詢(xún)

免費(fèi)通話

24小時(shí)免費(fèi)咨詢(xún)

請(qǐng)輸入您的聯(lián)系電話,座機(jī)請(qǐng)加區(qū)號(hào)

免費(fèi)通話

微信掃一掃

微信聯(lián)系
返回頂部