如果讓你來設(shè)計(jì)消息加密
你是跑碼場(chǎng)的一個(gè)程序員,名字叫招財(cái)。
利用上班摸魚的時(shí)間編寫了一個(gè)簡(jiǎn)易的即時(shí)通訊軟件,并發(fā)布到了網(wǎng)上。過了一段時(shí)間,你在軟件上突然收到一條私信。
“小哥哥,我很喜歡你寫的這個(gè)軟件,我也是程序媛,希望以后能多多和您交流?!?/p>
你望了望四周,沒有人表現(xiàn)得異常,于是你確認(rèn)不是同事在逗自己。
你興沖沖地給對(duì)方回復(fù),一來二去,慢慢熟識(shí)了起來。你知道了對(duì)方的名字,叫小美。
1、不安全的明文
隨著交流的話題越來越深入,你也慢慢變得惶恐。你本來就是隨手寫著玩兒的軟件,消息都是明文發(fā)送的,這萬一被研究網(wǎng)絡(luò)的同事給監(jiān)聽了,以后在公司還怎么混?
暴露的明文
于是你想到了:加密。
2、對(duì)稱加密
如果雙方擁有同樣的鑰匙,發(fā)送消息的一方使用這個(gè)鑰匙進(jìn)行加密,接收消息的一方使用同一個(gè)鑰匙進(jìn)行解密。這么一來,加密的事情就變得簡(jiǎn)單了。
為了實(shí)現(xiàn)這個(gè)功能,你想到了之前學(xué)過的異或(XOR)操作,這個(gè)操作的規(guī)則總結(jié)起來就是:相等為0,不等為1。任何數(shù)據(jù)在計(jì)算機(jī)中都會(huì)最終表示為01的字節(jié)流,比如你想發(fā)送的消息被最終編碼為1001110011001110,然后隨機(jī)生成一個(gè)同樣長(zhǎng)度的字符串1011011100110011,稱為秘鑰,兩者做一下異或操作:
異或加密
消息明文與秘鑰進(jìn)行異或操作之后就得到了密文,按照異或運(yùn)算的性質(zhì),如果此時(shí)將密文再和秘鑰做一次異或操作,那么將重新得到消息明文。
異或解密
如此一來便實(shí)現(xiàn)了雙方使用同一個(gè)共享秘鑰對(duì)消息進(jìn)行加密/解密的操作。
但是目前有個(gè)問題,你生成的秘鑰長(zhǎng)度必須和消息的長(zhǎng)度保持一致才能做異或操作,除非你對(duì)異或操作進(jìn)一步封裝。你才懶得琢磨這些東西呢,于是上網(wǎng)找思路。
這才知道,你的想法其實(shí)就是對(duì)稱加密。DES、3DES以及AES密碼算法都屬于對(duì)稱加密,其中或多或少都利用了異或運(yùn)算的性質(zhì),否則就沒有了“對(duì)稱”的效果。
你選擇了目前最流行的AES算法,打算直接使用現(xiàn)成的類庫(kù)實(shí)現(xiàn)加密。你把這個(gè)想法告訴了小美,迫不及待地想讓她知道你們的對(duì)話已經(jīng)實(shí)現(xiàn)了機(jī)密性。
誰知,小美一針見血地指出了問題,“秘鑰如何安全地在我們之間進(jìn)行共享呢?”
是啊,監(jiān)聽者也是可以獲取到秘鑰的。這樣就很矛盾,如果有一個(gè)辦法能將秘鑰安全地共享出去,那么豈不是也可以用同樣的方法來安全地發(fā)送明文咯?
對(duì)稱密碼的秘鑰必須發(fā)送,而又不能明文發(fā)送,這就是對(duì)稱秘鑰的配送問題。
秘鑰配送問題
3、非對(duì)稱加密
于是你又開始尋找其他方法,終于發(fā)現(xiàn)了一種叫做公鑰密碼的算法。
這種算法可以生成一組密鑰對(duì),分別叫做公鑰和私鑰。正如其名字暗示的那樣,公鑰是可以公開的,地球人都可以知道,而私鑰必須自己保存好,打死也不能讓別人知道的。
由公鑰進(jìn)行加密的密文,必須使用與該公鑰配對(duì)的私鑰才能夠解密。反過來,使用私鑰進(jìn)行加密的密文,擁有公鑰的人都可以進(jìn)行解密。這個(gè)特點(diǎn)非常重要。
于是你生成了一組密鑰對(duì),并將公鑰發(fā)給了小美,自己保存好私鑰。你讓小美給你發(fā)送消息之前先用你的公鑰對(duì)消息進(jìn)行加密,這樣一來,小美的加密消息只有你的私鑰才能夠解密。
于是便實(shí)現(xiàn)了小美對(duì)你發(fā)送消息的機(jī)密性。
消息的機(jī)密性
反過來,如果要實(shí)現(xiàn)你對(duì)小美發(fā)送消息的機(jī)密性,就需要小美生成一組密鑰對(duì),你獲得小美的公鑰,然后發(fā)送消息之前首先用小美的公鑰對(duì)消息進(jìn)行加密,將密文發(fā)送給小美之后,小美再用她的私鑰進(jìn)行解密。
如果通信過程中的公鑰和密文被監(jiān)聽者獲取到也沒關(guān)系,因?yàn)槟銈儍扇说乃借€都沒有出現(xiàn)在通信過程中,監(jiān)聽者即使有了密文也沒法解密。
這樣一來,你通過了公鑰加密解決了消息的機(jī)密性和秘鑰的配送問題(因?yàn)閴焊恍枰渌蛯?duì)稱加密的秘鑰了)。
公鑰密碼還有一個(gè)名字,叫做非對(duì)稱加密。這個(gè)名字是相對(duì)對(duì)稱加密而言的。
對(duì)稱加密是使用同一個(gè)秘鑰進(jìn)行相反的運(yùn)算,因此對(duì)稱密碼中的加密和解密就像照鏡子一樣,是相互對(duì)稱的。非對(duì)稱加密中,加密和解密使用的是不同的密鑰,并非相互對(duì)稱,因此稱為非對(duì)稱加密。
注:大名鼎鼎的RSA算法和ECC(橢圓密碼曲線)算法都是公鑰密碼算法的具體實(shí)現(xiàn)
4、混合加密算法
公鑰加密的改進(jìn)讓你感到非常踏實(shí),現(xiàn)在滿腦子里裝的都是跟小美聊天??墒悄阒饾u發(fā)現(xiàn),小美的回復(fù)變得比之前慢了一些。
和小美溝通一番之后發(fā)現(xiàn),她對(duì)你的回復(fù)也有同樣的感覺。
聰明的你最終發(fā)現(xiàn)了原因,公鑰加密的算法效率太低下了!在采用具備同等機(jī)密性的密鑰長(zhǎng)度的情況下,公鑰加密的速度只有對(duì)稱加密的幾百分之一。
知道了問題,解決方案也是很明顯了,把對(duì)稱加密和公鑰加密結(jié)合起來使用,也就是混合加密。
既然是混合加密,自然既要傳送消息,又要傳送對(duì)稱加密的秘鑰。你先用加密效率很高的對(duì)稱加密算法對(duì)消息進(jìn)行加密,這樣就實(shí)現(xiàn)了消息的機(jī)密性。接下來只要保證對(duì)稱加密秘鑰傳輸?shù)臋C(jī)密性就可以了。
這時(shí)候又要用到公鑰加密了。你將對(duì)稱加密的秘鑰當(dāng)做消息,利用上文公鑰加密的步驟,先使用小美的公鑰對(duì)對(duì)稱加密的秘鑰進(jìn)行加密,小美收到之后再用自己的私鑰進(jìn)行解密,就能安全在你倆之間傳遞對(duì)稱加密的秘鑰了。
你也不用擔(dān)心效率問題,畢竟對(duì)稱加密的秘鑰通常比消息短得多,況且秘鑰的傳遞只需要一次就可以了,因此加密的時(shí)間代價(jià)可以忽略。
畫一下使用混合加密算法時(shí),你發(fā)送消息進(jìn)行加密的流程圖。
混合加密
懂了加密,解密也就很簡(jiǎn)單了,為了讓小美看得明白,你還是給她畫了個(gè)解密流程圖。
混合解密
到此為止,你解決了公鑰加密速度慢的問題,并通過公鑰加密解決了對(duì)稱秘鑰的密鑰配送問題。
5、誰改了我的消息
你和小美聊得愈發(fā)火熱。但是你最近總是時(shí)不時(shí)收到小美發(fā)過來的莫名其妙的文字。你開始不以為意,但是這種文字的發(fā)送頻率越來越高,實(shí)在搞得你頭大。
你趕緊問了問小美,在確定不是她故意發(fā)這種文字之后,你明白了。
你倆被人盯上了。。。

你分析了一下小美實(shí)際傳給你的加密消息與你實(shí)際收到的加密消息,發(fā)現(xiàn)編碼之后的數(shù)據(jù)有兩位比特被改變了。改變雖然不大,但是對(duì)消息的可讀性會(huì)造成相當(dāng)大的影響。
你遇到中間人攻擊了。
這種情況下,中間人不需要知道你倆聊得究竟是什么,只需要截取你們的消息,然后進(jìn)行隨意篡改,就能擾亂你倆之間的正常交流。
你不禁感嘆:怎么傳遞個(gè)消息就這么難呢!
好在這個(gè)問題不難解決,你馬上就想到了單向散列算法。
單向散列算法可以對(duì)任意長(zhǎng)度的消息生成一個(gè)固定長(zhǎng)度的一串?dāng)?shù)據(jù)(散列值),這串?dāng)?shù)據(jù)的長(zhǎng)度遠(yuǎn)小于輸入的消息長(zhǎng)度,因此又被稱為消息摘要,單向散列算法也因此被稱為消息摘要算法。
摘要算法可以實(shí)現(xiàn)消息的完整性檢查。
單向散列函數(shù)
消息摘要算法的一個(gè)巨大好處是對(duì)消息的變化極為敏感,哪怕對(duì)消息僅修改了1個(gè)比特,生成的消息摘要也會(huì)有翻天覆地的變化。而且,即使中間人截獲了摘要消息,他想自己偽造一個(gè)經(jīng)過散列能夠生成同樣摘要的消息幾乎是一件不可能的事情。
注:經(jīng)常聽到的MD5、SHA-1、SHA2、SHA3算法都是消息摘要算法的具體實(shí)現(xiàn)
于是你在消息發(fā)送和解析過程中又添加了對(duì)消息摘要算法的支持。發(fā)送數(shù)據(jù)之前,先對(duì)混合算法加密之后的密文計(jì)算摘要值,然后將摘要值附在密文的最后,一起傳輸給小美。
圖片
小美收到數(shù)據(jù)后,根據(jù)信息摘要的固定長(zhǎng)度,首先分離摘要值和密文,然后使用相同的摘要算法計(jì)算出密文的摘要,與接收到的摘要進(jìn)行對(duì)比,一致則表示信息沒有被篡改,否則就說明被動(dòng)了手腳,直接丟棄。
圖片
你認(rèn)為事情到這終于結(jié)束了。但是軟件更新之后,你發(fā)現(xiàn)還是會(huì)時(shí)不時(shí)收到莫名其妙的消息。
6、是誰在冒充
你思來想去,問題到底出在了哪里?
“會(huì)不會(huì)是我們的消息整個(gè)都被篡改了啊?!毙∶勒f道。“如果中間人監(jiān)聽到了整個(gè)消息,分離出了混合加密的密文和摘要值,然后篡改混合加密的密文,再重新對(duì)篡改之后的密文計(jì)算出一個(gè)新的摘要,附在篡改的密文之后再發(fā)送,我們根本無法識(shí)別出篡改。”
中間人攻擊
是啊,如果真的是這樣,摘要值檢查的實(shí)際上就是中間人發(fā)送的消息的完整性,這個(gè)檢查就失去了意義。
現(xiàn)在中間人已經(jīng)徹底挾持了通信線路,只要中間人想,他就能篡改你們之間的所有數(shù)據(jù)。換句話說,你現(xiàn)在根本不知道和你進(jìn)行通信的是小美還是中間人。
這就是單向散列函數(shù)的缺陷,能夠辨別出“篡改”,但是無法分辨出“偽裝”。
你現(xiàn)在需要實(shí)現(xiàn)消息的認(rèn)證功能。也就是說,你對(duì)小美發(fā)送的消息,需要向小美證明消息確實(shí)是你發(fā)的,并且沒有被篡改。同樣,小美對(duì)你發(fā)送的消息,也需要向你證明,消息確實(shí)是小美發(fā)的,并且沒有被篡改。
7、確認(rèn)對(duì)方身份
于是你跟小美商量,需要約定一個(gè)只有你們兩人才知道的「信息」,并且通過接收到的消息可以判定,只有擁有這個(gè)共同信息的彼此才能發(fā)出這條消息。
小美立刻想到,“我們一直在使用的對(duì)稱加密的秘鑰不就是這個(gè)「共同信息」嘛!如果我發(fā)的消息你能解密出來,不就證明我就是我了嗎?”
“你這么想從一定程度上是對(duì)的,但是有個(gè)前提條件,就是你發(fā)的消息一定是有意義的。如果你本身就是想給我發(fā)送一堆亂七八糟的文字,我無法確定這是你的本意,還是說消息已經(jīng)被中間人篡改了?!?/p>
你倆陷入了短暫的沉默。
你思考了一下,摘要值目前無法發(fā)揮認(rèn)證的作用是因?yàn)橹灰虚g人獲取到了密文,就能隨便生成摘要值。那如果給摘要值的生成提高門檻,只有擁有「共同信息」的彼此才能夠生成正確的摘要值,這樣的話不就能認(rèn)證了嘛。
這個(gè)摘要值就叫做消息認(rèn)證碼。
網(wǎng)上搜索了一番,還真有這種方法,叫HMAC(Hash Message Authentication Code,哈希消息認(rèn)證碼),是用單向散列函數(shù)來構(gòu)造消息認(rèn)證碼的一種方法。
單向散列與HMAC
摘要值和HAMC值的最主要的區(qū)別就是后者在進(jìn)行單向散列的時(shí)候,需要用到一個(gè)秘鑰。因此這個(gè)可以簡(jiǎn)單理解為,消息認(rèn)證碼是一種與秘鑰相關(guān)聯(lián)的單向散列函數(shù)。
有了這個(gè)技術(shù),再來看一下你給小美發(fā)送消息的過程。
消息認(rèn)證碼
本來應(yīng)該為HMAC單獨(dú)生成一個(gè)隨機(jī)秘鑰,但是又會(huì)涉及到秘鑰配送的問題。由于我們之前已經(jīng)使用公鑰解決了秘鑰配送問題了,為了方便,就直接使用對(duì)稱秘鑰作為HMAC的秘鑰了。
和原來不同的是,用計(jì)算出的MAC代替了原來的摘要值,小美接收到密文和摘要值之后。利用自己的私鑰解密獲得對(duì)稱加密的秘鑰,再利用這個(gè)秘鑰對(duì)收到的密文計(jì)算MAC值,如果兩個(gè)MAC一致,那么小美就可以確定這個(gè)消息是你發(fā)送的,沒有經(jīng)過中間人篡改。
如此一來,使用MAC值就實(shí)現(xiàn)了消息的完整性校驗(yàn)和身份認(rèn)證的功能。
8、對(duì)方死不認(rèn)賬了
突然有一天,小美對(duì)你發(fā)來了一條消息。
“PHP是世界上最好的語言~”
你當(dāng)場(chǎng)炸裂啊~說這話妥妥引戰(zhàn)嘛,你剛要和小美理論一下,小美緊接著來了句:“我跟你玩?zhèn)€游戲啊?!比缓笥职l(fā)送了一個(gè)調(diào)皮的表情?!澳闳绾巫C明我對(duì)你說了這句話呢?”
“這還需要啥證明啊,我用咱倆共享的秘鑰,生成了MAC值,然后和你發(fā)給我的MAC對(duì)比,是一致的。如果你的私鑰沒有泄露,我就敢絕對(duì)保證,這條消息就是你發(fā)的!”
“沒錯(cuò),在咱倆之間,是絕對(duì)能保證身份的認(rèn)證的。但是我的問題是,你如何向其他人證明這個(gè)消息是我發(fā)送的呢?你想啊,你和我有相同的秘鑰,也就是說,理論上這條消息你也能生成。我要是死不認(rèn)賬,你怎么辦?當(dāng)然咯,截圖可不算哈~”
小美說得沒錯(cuò),按照現(xiàn)在的加密設(shè)計(jì),的確沒法向第三方證明她說過的話。
現(xiàn)在你倆之間能相互認(rèn)證是由于擁有同一個(gè)秘鑰,你確定不是自己發(fā)的,那必然就是對(duì)方發(fā)的。如果想向第三方證明消息是小美發(fā)的,那就必須使用小美獨(dú)有的信息對(duì)消息做一下處理。
就好比古代的虎符,如果小美持有其中一半,并且保證私有,這樣不管另一半在誰的手里,只要兩部分能夠合二為一,就能確認(rèn)對(duì)方就是小美。
驗(yàn)證虎符
這不就是公鑰和私鑰嘛!
小美用自己的私鑰對(duì)消息計(jì)算出一個(gè)「簽名」,任何持有小美公鑰的人都沒辦法用公鑰復(fù)刻這個(gè)簽名,但是可以對(duì)小美發(fā)送的簽名用公鑰進(jìn)行解密,解開的內(nèi)容如果恰好就是消息本身,那說明收到的消息一定屬于小美!畢竟私鑰是獨(dú)一無二的,這下子小美就再也沒辦法否認(rèn)了!
但是有個(gè)小問題,公鑰加密的效率實(shí)在是太低了,如果對(duì)整個(gè)消息使用私鑰進(jìn)行加密,耗時(shí)太久。能不能找個(gè)比較短的數(shù)據(jù)來代替消息本身呢?
老朋友——單向散列函數(shù),又該出馬了。只要先用單向散列函數(shù)求出消息的散列值,然后使用私鑰對(duì)散列值進(jìn)行簽名就可以了。散列值可比消息本身短得多,因此對(duì)其進(jìn)行簽名的時(shí)間代價(jià)就小得多了。
數(shù)字簽名
如果中間人篡改了小美發(fā)送的消息,那么你計(jì)算出來的散列值就會(huì)發(fā)生變化,導(dǎo)致簽名驗(yàn)證失敗,簽名保證了消息的完整性和對(duì)方身份的認(rèn)證。不僅如此,簽名還能防止小美否認(rèn)之前發(fā)的消息。
從圖中可以看出,簽名已經(jīng)徹底替換掉了之前使用的消息驗(yàn)證碼。
到此為止,你已經(jīng)實(shí)現(xiàn)了消息的機(jī)密性、完整性、身份認(rèn)證以及不可否認(rèn)性。
居然不知不覺已經(jīng)走了這么多路,你不禁問自己,到簽名這一步已經(jīng)完美了嗎?簽名的驗(yàn)證依賴于消息發(fā)送者公開的公鑰,那如果從一開始,這個(gè)公鑰就被中間人劫持,換成了中間人自己的公鑰了呢?我們又該如何確保收到的公鑰沒有被篡改呢?
公鑰傳輸不安全
9、CA機(jī)構(gòu)與證書
你發(fā)現(xiàn)自己陷入了一個(gè)怪圈。數(shù)字簽名是用來進(jìn)行身份認(rèn)證、防止篡改以及防止否認(rèn)的,數(shù)字簽名依賴公鑰的傳遞,公鑰的傳遞又需要進(jìn)行身份認(rèn)證以及防止篡改。。。
你把現(xiàn)在的設(shè)計(jì)告訴了小美,想看看她還有沒有其他的主意。
“我覺得你現(xiàn)在設(shè)計(jì)地已經(jīng)夠好了,接下來已經(jīng)不是單純的軟件設(shè)計(jì)能力能夠解決的事情了,已經(jīng)上升到社會(huì)學(xué)層次的高度了?!?/p>
“這是什么意思?”
小美解釋道:“就是說,我們應(yīng)該成立類似于公證處這樣的專業(yè)機(jī)構(gòu),來認(rèn)證公鑰的合法性,叫做CA。認(rèn)證機(jī)構(gòu)也要生成自己的一組密鑰對(duì),分成公鑰和私鑰,私鑰自己保管,公鑰任何人都可知?!?/p>
“那這怎么進(jìn)行認(rèn)證呢?”
“還是用你數(shù)字簽名的思路,比如我在分發(fā)我的公鑰讓你知道之前,我先把公鑰發(fā)給認(rèn)證機(jī)構(gòu),然后認(rèn)證機(jī)構(gòu)用自己的私鑰對(duì)我的公鑰添加數(shù)字簽名,最后頒發(fā)一個(gè)包含我的公鑰和認(rèn)證機(jī)構(gòu)數(shù)字簽名的證書。這個(gè)證書就證明我的公鑰是合法的?!毙∶姥a(bǔ)充道,然后畫了個(gè)圖。
公鑰的證書
你一眼就看出了這個(gè)邏輯的漏洞,這個(gè)方案沒有從根本上解決問題,只是把問題轉(zhuǎn)移了而已。因?yàn)樾∶缷寢屵@個(gè)認(rèn)證機(jī)構(gòu)需要對(duì)小美的公鑰施加數(shù)字簽名,你勢(shì)必需要用到小美媽媽這個(gè)認(rèn)證機(jī)構(gòu)的公鑰。
“我現(xiàn)在不信任你的公鑰,也對(duì)你媽媽這個(gè)認(rèn)證機(jī)構(gòu)的公鑰有所懷疑,那認(rèn)證機(jī)構(gòu)對(duì)頒發(fā)的證書就沒有任何意義!依舊是個(gè)死循環(huán)!“你對(duì)小美提出了自己的疑問。
”那取決于你怎么理解『信任』這個(gè)詞兒了,如果你除了自己誰也不信,那對(duì)你來說永遠(yuǎn)不會(huì)有安全的通信,這個(gè)邏輯鏈條就是個(gè)死循環(huán)。實(shí)際上,『信任』這個(gè)詞兒是比較出來的,在多個(gè)供選擇的信任對(duì)象中只要有一個(gè)你最信任的就可以了“。
小美繼續(xù)說:”我舉個(gè)例子。你覺得銀行愿意把錢借給你是因?yàn)樾湃文氵@個(gè)人本身嗎?當(dāng)然不是,銀行相信的是你背后抵押的房子等所有值錢的東西?;氐竭@個(gè)例子里,如果我媽媽的認(rèn)證機(jī)構(gòu)經(jīng)過層層關(guān)系,最終找到了你媽媽的認(rèn)證機(jī)構(gòu),你看看這個(gè)邏輯是不是就解釋的通了?!?/p>
圖片
信任鏈
你看了一下這個(gè)層級(jí),既然是你媽媽開的認(rèn)證機(jī)構(gòu),你肯定選擇信任,由于「招財(cái)媽媽認(rèn)證機(jī)構(gòu)」出具了「小美媽媽認(rèn)證機(jī)構(gòu)」公鑰的證書,「小美媽媽認(rèn)證機(jī)構(gòu)」又出具了小美公鑰的證書,根據(jù)這個(gè)信任鏈條,你自然就信任小美的公鑰的合法性了。
到此,問題全部解決。