https連接的前幾毫秒發(fā)生了什么
在討論這個(gè)話題之前,先提幾個(gè)問題:
為什么說https是安全的,安全在哪里?
https是使用了證書保證它的安全的么?
為什么證書需要購買?
我們先來看https要解決什么問題
一、 https解決什么問題
https要解決的問題就是中間人攻擊,什么是中間人攻擊(Man In The Middle Attack)呢?如下圖所示:
你和服務(wù)器的連接會(huì)經(jīng)過一個(gè)中間人,你以為你和服務(wù)器在正常地傳輸入數(shù)據(jù),其實(shí)這些數(shù)據(jù)都先經(jīng)過了一個(gè)中間人,這個(gè)中間人可以窺探你的數(shù)據(jù)或者篡改你的數(shù)據(jù)后再發(fā)給服務(wù)器,相反也可以把服務(wù)器的數(shù)據(jù)修改了之后再發(fā)給你。而這個(gè)中間人對(duì)你是透明的,你不知道你的數(shù)據(jù)已經(jīng)被人竊取或者修改了。
二、 中間人攻擊的方式
常見的有以下兩種:
1)域名污染
由于我們?cè)L問一個(gè)域名時(shí)需要先進(jìn)行域名解析,即向DNS服務(wù)器請(qǐng)求某個(gè)域名的IP地址。例如taobao.com我這邊解析的IP地址為:
在經(jīng)過DNS的中間鏈點(diǎn)可能會(huì)搶答,返回給你一個(gè)錯(cuò)誤的IP地址,這個(gè)IP地址就指向中間人的機(jī)器。
2)APR欺騙
廣域網(wǎng)的傳輸是用的IP地址,而在局域網(wǎng)里面是用的物理地址,例如路由器需要知道連接它的設(shè)備的物理地址它才可以把數(shù)據(jù)包發(fā)給你,它會(huì)通過一個(gè)ARP的廣播,向所有設(shè)備查詢某個(gè)IP地址的物理地址是多少,如下所示:
路由器發(fā)了一個(gè)廣播,詢問192.168.1.100的物理地址是多少,由于沒有人響應(yīng),所以它每隔1秒就重新發(fā)了個(gè)包。由于這個(gè)網(wǎng)絡(luò)上的所有機(jī)器都會(huì)收到這個(gè)包,所以這個(gè)時(shí)候就可以欺騙路由器:
上面的192.168.1.102就向路由器發(fā)了一個(gè)響應(yīng)的包,告訴路由器它的物理地址。
三、https是應(yīng)對(duì)中間人攻擊的唯一方式
在ssl的源碼里面就有一段注釋:
- /* cert is OK. This is the client side of an SSL connection.
- * Now check the name field in the cert against the desired hostname.
- * NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
- */
最后一句的意思就是說使用https,是應(yīng)對(duì)中間人攻擊的唯一方式。為什么這么說呢,這得從https連接的過程說起。
四、https連接的過程
如果對(duì)于一個(gè)外行人,可以這么解釋:
https連接,服務(wù)器發(fā)送它的證書給瀏覽器(客戶端),瀏覽器確認(rèn)證書正確,并檢查證書中對(duì)應(yīng)的主機(jī)名是否正確,如果正確則雙方加密數(shù)據(jù)后發(fā)給對(duì)方,對(duì)方再進(jìn)行解密,保證數(shù)據(jù)是不透明的
但是如果這個(gè)外行人比較聰明,他可能會(huì)問你瀏覽器是怎么檢驗(yàn)證書正確的,證書又是什么東西,加密后不會(huì)被中間人破解么?
首先證書是個(gè)什么東西,可以在瀏覽器上面看到證書的內(nèi)容,例如我們?cè)L問谷歌,然后點(diǎn)擊地址欄的小鎖:
再點(diǎn)擊詳情->查看證書,就可以看到整個(gè)證書的完整內(nèi)容:
接下來再用一個(gè)WireShark的抓包工具,抓取整個(gè)https連接的包,并分析這些包的內(nèi)容。
下面以訪問淘寶為例,打開淘寶,可以在Chrome里面看到淘寶的IP

然后打開WireShark,設(shè)定過濾條件為源IP和目的IP都為上面的IP,就可以觀察到整一個(gè)連接建立的過程:
第一步是肯定是要先建立TCP連接,這里就不說了,我們從Client Hello開始說起:
1. Client Hello
我們?cè)趙ireshark里面觀察,將client hello里面客戶端發(fā)給服務(wù)器的一些重要信息羅列出來
(1)使用的TLS版本是1.2,TLS有三個(gè)版本,1.0,1.1,1.2,1.2是最新的版本,https的加密就是靠的TLS安全傳輸層協(xié)議:
(2)客戶端當(dāng)前的時(shí)間和一個(gè)隨機(jī)密碼串,這個(gè)時(shí)間是距Unix元年(1970.1.1)的秒數(shù),這里是147895117,隨機(jī)數(shù)的作用下面再提及。
(3)sessionId,會(huì)話ID,第一次連接時(shí)為0,如果有sessionId,則可以恢復(fù)會(huì)話,而不用重復(fù)握手過程:
(4)瀏覽器支持的加密組合方式:可以看到,瀏覽器一共支持22種加密組合方式,發(fā)給服務(wù)器,讓服務(wù)器選一個(gè)。具體的加密方式下文再介紹
(5)還有一個(gè)比較有趣的東西是域名:
為什么說這個(gè)比較特別呢,因?yàn)橛蛎枪ぷ髟趹?yīng)用層http里的,而握手是發(fā)生在TLS還在傳輸層。在傳輸層里面就把域名信息告訴服務(wù)器,好讓服務(wù)根據(jù)域名發(fā)送相應(yīng)的證書。
可以說,https = http + tls,如下圖所示:
數(shù)據(jù)傳輸還是用的http,加密用的tls。tls和ssl又是什么關(guān)系?ssl是tls的前身,ssl deprecated之后,才開始有了tls 1.0、1.1、1.2
3. Server Hello
服務(wù)器收到了Client Hello的信息后,就給瀏覽器發(fā)送了一個(gè)Server Hello的包,這個(gè)包里面有著跟Client Hello類似的消息:
(1)時(shí)間、隨機(jī)數(shù)等,注意服務(wù)器還發(fā)送了一個(gè)Session Id給瀏覽器。
(2)服務(wù)器選中的加密方式:服務(wù)器在客戶端提供的方式里面選擇了下面這種,這種加密方式也是目前很流行的一種方式:
4. Certificate證書
接著服務(wù)器發(fā)送一個(gè)證書的包過來:
在WireShark里面展開證書:
服務(wù)器總共是發(fā)了三個(gè)證書,第一個(gè)叫做*.tmall.com,第二個(gè)證書叫做GlobalSign Org.,第三個(gè)叫GlobalSign Root.這三個(gè)證書是什么關(guān)系呢?這三個(gè)證書是相互依賴的關(guān)系,在瀏覽器里面可以看出:
tmall的證書是依賴于GlobalSign Org的證書,換句話說,GlobalSign Org的證書為tmall的證書做擔(dān)保,而根證書GlobalSign Root為GlobalSign Org做擔(dān)保,形成一條依賴鏈。明白這點(diǎn)很重要,從技術(shù)的角度上來說,GlobalSign為tmall的證書做簽名,只要簽名驗(yàn)證正確就說明tmall的證書是合法的。
在tmall的證書里面會(huì)指明它的上一級(jí)證書是啥:
現(xiàn)在來看下一個(gè)證書里面具體有什么內(nèi)容。
除了上面提到的簽名外,每個(gè)證書還包含簽名的算法,和被簽名的證書tbsCertificate(to be signed Certificate)三部分:
這個(gè)tbsCertificate里面有什么東西呢?在WireShark里面展開可以看到,里面有申請(qǐng)證書時(shí)所填寫的國家、省份、城市、組織名稱:
以及證書支持的域名,可以看到taobao就在里面:
證書的有效期,可以看到這個(gè)證書如果不續(xù)費(fèi)到今年年底就要到期了:
還有證書的公鑰,GlobalSign Org的公鑰為:
我們把證書的公鑰拷貝出來,它是一串270個(gè)字節(jié)的數(shù)字,16進(jìn)制為540位:
- String publicKey = "3082010a0282010100c70e6c3f23937fcc70a59d20c30e533f7ec04ec29849ca47d523ef03348574c8a3022e465c0b7dc9889d4f8bf0f89c6c8c5535dbbff2b3eafbe356e74a46d91322ca36d59bc1a8e3964393f20cbce6f9e6e899c86348787f5736691a191d5ad1d47dc29cd47fe18012ae7aea88ea57d8ca0a0a3a1249a262197a0d24f737ebb473927b05239b12b5ceeb29dfa41402b901a5d4a69c436488def87efee3f51ee5fedca3a8e46631d94c25e918b9895909aee99d1c6d370f4a1e352028e2afd4218b01c445ad6e2b63ab926b610a4d20ed73ba7ccefe16b5db9f80f0d68b6cd908794a4f7865da92bcbe35f9b3c4f927804eff9652e60220e10773e95d2bbdb2f10203010001";
這個(gè)公鑰是由什么組成的呢?這是由N和e組成的:
- publicKey = (N, e)
其中N是一個(gè)大整數(shù),由兩個(gè)質(zhì)數(shù)相乘得到:
- N = p * q
e是一個(gè)冪指數(shù)。這個(gè)就涉及到非對(duì)稱加密算法,它是針對(duì)對(duì)稱加密算法來說的。什么是對(duì)稱加密算法呢?所謂對(duì)稱加密算法是說:會(huì)話雙方使用相同的加密解密方式,所以會(huì)話前需要先傳遞加密方式或者說是密鑰,而這個(gè)密鑰很可能會(huì)被中間人截取。所以后來才有了非對(duì)稱加密算法:加密和解密的方式不一樣,加密用的密鑰,而解密用的公鑰,公鑰是公開的,密鑰是不會(huì)傳播的,可能是保存在擁有視網(wǎng)膜掃描和荷槍實(shí)彈的警衛(wèi)守護(hù)的機(jī)房當(dāng)中。
第一個(gè)非對(duì)稱加密算法叫Diffie-Hellman密鑰交換算法,它是Diffie和Hellman發(fā)明的,后來1977年麻省理工的Rivest、Shamir 和 Adleman提出了一種新的非對(duì)稱加密算法并以他們的名字命名叫RSA。它的優(yōu)點(diǎn)就在于:
加密和解密的計(jì)算非常簡(jiǎn)單
破解十分難,只要密鑰的位數(shù)夠大,以目前的計(jì)算能力是無法破解出密鑰的
可以說,只要有計(jì)算機(jī)網(wǎng)絡(luò)的地方,就會(huì)有RSA。RSA加密具體是怎么進(jìn)行的呢:
5. RSA加密和解密
假設(shè)發(fā)送的信息為Hello,由于Hello的ASCII編碼為:104 101 108 108 111,所以要發(fā)送的信息為:
M = 1041010108108111
即先把要發(fā)送的文本轉(zhuǎn)成ASCII編碼或者是Unicode編碼,然后進(jìn)行加密:
EM = M^e % N
就是把M作e次冪,然后除以N取余數(shù),得到EM,EM即為加密后的信息。其中(N,e)就是上文提到的公鑰。接下來將EM發(fā)送給對(duì)方,對(duì)方收到后用自己的密鑰進(jìn)行解密:
M = EM^d % N
將加密的信息作d次冪,再除以N取模,(N,d)就是對(duì)方的密鑰,這樣就能夠?qū)M還原為M,可以證明,只要密鑰和公鑰是一一配對(duì)的,上式一定成立。不知道密鑰的人是無法破譯的,上文已提到破解密鑰是相當(dāng)困難的。
接下來回到上文提到的證書的公鑰,這是一串270個(gè)字節(jié)的數(shù)字,可以拆成兩部分N和e:
灰色的數(shù)字是用來作為標(biāo)志的。N是一個(gè)16進(jìn)制為512位、二進(jìn)制為2048位的大數(shù)字。普通的證書是1024位,2048位是一個(gè)很高安全級(jí)別,換算成10進(jìn)制是617位,如果你能夠?qū)⑦@個(gè)617位的大整數(shù)拆成兩個(gè)質(zhì)數(shù)相乘,就可以推導(dǎo)出GlobalSign的密鑰,也就是說你破解了GlobalSign的證書(但這是不可能的)。
e為65537,證書通常取的冪指數(shù)都為這個(gè)數(shù)字。
在證書里面知道證書使用的加密算法為RSA + SHA256,SHA是一種哈希算法,可用來檢驗(yàn)證書是否被篡改過:
我們將encrypted的值拷貝出來就是證書的簽名:
- String sinature = "3ec0c71903a19be74dca101a01347ac1464c97e6e5be6d3c6677d599938a13b0db7faf2603660d4aec7056c53381b5d24c2bc0217eb78fb734874714025a0f99259c5b765c26cacff0b3c20adc9b57ea7ca63ae6a2c990837473f72c19b1d29ec575f7d7f34041a2eb744ded2dff4a2e2181979dc12f1e7511464d23d1a40b82a683df4a64d84599df0ac5999abb8946d36481cf3159b6f1e07155cf0e8125b17aba962f642e0817a896fd6c83e9e7a9aeaebfcc4adaae4df834cfeebbc95342a731f7252caa2a97796b148fd35a336476b6c23feef94d012dbfe310da3ea372043d1a396580efa7201f0f405401dff00ecd86e0dcd2f0d824b596175cb07b3d";
這個(gè)簽名是一個(gè)256個(gè)字節(jié)的數(shù)字,它是GlobalSign Org用它的密鑰對(duì)tbsCertificate做的簽名,接下來用GlobalSign Org的公鑰進(jìn)行解密:
- String decode =
- new BigInteger(signature, 16) //先轉(zhuǎn)成一個(gè)大數(shù)字
- .pow(e) //再做e次冪
- .mod(new BigInteger(N) ) //再模以N
- .toString();
- System.out.println(decode);
注意在實(shí)際的計(jì)算中,不能直接e次冪,不然將是一個(gè)天文數(shù)字里的天文數(shù)字,計(jì)算量將會(huì)非常大,需要乘一次就模一次,很快就算出來了。計(jì)算出來的結(jié)果為:
這個(gè)結(jié)果又可以拆成幾部分來看:
第一個(gè)字節(jié)是00,第一個(gè)字節(jié)要比其它字節(jié)都要小,第二個(gè)字節(jié)是01,表示這是一個(gè)私有密鑰操作,中間的ff是用來填充,加大簽名的長(zhǎng)度,加大破解難度,最后面的64個(gè)字節(jié)就是SHA哈希的值,如果證書沒有被篡改過,那么將tbsCertificate作一個(gè)SHA哈希,它的值應(yīng)該是和簽名里面是完全一樣的。所以接下來我們手動(dòng)計(jì)算一下tbsCertificate的哈希值和簽名里面的哈希進(jìn)行對(duì)比。
這個(gè)的計(jì)算方式如下:
hash = SHA_256(DER(tbsCertificate))
注意不是將tbsCertificate直接哈希,是對(duì)它的DER編碼值進(jìn)行哈希,DER是一種加密方式。DER值可以在WireShark里面導(dǎo)出來,證書發(fā)過來的時(shí)候就已經(jīng)被DER過了:
然后再用openssl計(jì)算它的哈希值,命令為:
- openssl dgst -sha256 ~/tbsCertificate.bin
計(jì)算結(jié)果為:
即為:
- bedfc063c41b62e0438bc8c0fff669de1926b5accfb487bf3fa98b8ff216d650
和上面簽名里面的哈希值進(jìn)行對(duì)比,即下面的綠色字:
可以發(fā)現(xiàn):檢驗(yàn)正確!這個(gè)證書沒有被篡改過,確實(shí)是tmall的證書。那么這個(gè)時(shí)候問題來了,中間人有沒有可能既篡改了證書,還能保證哈希值是對(duì)的?首先不同的字符串被SHA256哈希后的值是一樣的概率比較小,同時(shí)由于密鑰和公鑰是一一配對(duì)的,所以中間人只能把公鑰改成它的公鑰,這個(gè)公鑰是一個(gè)p * q的整數(shù),所以他必須得滿足兩個(gè)條件,一個(gè)是要更改成一個(gè)有意義的公鑰,另一個(gè)是整個(gè)證書的內(nèi)容被哈希后的值和沒改前是一樣的,滿足這兩個(gè)條件就相當(dāng)困難了。
所以我們有理由相信,只有知道了GlobalSign Org密鑰的人,才能對(duì)這個(gè)證書正確地加密。也就是說,tmall的證書確實(shí)是GobalSign頒發(fā)和簽名的,我們可以用相同的方式驗(yàn)證GlobalSign Org是GlobalSign Root頒發(fā)和簽名。但是我們?yōu)槭裁匆嘈臛loabal Sign呢?
如果上面提到的中間人不篡改證書,而是把整一個(gè)證書都換它自己的證書,假設(shè)叫HackSign,證書里面要帶上訪問的域名,所以HackSign里面也會(huì)帶上*.taobao.com。這個(gè)HackSign和GlobalSign有什么區(qū)別呢?為什么我們要相信GlobalSign,而不相信HackSign呢?
因?yàn)镚lobalSign Root是瀏覽器或者操作系統(tǒng)自帶的證書,在火狐瀏覽器的證書列表里面可以看到:
GlobalSign Root是Builtin的,即內(nèi)置的。我們絕對(duì)相信GlobalSign Root,同時(shí)驗(yàn)證了GlobalSign Org是簽名是合法的,GlobalSign Org給tmall的證書也是合法的。所以這個(gè)就是我們的信任鏈,從GlobalSign Root一直相信到了tmall。而*.taobao.com的域名已經(jīng)被證書機(jī)構(gòu)注冊(cè)了,所以另外的人是不能再用*.taobao.com去注冊(cè)它的證書的。
所以證書為什么會(huì)有有效期,證書為什么要購買,從這里就可以知道原因了。
到這里,你可能又會(huì)冒出來另外一個(gè)問題,既然我不能改證書,也不能換證書,那我難道不能直接克隆你的證書放到我的機(jī)器上去,因?yàn)樽C書是完全公開透明的,可以在瀏覽器或者wireshark里面看到證書的完整內(nèi)容。然后我再用域名污染等技術(shù)將你訪問的域名打到我的機(jī)器上,那么我跟你一模一樣的證書不也是合法的?證書不就沒用了?
這個(gè)問題之前也困擾我許久,為了回答這個(gè)問題,我們先繼續(xù)講解連接的過程。
上面已經(jīng)說到瀏覽器已經(jīng)驗(yàn)證了證書是合法的,接下來:
6. 密鑰交換Key Exchange
上文提到服務(wù)器選擇的加密組合方式為:
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
這一串加密方式可以分成三部分:
服務(wù)器選中的密鑰交換加密方式為RSA,數(shù)據(jù)傳輸加密方式為AES,檢驗(yàn)數(shù)據(jù)是否合法的算法為SHA256.
具體的密鑰交換為ECDHE_RSA,什么叫ECDHE呢?
由于證書的密鑰和公鑰(2048位)都比較大,使用ECDH 算法最大的優(yōu)勢(shì)在于,在安全性強(qiáng)度相同的情況下,其所需的密鑰長(zhǎng)度較短。以 DH 演算法密鑰長(zhǎng)度 2048 位元為例。ECDH 算法的密鑰長(zhǎng)度僅需要 224 位元即可
所以RSA是用來驗(yàn)證身份然后交換密鑰的,并不是用來加密數(shù)據(jù)的,因?yàn)樗L(zhǎng)了,計(jì)算量太大。加密數(shù)據(jù)是用的ECDHE生成的密鑰和公鑰。
在服務(wù)器發(fā)送了它的證書給瀏覽器之后,就進(jìn)行了密鑰交換Server Key Exchange和Client Key Exchange:
在WireShark里面展開,可以看到它發(fā)給客戶端的公鑰:
這個(gè)公鑰只有65個(gè)字節(jié),260位,和上面的270個(gè)字節(jié)的公鑰相比,已經(jīng)短了很多。
同樣地,瀏覽器結(jié)合服務(wù)器發(fā)給它的隨機(jī)密碼(Server Hello),生成它自己的主密鑰,然后發(fā)送公鑰發(fā)給服務(wù)器:
這個(gè)公鑰也是只有65個(gè)字節(jié)
雙方交換密鑰之后,瀏覽器給服務(wù)器發(fā)了一個(gè)明文的Change Cipher Spec的包,告訴服務(wù)器我已經(jīng)準(zhǔn)備好了,可以開始傳輸數(shù)據(jù)了:
同樣地,服務(wù)器也會(huì)給瀏覽器發(fā)一個(gè)Change Cipher Spec的包:
瀏覽器給服務(wù)回了個(gè)ACK,然后就開始傳輸數(shù)據(jù):
傳輸數(shù)據(jù)是用的http傳輸?shù)?,但是?shù)據(jù)是加密的,沒有密鑰是沒辦法解密的:
上面已經(jīng)提到服務(wù)器選擇的數(shù)據(jù)傳輸加密方式為AES,AES是一種高效的加密方式,它會(huì)使用主密鑰生成另外一把密鑰,其加密過程可見維基百科。
到此,整一個(gè)連接過程就講解完了。這個(gè)時(shí)候就可以回答上文提出的證書被克隆的問題,其實(shí)答案很簡(jiǎn)單,因?yàn)檫@是沒有意義的。雙方采用RSA交換公鑰,使用的公鑰和密鑰是一一配套的,所以只要證書是對(duì)的,即公鑰是對(duì)的,對(duì)方?jīng)]辦法知道配套的密鑰是多少,所以即使證書被克隆,對(duì)方收到的數(shù)據(jù)是無法解密的。所以沒有人會(huì)取偷證書,因?yàn)槭菦]有意義的,因?yàn)樗恢烂荑€,從這個(gè)角度來說證書可以驗(yàn)證身份的合法性是可以理解的
這個(gè)連接的過程大概多久呢?
四、使用https的代價(jià)
在wireshark里面可以看到每個(gè)包的發(fā)送時(shí)間:
從最開始的Client Hello,到最后的Change Cipher Spec的包,即從4.99s到5.299秒,這個(gè)建立https連接的過程為0.3s。所以使用https是需要付出代價(jià):
建立https需要花費(fèi)時(shí)間(~0.3s)
數(shù)據(jù)需要加密和解密,占用更多的cpu
數(shù)據(jù)加密后比原信息更大,占用更多的帶寬
五、怎樣繞過https
使用ssltrip,這個(gè)工具它的實(shí)現(xiàn)原理是先使用arp欺騙和用戶建立連接,然后強(qiáng)制將用戶訪問的https替換成http。即中間人和用戶之間是http,而和服務(wù)器還是用的https。
怎樣規(guī)避這個(gè)問題:
如果經(jīng)常訪問的網(wǎng)站是https的,某一天突然變成了http,那么很可能有問題,最直觀的就是瀏覽器地址欄的小鎖沒有了:
六、怎樣創(chuàng)建一個(gè)自簽名的證書
證書要么買,要么自己創(chuàng)建一個(gè),可以使用openssl生成一個(gè)證書:
- openssl req -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -keyout test.com.key -out test.com.crt
如上,使用sha256 + rsa 2048位,有效期為365天,輸出為證書test.com.crt和密鑰test.com.key,在生成過程中它會(huì)讓你填相關(guān)的信息:
最后會(huì)生成兩個(gè)文件,一個(gè)是證書(未被簽名),另一個(gè)是密鑰:
然后把這個(gè)證書添加到瀏覽器里面,設(shè)置為信任,瀏覽器就不會(huì)報(bào)NET::ERR_CERT_AUTHORITY_INVALID的錯(cuò)誤,就可以正常使用這個(gè)證書了。
【本文是51CTO專欄作者“人人網(wǎng)FED”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過51CTO聯(lián)系原作者獲取授權(quán)】