我們現(xiàn)在經(jīng)常聽說誰誰誰密碼被盜了,誰誰誰信息又被劫持了。其中有一個原因:絕大部分網(wǎng)站用的是http這個明文協(xié)議。你以為很安全的在password框里填了隱藏的密碼,他卻一字一句明明白白的寫到了網(wǎng)絡(luò)上。于是乎好多網(wǎng)站開始從http遷移到https(至少登錄部分)。我也準(zhǔn)備做同樣的事情,因此抽時間和小伙伴tt一起研究了一下https。
剛開始看https的時候,各種頭大。國內(nèi)網(wǎng)上講相關(guān)的資料雖然一大堆,但是大部分是相互的抄,內(nèi)容多而亂,且沒有把事情講清楚。后來查閱了一些外文資料(包括rfc、wikipedia等),讀了JSSE的源代碼以后,基本把這個事情的來龍去脈看懂了大部分,但是涉及到很多很細(xì)節(jié)的東西還是覺得不是完全懂,如有疏漏和錯誤,敬請大家指正和原諒 :-)
這篇文章的目標(biāo):用盡量簡單和有趣的語言,把這個復(fù)雜的東東講述清楚。所以,接下來我打算分成三部分來聊聊我理解的Https:
1、入門篇:主要用通俗的語言講講Https是什么東東,以及他大體的工作方式;
2、技術(shù)篇:結(jié)合抓包工具和源代碼,分析Https的通訊流程和細(xì)節(jié);
3、理論篇:不是特別深入的聊聊一些跟Https相關(guān)的算法。
====入門篇的分割線====
What’s HTTPS?
簡單的說,https就是給http帶了一個安全套,即使別人拿到了信息,也不知道這個里面裝的啥。客戶端(包括browser、手機(jī)app等)和服務(wù)器每次發(fā)http包的時候,都對這個包加個密,讓第三者看到的只是加密后的亂碼(我只想對你說:你猜你猜你猜猜猜),到對端以后再解密。
這個安全套,原來是叫SSL(Secure Sockets Layer),最先是Netscape弄出來的,后來哥們兒完蛋了,就慢慢變了名字,叫TLS(Transport Layer Security Protocol)。具體的區(qū)別可以去wikipedia搜索TLS,他們之間的升級細(xì)節(jié)講述的非常詳細(xì)(這一點(diǎn)百度百科真的差的有點(diǎn)遠(yuǎn)~)。
這個安全套跑在TCP的上層,在TCP連接完成后且HTTP啟動前,協(xié)商一些跟加密相關(guān)的工作,完成協(xié)商之后,就可以對要發(fā)送的http包加密/解密了。
那他到底協(xié)商了些啥呢?其實就是保證安全的幾個問題:
1、服務(wù)器要證明自己是靠譜的、安全的,不然給一個假網(wǎng)站發(fā)加密的密文就跟裸奔沒啥區(qū)別
2、服務(wù)器和客戶端通訊需要的加密算法和加密密鑰
就跟當(dāng)年天地會和韋小寶通信一樣,先要亮出身份,證明自己,然后再拿出暗語的書信。
ComeOn! How TLS works?
第一步,服務(wù)器證明自己是靠譜的。
一個哥們兒XX說他是天地會的。如果你是韋小寶,你會怎么確認(rèn)他的身份呢?
其中有一種方案可能是這樣的:他會說S1是他師傅,如果你知道S1并和他確認(rèn)了,就ok了。如果不認(rèn)識,就繼續(xù)問S1的師傅S2……一直問道陳近南,只要陳近南確認(rèn)了,那就可以證明他了??雌饋砗孟裨O(shè)計模式里面的責(zé)任鏈 XX -> S1 -> S2 -> … -> ROOT
服務(wù)器證明自己也是同樣的邏輯,服務(wù)器S0有一個證書,說我是誰誰誰,這個證書由上級簽發(fā)機(jī)構(gòu)S1核準(zhǔn),如果你本地有這個S1的證書,那驗證一下就可以了。如果沒有,就問S1的簽發(fā)機(jī)構(gòu)S2。直到根的簽發(fā)機(jī)構(gòu)。如果本地認(rèn)證找到了其中任何一級的證書,就認(rèn)為S0是靠譜的。否則就是不靠譜。S0 -> S1 -> S2 -> … -> Root CA
實際上非常像工商局發(fā)的營業(yè)執(zhí)照,你上面有我蓋的紅坨坨才是靠譜的。
上圖就是淘寶的認(rèn)證級聯(lián)關(guān)系。
這些靠譜的證書內(nèi)置在操作系統(tǒng)、jdk等地方(百度或者谷歌上搜索“https數(shù)字證書設(shè)置”相關(guān)內(nèi)容就可以看到)。
此圖就是我本機(jī)證書列表的一部分。
這個就是基本邏輯,說白了,就是找一個我們都公認(rèn)靠譜的人來證實你的靠譜。
第二步,協(xié)商加密算法+密鑰。
加密和摘要算法有很多,常見的比如RSA、AES、DES、MD5、SHA等等。
大家把他們這樣來分:
1、加密/解密算法:能加密同時能反解的,就是加解密算法。按照加解密的密鑰是否一樣,又分為對稱和非對稱算法。比如對稱加密算法:AES、DES;非對稱加密算法:RSA。
2、摘要算法:就是只用來做摘要、簽名、驗證防止被別人篡改,基本不能反解(有可能可以通過碰撞暴力破解)。比如:MD5、SHA。
那服務(wù)器和客戶端接下來就協(xié)商一下,我們要用什么加密解密算法和密鑰防止別人看見,用什么摘要算法,防止別人篡改。
一般來講,對稱加密算法效率會比非對稱高,所以通常選擇對稱加密的AES較多。雙方通過某種方式協(xié)商出一個密鑰,后面就通過這個密鑰和加密算法進(jìn)行加解密。
客戶端發(fā)送一個:“地振高岡,一派溪山千古秀”
服務(wù)端回復(fù)一個:“門朝大海,三河合水萬年流”
整個過程大體就是這樣,后面雙方就開始發(fā)HTTP的加密包,對方解包得到對應(yīng)的HTTP數(shù)據(jù)。
世界一下就清晰了,對嗎?
No No No 其實還是很復(fù)雜滴…… 如果要想了解詳細(xì)的技術(shù)內(nèi)容,就讓我?guī)е憷^續(xù)往下看(你敢不敢跟我來)
===技術(shù)篇的分割線===
工欲善其事,必先利其器
為了做詳細(xì)的分析,我做了幾個準(zhǔn)備工作:
1、裝了一個wireshark,用來抓取網(wǎng)絡(luò)包
2、寫了一個java程序,打開debug運(yùn)行(java -Djavax.net.debug=all TestHttps),用來看交互細(xì)節(jié)
import java.net.URL;
import java.net.URLConnection;
public class TestHttps
{
public static void main(String[] args) throws Exception
{
final URL url = new URL('https://www.taobao.com');
final URLConnection conn = url.openConnection();
conn.connect();
}
}
3、找到openjdk源代碼:http://grepcode.com/
通過前兩個工作可以看到網(wǎng)絡(luò)交互的過程和詳細(xì)的數(shù)據(jù)包,第三個可以用來分析整個流程的代碼。
(注:以下涉及到代碼的分析,都是基于JDK8進(jìn)行的,如果因為版本原因,相關(guān)函數(shù)和代碼行數(shù)對接不上,請大家查找對應(yīng)版本的代碼)
好了,準(zhǔn)備工作做好了,我們開始吧!
抓個包,先看看門道
先給taobao同學(xué)發(fā)個請求吧:curl https://www.taobao.com,看到整個交互過程大體是這樣的(我把tcp三次握手,ACK包等無關(guān)的數(shù)據(jù)包都過濾掉了,只剩TLS相關(guān)的數(shù)據(jù)包):
上圖有幾個交互數(shù)據(jù)都合并到一個TCP包進(jìn)行發(fā)送了,比如漂藍(lán)的那一行(No = 49)的TCP包實際上包含了三個TLS包(Certificate、Server Key Exchange、Server Hello Done),下面分析的時候,我就把這個包展開。
Client | Server |
Client Hello -> | |
<- server="">-> | |
<->-> | |
<- server="" key="">-> | |
<- server="" hello="">-> | |
Client Key Exchange -> | |
Change Cipher Spec -> | |
Encrypted Handshake Message -> | |
<- change="" cipher="">-> | |
<- encrypted="" handshake="">-> | |
Application Data -> | |
<- application="">-> | |
Encrypted Alert -> |
上面抓的包全部展開就是這樣的效果。怎么樣,是不是差不多也看了個大概?我來翻譯翻譯吧。
Client | Server |
Client Hello你好! | |
Server Hello嗯,你好! | |
Certificate我的證書給你,驗證我吧 | |
Server Key Exchange這是我給你的加密密鑰相關(guān)的東東 | |
Server Hello Done好,我說完了 | |
Client Key Exchange這是我給你的加密密鑰相關(guān)的東東 | |
Change Cipher Spec準(zhǔn)備轉(zhuǎn)換成密文了哦 | |
Encrypted Handshake Message%……&*4 (密文思密達(dá)) | |
Change Cipher Spec我也轉(zhuǎn)換密文了 | |
Encrypted Handshake Message#%&……* (密文思密達(dá)) | |
Application Data%&¥&%*……(HTTP密文數(shù)據(jù)) | |
Application Data**……&%(HTTP密文數(shù)據(jù)) | |
Encrypted Alert警告(實際就是說完了,拜拜~) |
看起來是不是很簡單呢?
這實際上就是文章一開始,我說的要解決的兩個大問題:
1、認(rèn)證server端的靠譜性
2、交換加密算法和密鑰
具體每個包里面都發(fā)了哪些數(shù)據(jù)?server端靠譜性是如何來證明的?加密算法和密鑰是怎么交換的?接下來讓我一一給你道來。
具體的交互流程和代碼的實現(xiàn)
我們就按命令逐個來分析一下。
C:Client Hello
可以看到發(fā)送了很多數(shù)據(jù),但是最關(guān)鍵的幾個數(shù)據(jù):
1、TLS的版本
2、隨機(jī)數(shù):這個是用來生成最后加密密鑰的影響因子之一,包含兩部分:時間戳(4-Bytes)和隨機(jī)數(shù)(28-Bytes)
3、session-id:用來表明一次會話,第一次建立沒有。如果以前建立過,可以直接帶過去。
4、加密算法套裝列表:客戶端支持的加密-簽名算法的列表,讓服務(wù)器去選擇。
5、壓縮算法:似乎一般都不用
6、擴(kuò)展字段:比如密碼交換算法的參數(shù)、請求主機(jī)的名字等等
這一段的java實現(xiàn),是在sun.security.ssl.HandshakeMessage.ClientHello里面:
S:Server Hello
當(dāng)服務(wù)器收到客戶端的問候以后,立即做出了響應(yīng):
大體內(nèi)容和客戶端差不多,只是把加密算法的套裝列表換成了服務(wù)器選擇支持的具體算法。
通過這一步,客戶端和服務(wù)器就完成了加密和簽名算法的交換。這里的TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256拆分開看就是:TLS協(xié)議,用ECDH密鑰交換算法交換對稱加密密鑰相關(guān)的參數(shù),用RSA算法做簽名,最后使用AES_128_CBC做內(nèi)容的對稱加密,SHA256做摘要。
具體實現(xiàn)在:sun.security.ssl.HandshakeMessage.ServerHello
S:Certificate
這一步很關(guān)鍵,是服務(wù)器給客戶端展示證書的時候。
證書是一個鏈,從最底層一直到最頂層,表示誰誰誰給我認(rèn)證的。翻譯過來就是文章一開始給大家看到的那個東東:
證書一般采用X.509標(biāo)準(zhǔn),后面我會詳細(xì)來講述證書格式和如何級聯(lián)認(rèn)證。X509證書具體實現(xiàn)在:sun.security.x509.X509CertImpl
Certificate消息的實現(xiàn)代碼在:sun.security.ssl.HandshakeMessage.CertificateMsg 里面
S:Server Key Exchange
這個消息是用來發(fā)送密鑰交換算法相關(guān)參數(shù)和數(shù)據(jù)的。這里要提前提一下,就是根據(jù)密鑰交換算法的不同,傳遞的參數(shù)也是不同的。
常用的密鑰交換算法:RSA、DH(Diffie-Hellman)、ECDH(Elliptic curve Diffie–Hellman)
后面會詳細(xì)來講這幾個算法的某幾個,現(xiàn)在就不詳細(xì)走這個分支,只是知道他們可以交換密鑰,有參數(shù)要傳遞即可。
(這里不得不感嘆一句,老外對基礎(chǔ)科學(xué)的研究真的是太深入了,這些算法十分的巧妙。希望有一天中國人也能對基礎(chǔ)科學(xué)做出更多的貢獻(xiàn)~)
可以看到這里用到的是ECDH算法,交換了一些參數(shù),對數(shù)據(jù)做了簽名,防止劫持者篡改。
在Java里,這個消息有多個實現(xiàn),分別代表RSA、DH、ECDH算法,對應(yīng)的類分別是:
sun.security.ssl.HandshakeMessage.ServerKeyExchange
sun.security.ssl.HandshakeMessage.RSA_ServerKeyExchange
sun.security.ssl.HandshakeMessage.DH_ServerKeyExchange
sun.security.ssl.HandshakeMessage.ECDH_ServerKeyExchange
以下是ECDH的實現(xiàn):
S:Server Hello Done
Server要表達(dá)的信息基本表達(dá)完了,把主持人話筒交給客戶端:
對應(yīng)的實現(xiàn):sun.security.ssl.HandshakeMessage.ServerHelloDone
C:Client Key Exchange
這是客戶端對Server Key Exchange的回應(yīng),用于交換密鑰需要的參數(shù)。和服務(wù)器一樣,不同的密鑰交換算法實現(xiàn)是不一樣的,因此需要的參數(shù)也是有差異的。
這里用的是ECDH交換算法。
Java的實現(xiàn)也對應(yīng)的多個,分別是:
sun.security.ssl.RSAClientKeyExchange
sun.security.ssl.DHClientKeyExchange
sun.security.ssl.ECDHClientKeyExchange
以下是ECDH的具體實現(xiàn):
好了,經(jīng)過以上的步驟,Server-Client已經(jīng)將服務(wù)器認(rèn)證的相關(guān)工作做完了,密文函數(shù)&密鑰交換需要的參數(shù)也都相互傳遞了。剩下的,就是各自用一個叫做PRF(Pseudo-Random Function)的算法去生成加密密鑰,具體的這個函數(shù)是一個對多因子多次迭代摘要運(yùn)算等的實現(xiàn),這里姑且就當(dāng)做是一個很簡單的隨機(jī)運(yùn)算函數(shù)吧,比如:key = rand_c + rand_s + C。
到這一步,客戶端和服務(wù)器就完成了密鑰相關(guān)的交換。
有了這個密鑰,接下來,客戶端和服務(wù)器就開始切換交流語言了(用密文開始說悄悄話),他們會各自發(fā)一個命令,說明自己已經(jīng)準(zhǔn)備好,開始切換語言了。
C:Change Cipher Spec
客戶端切換成密文模式
這個在Java里的實現(xiàn)在:sun.security.ssl.Handshaker
C:Finished(Encrypted Handshake Message)
這個包表明握手已經(jīng)完成,并且對之前發(fā)過的數(shù)據(jù)進(jìn)行加密發(fā)送給對方做校驗,防止被篡改。同時也驗證一下,加密算法、密鑰工作是否正常。
具體代碼在:sun.security.ssl.HandshakeMessage.Finished
在收到這兩個消息以后,服務(wù)器也發(fā)出同樣的消息,即:
S:Change Cipher Spec
S:Finished(Encrypted Handshake Message)
噓~~~(此地長出一口氣) 至此,整個身份驗證、加密/解密算法&密鑰的交換都已經(jīng)結(jié)束,剩余的,就是進(jìn)行正常的HTTP請求以及對請求數(shù)據(jù)的加密/解密。
以上這些步驟,都是對于使用ECDH密鑰交換算法、沒有session、不要求驗證客戶端有效性的情況。對于有session的、或者要求驗證客戶端有效性、或者使用其他密鑰交換算法的,請求會有不一樣,具體的可以看看實現(xiàn)代碼,里面都非常詳細(xì)也容易閱讀,具體代碼在:
sun.security.ssl.ClientHandshaker
sun.security.ssl.ServerHandshaker
里面有一個processMessage函數(shù),是用switch...case寫的相關(guān)的狀態(tài)機(jī)。
看看,為了做安全的HTTP請求,需要額外付出多少的代價,來來回回需要多出多少次數(shù)據(jù)交換。
好了,如果還想了解其他更多更詳細(xì)的東東,就繼續(xù)跟我往下走,否則,在這里就可以return了 ^o^
===有點(diǎn)難度的理論分割線===
接下來準(zhǔn)備聊聊關(guān)于x509證書&證書驗證、加解密、簽名、密鑰交換、隨機(jī)的一些算法。由于我自己對這部分沒有專門的研究,只是借這次機(jī)會看了一些資料,所以了解的不是非常深入,可能只能涉及一些皮毛,大家多多諒解和指正。
1、關(guān)于X.509證書&證書驗證
X.509就是一個數(shù)字證書的標(biāo)準(zhǔn),就像工商營業(yè)執(zhí)照一樣,證明你這個網(wǎng)站是合法的。詳細(xì)的可以參見wikipedia和RFC:
https://en.wikipedia.org/wiki/X.509
http://www.ietf.org/rfc/rfc2459.txt
在TLS中使用到了這樣一個證書來進(jìn)行有效性的認(rèn)證。因為不是專業(yè)研究這個的機(jī)構(gòu),我們就不深入去研究這個標(biāo)準(zhǔn),而是看看他的數(shù)據(jù)格式和如何進(jìn)行驗證。
證書的模樣
我們先看看wireshark抓到的包長什么樣:
再看看Java的API文檔給出的比較詳細(xì)的定義:
http://docs.oracle.com/javase/8/docs/api/java/security/cert/X509Certificate.html
可以看出來,整體分為證書和對證書的簽名兩大部分。
證書包含:版本、序號、證書的簽名算法、簽發(fā)者、主題(被簽發(fā)者)、有效期等的信息。
為了方便閱讀,我們直接用chrome來查看證書:
可以看到非常詳細(xì)的信息,包括:被簽發(fā)者的基本信息、簽發(fā)者的基本信息,加密信息和簽名。我們?nèi)绻约河胦penssl做證書的話,都會要求相關(guān)項的填寫。有興趣的同學(xué)可以自己做一個證書試試手 ^_^
證書的驗證
當(dāng)TLS協(xié)議驗證一個網(wǎng)站是否有效的時候,Server會給出一個X509的證書鏈??蛻舳耸盏竭@個證書鏈以后,對證書鏈進(jìn)行驗證,所做的工作如下:
1、用最底端(證書鏈第一個)的證書,去驗證請求的主機(jī)和證書里的是否是一致
2、逐次驗證證書鏈里每張證書的合法性,直到找到一張證書在系統(tǒng)中存在:這一步又包含每張證書是否在不信任名單里、檢查簽名算法、檢查時間是否過期、檢查證書的發(fā)布者和證書鏈的上一級是否匹配、證書鏈的簽名檢查
以上檢查中,大多是按字節(jié)對比,相對比較簡單,相關(guān)代碼參見以下幾個函數(shù)的實現(xiàn):
sun.security.ssl.X509TrustManagerImpl.checkTrusted
sun.security.validator.SimpleValidator.engineValidate
sun.security.x509.X509CertImpl.verify
sun.security.x509.X509CertImpl.checkValidity
不過,其中證書鏈的簽名檢查是一個非常有意思的算法,這個算法我著實研究了一陣兒,還寫了一個簡單的程序去實驗,在這里稍微詳細(xì)講述一下。
我們得到了一個證書鏈,將他擴(kuò)展開成為一個層級關(guān)系:
對于每一級的證書,都是由上一級用私鑰對證書的sha摘要值進(jìn)行簽名(Root一般由自己簽發(fā)),簽名一般使用RSA算法。驗證的時候,用上一級的公鑰對簽名進(jìn)行解密,還原對應(yīng)的摘要值。如下圖:
這里先提前最最最簡單的插入一下RSA算法。RSA是非對稱加密,有一個公鑰(模數(shù)和指數(shù))和一個私鑰。M代表明文消息,C代表密文,n代表公鑰模數(shù),e代表公鑰指數(shù),d代表私鑰。
如果我們用公鑰對明文M加密,私鑰解密,則是為了傳遞信息,對消息進(jìn)行加密;
如果我們用私鑰對明文M加密,公鑰解密,則是為了保證消息是我簽發(fā)的,沒有偽造和篡改。
這里,我們介紹后一種(即防篡改和偽造)。
簽名加密:C = (M ^ d) mod n
簽名解密:M = (C ^ e) mod n
先請不要問我為什么,后面會單獨(dú)講,哈哈哈(是不是很賤)~
好,回歸正題,上一級認(rèn)證機(jī)構(gòu)用私鑰(d)對下一級的證書的SHA數(shù)字簽名(M) 進(jìn)行RSA加密得到密文(C)。而第三方只要用上一級的公鑰(e, n)對這個加密進(jìn)行還原,并能得到相關(guān)的SHA數(shù)字簽名(M),就可以認(rèn)為是經(jīng)過上一級認(rèn)證過的(因為只有他才能簽的出來)。
Come on,我們來實踐一下吧:
下圖淘寶上一級證書的公鑰(這些機(jī)構(gòu)就賣證書就可以賺翻了):n(圖中的256字節(jié)公共密鑰)和 e(圖中的指數(shù))
我們寫一個程序來驗證一下:
因為要用大數(shù)計算,所以用到了Java的大數(shù)類(這是用的最直接暴力的算法,其實還可以有很多優(yōu)化,比如O(n) -> O(lgn),先取模再乘等等)。
最后輸出的結(jié)果如下:
1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420bedfc063c41b62e0438bc8c0fff669de1926b5accfb487bf3fa98b8ff216d650
其中1fff...ff00是PKCS #1 v1.5 標(biāo)準(zhǔn)的前導(dǎo)補(bǔ)位,詳見:http://tools.ietf.org/html/rfc2313#page-9
接下來 303130…0420 是SHA-256摘要算法的標(biāo)識,詳見:http://tools.ietf.org/html/rfc3447#page-43
剩余部分正好32Bytes,是整個證書的SHA-256摘要。
是真的嗎?確認(rèn)那個32Bytes就是SHA-256的摘要么?來吧,我們寫一個程序驗證一下:
這個程序大致的意思,就是截獲X509證書的驗證,輸出對應(yīng)的證書信息。并用SHA-256對整個證書做摘要。得到的信息輸出如下:
======================
cert-count:
3
subject:
CN=*.tmall.com, O='Alibaba (China) Technology Co., Ltd.', L=HangZhou, ST=ZheJiang, C=CN
cert-sign:
3ec0c71903a19be74dca101a01347ac1464c97e6e5be6d3c6677d599938a13b0db7faf2603660d4aec7056c53381b5d24c2bc0217eb78fb734874714025a0f99259c5b765c26cacff0b3c20adc9b57ea7ca63ae6a2c990837473f72c19b1d29ec575f7d7f34041a2eb744ded2dff4a2e2181979dc12f1e7511464d23d1a40b82a683df4a64d84599df0ac5999abb8946d36481cf3159b6f1e07155cf0e8125b17aba962f642e0817a896fd6c83e9e7a9aeaebfcc4adaae4df834cfeebbc95342a731f7252caa2a97796b148fd35a336476b6c23feef94d012dbfe310da3ea372043d1a396580efa7201f0f405401dff00ecd86e0dcd2f0d824b596175cb07b3d
sha-256:
bedfc063c41b62e0438bc8c0fff669de1926b5accfb487bf3fa98b8ff216d650
======================
這個證書簽名以及對證書做的SHA-256摘要,和我們之前所得到的結(jié)果一樣一樣的~~
好了,到此為止,X509證書的格式和驗證基本上也講了一個大概了,真是非常不容易啊(為了寫文章,肚子已經(jīng)餓的咕咕叫了)~
2.關(guān)于RSA算法
仔細(xì)想想,RSA算法在哪些地方被用到了呢?
1、證書簽名的時候(上面剛剛做了驗證,對吧)
2、密鑰交換
為了防止信息被截獲篡改,需要對密鑰交換的參數(shù)做簽名。
所以,RSA算法是一個非常關(guān)鍵的加解密算法。那我們就來簡單聊聊吧~
(最近看RSA,越看越覺得這個算法很有意思~)
注:為了在文本上打印方便,以下采用 ^ 這個符號作為乘方的運(yùn)算符,即2^4代表2的4次方。
來看看他的定義吧,老復(fù)雜了!
1、選兩個超級大的素數(shù):p 和 q
2、把他們乘起來:n = p * q
3、然后把p-1和q-1也乘起來:m = (p - 1) * (q - 1)
4、再找一個和m互質(zhì)的數(shù):e -> gcd(e, m) = 1
5、最后,找一個d,滿足:(e * d) mod m = 1
6、然后公鑰就是(n, e)的組合,私鑰就是(n, d)的組合
……
我的媽,這么復(fù)雜,人都要瘋了,是不是?
為什么要搞這么復(fù)雜呢?
其實用到了幾個原理或者定理或者…… 他們分別是:
1、大數(shù)分解難題
2、費(fèi)馬小定理
3、中國剩余定理
4、擴(kuò)展歐幾里德算法
是不是被嚇蒙了呢?哈哈哈,由于這一篇是講Https的,所以就不詳細(xì)講這幾個原理,簡單表述一下(就算是簡單,也要說很多,也要碼很多的字……)。
首先,RSA建立的一個基本原則就是大數(shù)分解,如果沒有這個原則,就扯淡了。
我們給兩個素數(shù),比如 5 和 11。我們能很容易求出他們的乘積:5 * 11 = 55
當(dāng)然,在這個規(guī)模下,我們也很容易將 55 分解成 5 * 11。
但是,如果這兩個素數(shù)很大呢,比如10的幾百次方。我們還是很容易求出他們的乘積。不過,你再想分解他,就不容易咯~
其次,著名的費(fèi)馬同學(xué),發(fā)明了很多很多定理,其中比較著名的就是費(fèi)馬大小定理,小定理是這么說的:對于一個素數(shù)n和任意的正整數(shù)a,( a ^ ( n - 1 ) ) mod n = 1
我們來試試,比如 n = 5,a = 4, 那么 a ^ ( n - 1 ) = 4 ^ ( 5 - 1 ) = 256,256 mod 5 = 1
很神奇吧!網(wǎng)上可以搜一搜詳細(xì)的證明。
這個公式演化一下,就可以得到a的n次方和a分別對n取模,結(jié)果是一樣的:( a ^ n ) ≡ a (mod n)
比如上面的那個例子:( 4 ^ 5 ) mod 5 = 4 ; 4 mod 5 = 4
那就是說如果n是一個素數(shù),任意一個數(shù)的i次方模n,都會呈現(xiàn)n-1個數(shù)的循環(huán)周期(不一定是最短的周期),比如:
4的1,2,3,4,5,6……次方模5的余數(shù):4 1 4 1 4 1……
如果n是兩個素數(shù)p和q的乘積,那么這個周期會呈現(xiàn) (p -1) * (q - 1)這么長。也就是說,a 和 a的(p - 1) * (q - 1) + 1次方模n,得到的結(jié)果是一樣的。特別的,如果a <>
哈哈哈,繞了那么大一個彎子,最后的意思就是說,我只要知道 ( a ^ ( (p - 1) * (q - 1) + 1) ) mod n的余數(shù),實際上就是知道了a,對吧!
那我只要變個花樣兒就可以。 我先讓m = (p - 1) * (q - 1) ,然后找一個和m互質(zhì)的數(shù)e,再求出一個d,讓 e * d = k * m + 1。這樣,我給一個數(shù)字 T (其中T < n),計算出="" c="(T" ^="" e)="" mod="" n,="" 我拿著c做一個(c="" ^="" d="" )="" mod="" n,于是乎,神奇的一幕發(fā)生了:(c="" ^="" d)="" mod="" n="(" (t="" ^="" e)="" ^="" d="" )="" mod="" n="(" t="" ^="" (e="" *="" d)="" )="" mod="" n="(" t="" ^="" (k="" *="" m="" +="" 1)="" )="" mod="" n="T" mod="" n,因為="" t="">< n="">
舉個例子,我們讓p = 5, q = 7,推算出來 n = 5 * 7 = 35, m = (5 - 1) * (7 - 1) = 24
我們找一個e,比方是11,那么一定可以找到一個d = 35,e * d = 11 * 35 = 385,
( e * d ) mod m = 385 mod 24 = 1
我們選一個數(shù)字T = 18, 計算 C = (T ^ e) mod n = 2
然后 計算 (C ^ d) mod n = 18 = T
以上就是RSA的原理,是不是要清楚一點(diǎn)了呢?
現(xiàn)在還有一個問題沒有解答,就是我們是怎么找出d來的,實際上就是要解一個方程:
( e * x ) mod m = 1 等價于 e * x + m * y = 1,其中x就是d。
這個就是中國剩余定理里面講到的東東,具體實現(xiàn)的時候,用擴(kuò)展輾轉(zhuǎn)相除法(這就是擴(kuò)展歐拉算法)做個迭代就出來了。
補(bǔ)充一點(diǎn),RSA涉及到指數(shù)運(yùn)算,效率會比較低。實際上有很多優(yōu)化的方法,比如O(n) -> O(lgn),先取模再乘,降維到p和q等等,就不在這里細(xì)展開了。
3.最后簡單聊一個密鑰交換的DH算法
我們在之前提到,TLS要做的事情就是兩個:身份校驗 & 加密算法和密鑰協(xié)商。
身份校驗我們前面已經(jīng)比較詳細(xì)的講述過了。加密算法的協(xié)商我們之前也在流程中講述過。剩下關(guān)于密鑰交換和協(xié)商,我們之前輕描淡寫的聊了下。下面稍微詳細(xì)的講述一下。
在密鑰交換的過程中,會用到一個PRF的函數(shù),是”Pseudo-Random Function”這個的簡稱,中間計算過程比較復(fù)雜,有興趣的同學(xué)可以在網(wǎng)上搜索查閱(由于偷懶,我沒有詳細(xì)深入這個函數(shù) ^_^)。
整個密鑰的生成大體如下:
master_secret = PRF(pre_master_secret, 'master secret', ClientHello.random + ServerHello.random)
具體代碼可以參見:
抽象上來講,就是 server和client 根據(jù)Hello時的兩個隨機(jī)數(shù) 加上 客戶端產(chǎn)生的pre_master_secret來產(chǎn)生一個master_secret,最后由這個東東生成需要的MAC(Message Authentication Code)、key等等加密需要東東。
那其中就有一個關(guān)鍵問題,客戶端的pre_master_secret怎么樣告訴服務(wù)器的?
我們可以用之前講過的RSA算法,客戶端通過服務(wù)器公鑰將這個值加密后傳遞給服務(wù)器,服務(wù)器再去解密。也可以通過一個叫做DH(Diffie-Hellman)的算法。維基百科對這個算法講的十分詳細(xì)。
我就簡單翻譯一下:
有兩個哥們兒,Alice和Bob,他們想交換數(shù)據(jù),于是乎也不知道怎么就想出了一個牛逼的算法:
1、取模數(shù) p = 23,底數(shù) g = 5
2、然后Alice想了一個整數(shù)a = 6,發(fā)送給Bob一個數(shù):A = (g ^ a) mod p = (5 ^ 6) % 23 = 8
3、同理,Bob想了一個整數(shù)b = 15,發(fā)送給Alice一個數(shù):B = (g ^ b) mod p = (5 ^ 15) % 23 = 19
4、Alice拿著Bob給的B = 19,計算了一個數(shù):s = (B ^ a) mod p = (19 ^ 6) % 23 = 2
5、Bob也用同樣的方法,算了一下 : s = (A ^ b) mod p = (8 ^ 15) % 23 = 2
就這樣,在不泄露a、b的情況下,他們兩都得到了一個一樣的數(shù)。就這樣,數(shù)據(jù)交換了。。。
其實理論基礎(chǔ)就是: A ^ b ≡ (g ^ a) ^ b ≡ g ^ (a * b) ≡ (g ^ b) ^ a ≡ B ^ a (mod p)
更詳細(xì)的說明,可以看維基百科的解釋。
后來又有一個改進(jìn)的算法ECDH,這里就不詳細(xì)講述了(偷懶了,以后有機(jī)會再補(bǔ)~)
====總結(jié)的分割線====
好了,斷斷續(xù)續(xù)的抽大家睡覺的時間,把這篇文章寫完了(總算沒有當(dāng)太監(jiān))。個人覺得把HTTPS整個的流程、交互的過程做了一個大體的了解。有一部分做的深入些,還有很多算法看了一個一知半解,越是深入?yún)s發(fā)現(xiàn)不懂或者不了解的越多。剩余還有幾個點(diǎn)由于篇幅和時間的原因沒有講到,比如:如何訪篡改、如何防回放攻擊、如何做PRF、交互里面有些擴(kuò)展參數(shù)等,后面準(zhǔn)備再抽時間把這些補(bǔ)全。
由于之前主要是聚焦在常規(guī)算法和互聯(lián)網(wǎng)技術(shù)架構(gòu)方面,對信息相關(guān)的科學(xué)了解不是那么深入。后面準(zhǔn)備把信息論、加解密算法的相關(guān)東西系統(tǒng)性的看下,完了之后分享出來。有興趣的同學(xué)可以關(guān)注我的微信:simplemain
作為一個碼工,就想安安靜靜做做技術(shù)。春天來了,可以有更多的時間聽著優(yōu)美的輕音樂寫更多的東西了。下面這張照片是我2012年4月在山東濟(jì)南拍的春天,4年時間過去了,又把他翻出來,感受一下當(dāng)年的春意盎然。Hello, World!
附:部分參考資料
https://en.wikipedia.org/wiki/Transport_Layer_Security
http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html
http://www-brs.ub.ruhr-uni-bochum.de/netahtml/HSS/Diss/MeyerChristopher/diss.pdf
http://grepcode.com/snapshot/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/
http://drops.wooyun.org/tips/11232
http://tools.ietf.org/html/rfc3447#page-43
http://netsecurity.51cto.com/art/201505/476337_all.htm
x509:
http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/security/cert/X509Certificate.html
RSA:
http://blog.csdn.net/starryheavens/article/details/8536238
https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29
Diffie-Hellman:
https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
http://my.oschina.net/u/1382972/blog/330456
聯(lián)系客服