提示:英文原文寫於2009年,當時的Firefox和最新版的Firefox,界面也有很大改動。如下是正文。html
花了數小時閱讀了如潮的好評,Bob最終火燒眉毛爲他購買的托斯卡納全脂牛奶點擊了「進行結算」,而後……git
哇!剛剛發生了什麼?github
在點擊按鈕事後的220毫秒時間內,發生了一系列有趣的事情,火狐瀏覽器(Firefox)不只改變了地址欄顏色,並且在瀏覽器的右下角出現了一個小鎖頭的標誌。在我最喜歡的互聯網工具Wireshark的幫助下,咱們能夠經過一個通過略微調整的用於debug的火狐瀏覽器來探究這一過程。算法
根據RFC 2818標準(譯者注:RFC 2818爲HTTP Over TLS-網絡協議),火狐瀏覽器自動經過鏈接Amazon.com的443端口來響應HTTPS請求。編程
不少人會把HTTPS和網景公司(Netscape)於上世紀九十年代中期建立的SSL(安全套接層)聯繫起來。事實上,隨着時間的推移,這二者之間的關係也慢慢淡化。隨着網景公司漸漸的失去市場份額,SSL的維護工做移交給了Internet工程任務組(IETF)。由網景公司發佈的第一個版本被從新命名爲TLS 1.0(安全傳輸層協議 1.0),並於1999年1月正式發佈。考慮到TLS已經發布了將近10年,現在已經很難再見到真正的SSL通訊了。數組
TLS將所有的通訊以不一樣方式包裹爲「記錄」(Records)。咱們能夠看到,從瀏覽器發出的第一個字節爲0x16(十進制的22),它表示了這是一個「握手」記錄。瀏覽器
接下來的兩個字節是0x0301,它表示了這是一條版本爲3.1的記錄,同時也向咱們代表了TLS1.0其實是基於SSL3.1構建而來的。安全
整個握手記錄被拆分爲數條信息,其中第一條就是咱們的客戶端問候(Client Hello),即0x01。在客戶端問候中,有幾個須要着重注意的地方:服務器
在客戶端問候中,有四個字節以Unix時間格式記錄了客戶端的協調世界時間(UTC)。協調世界時間是從1970年1月1日開始到當前時刻所經歷的秒數。在這個例子中,0x4a2f07ca就是協調世界時間。在他後面有28字節的隨機數,在後面的過程當中咱們會用到這個隨機數。網絡
在這裏,SID是一個空值(Null)。若是咱們在幾秒鐘以前就登錄過了Amazon.com,咱們有可能會恢復以前的會話,從而避免一個完整的握手過程。
密文族是瀏覽器所支持的加密算法的清單。整個密文族是由推薦的加密算法「TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA」和33種其餘加密算法所組成。別擔憂其餘的加密算法會出現問題,咱們一下子就會發現Amazon也沒有使用推薦的加密算法。
經過這種方式,咱們可以告訴Amazon.com:瀏覽器正在試圖訪問https://www.amazon.com。這確實方便了不少,由於咱們的TLS握手時間發生在HTTP通訊以前,而HTTP請求會包含一個「Host頭」,從而使那些爲了節約成本而將數百個網站域名解析到一個IP地址上的網絡託管商可以分辨出一個網絡請求對應的是哪一個網站。傳統意義上的SSL一樣要求一個網站請求對應一個IP地址,可是Server_name擴展則容許服務器對瀏覽器的請求授予相對應的證書。若是沒有其餘的請求,Server_name擴展應該容許瀏覽器訪問這個IPV4地址一週左右的時間。
Amazon.com回覆的握手記錄由兩個比較大的包組成(2551字節)。記錄中包含了0x0301的版本信息,意味着Amazon贊成咱們使用TLS1.0訪問的請求。這條記錄包含了三條有趣的子信息:
1.服務器問候信息(Server Hello)(2):
2.證書信息(11):
這段巨大的信息共有2464字節,其證書容許客戶端在Amazon服務器上進行認證。這個證書其實並無什麼奇特之處,你能經過瀏覽器瀏覽它的大部份內容。
3.服務器問候結束信息(14):
這是一個零字節信息,用於告訴客戶端整個「問候」過程已經結束,而且代表服務器不會再向客戶端詢問證書。
此時,瀏覽器已經知道是否應該信任Amazon.com。在這個例子中,瀏覽器經過證書確認網站是否受信,它會檢查 Amazon.com 的證書,而且確認當前的時間是在「最先時間」2008年8月26日以後,在「最晚時間」2009年8月27日以前。瀏覽器還會確認證書所攜帶的公共密鑰已被受權用於交換密鑰。
爲何咱們要信任這個證書?
證書中所包含的簽名是一串很是長的大端格式的數字:
任何人均可以向咱們發送這些字節,但咱們爲何要信任這個簽名?爲了解釋這個問題,咱們首先要回顧一些重要的數學知識:
人人經常會問,編程和數學之間有什麼聯繫?證書就爲數學在編程領域的應用提供了一個實際的例子。Amazon的服務器告訴咱們須要使用RSA算法來校驗證書籤名。什麼又是RSA算法呢?RSA算法是由麻省理工(MIT)的Ron Rivest、Adi Shamirh和Len Adleman(RSA命名各取了三人名字中的首字母)三人於上世紀70年代建立的。三位天才的學者結合了2000多年數學史上的精華,發明了這種簡潔高效的算法:
選取兩個較大的初值p和q,相乘得n;n = p*q 接下來選取一個較小的數做爲加密指數e,d做爲解密指數是e的倒數。在加密的過程當中,n和e是公開信息,解密密鑰d則是最高機密。至於p和q,你能夠將他們公開,也能夠做爲機密保管。可是必定要記住,e和d是互爲倒數的兩個數。
假設你如今有一段信息M(轉換成數字),將其加密只須要進行運算:C ≡ Me (mod n)
這個公式表示M的e次冪,mod n表示除以n取餘數。當這段密文的接受者知道解密指數d的時候就能夠將密文進行還原:Cd ≡ (Me)d ≡ Me*d ≡ M1 ≡ M (mod n)
有趣的是,解密指數d的持有者還能夠將信息M進行用解密指數d進行加密:Md ≡ S (mod n)
加密者將S、M、e、n公開以後,任何人均可以得到這段信息的原文:Se ≡ (Md)e ≡ Md*e ≡ Me*d ≡ M1 ≡ M (mod n)
如同RSA的公共密鑰加密算法常常被稱之爲非對稱算法,由於加密密鑰(在咱們的例子中爲e)和解密密鑰(在咱們的例子中是d)並不對稱。取餘運算的過程也不像咱們日常接觸的運算(諸如對數運算)那樣簡單。RSA加密算法的神奇之處在於你能夠很是快速的進行數據的加密運算,即 ,可是若是沒有解密密碼d,你將很難破解出密碼,即運算 將不可能實現。正如咱們所看到的,經過對n進行因式分解而獲得p和q,再推斷出解密密鑰d的過程難於上青天。
在使用RSA加密算法的時候,最重要的一條就是要確保任何涉及到的數字都要足夠複雜才能保證不被現有的計算方法所破解。這些數字要多複雜呢?Amazon.com的服務器是利用「VeriSign Class 3 Secure Server CA」來對證書進行簽名的。從證書中,咱們能夠看到這個VeriSign(電子簽名校驗器,也稱威瑞信公司)的係數n有2048位二進制數構成,換算成十進制足足有617位數字:
1890572922 9464742433 9498401781 6528521078 8629616064 3051642608 4317020197 7241822595 6075980039 8371048211 4887504542 4200635317 0422636532 2091550579 0341204005 1169453804 7325464426 0479594122 4167270607 6731441028 3698615569 9947933786 3789783838 5829991518 1037601365 0218058341 7944190228 0926880299 3425241541 4300090021 1055372661 2125414429 9349272172 5333752665 6605550620 5558450610 3253786958 8361121949 2417723618 5199653627 5260212221 0847786057 9342235500 9443918198 9038906234 1550747726 8041766919 1500918876 1961879460 3091993360 6376719337 6644159792 1249204891 7079005527 7689341573 9395596650 5484628101 0469658502 1566385762 0175231997 6268718746 7514321
(若是你想要對這一大串數字進行分解因式得到p和q,那就祝你好運!順便一提,若是你真的計算出了p和q,那你就破解了Amazon.com數字簽名證書了!)
這個VeriSign的加密密鑰e是 。固然,他們將解密密鑰d保管得十分嚴密,一般是在擁有視網膜掃描和荷槍實彈的警衛守護的機房當中。在簽名以前,VeriSign會根據相關約定的技術文檔,對Amazon.com證書上所提供的信息進行校驗。一旦證書信息符合相關要求,VeriSign會利用SHA-1哈希算法獲取證書的哈希值(hash),並對其進行聲明。在Wireshark中,完整的證書信息會顯示在「signedCertificate」(已簽名證書)中:
這裏應該是軟件的用詞不當,由於這一段其實是指那些即將被簽名的信息,而不是指那些已經包含了簽名的信息。
實際上通過簽名的信息S,在Wireshark中被稱之爲「encrypted」(密文)。咱們將S的e次冪除以n取餘數(即公式: )就能計算出被加密的原文,其十六進制以下:
0001FFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFF00302130 0906052B0E03021A05000414C19F8786 871775C60EFE0542 E4C2167C830539DB
根據PKCS#1 v1.5標準(譯者注:The Public-Key Cryptography Standards (PKCS)是由美國RSA數據安全公司及其合做夥伴制定的一組公鑰密碼學標準)規定:「第一個字節是00,這樣就能夠保證加密塊在被轉換爲整數的時候比其加密參數要小。」第二個字節爲01,表示了這是一個私有密鑰操做(數字簽名就是私有密鑰操做的一種)。後面緊接着的一連串的FF字節是爲了填充數據,使得這一串數字變得足夠大(加大黑客惡意破解的難度)。填充數字以一個00字節結束。緊接着的30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14這些字節是PKCS#1 v2.1標準中用於說明這段哈希值是經過SHA-1算法計算而出的。最後的20字節是SHA-1算法所計算出來的哈希值,即對未加密信息的摘要描述。(譯者注:原文中這裏使用了帶引號的signedCertificate,根據做者前文描述,這應該是Wireshark軟件的bug,實際上應指的是未被加密的信息。)
由於這段信息的格式正確,且最後的哈希值與咱們獨立計算出來的校驗一致,因此咱們能夠判定,這必定是知道「VeriSign Class 3 Secure Server CA」的解密密鑰d的人對它進行了簽名。而世界上只有VeriSign公司才知道這串密鑰。
固然了,咱們也能夠重複驗證這個「VeriSign Class 3 Secure Server CA」的證書的確是經過VeriSign公司的「第三類公私證書認證(Class 3 Public Primary Certification Authority)」進行簽名的。
可是,即使是這樣,咱們爲何要信任VeriSign公司?整個的信任鏈條就此斷掉了。
由圖能夠看到,「VeriSign Class 3 Secure Server CA」對Amazon.com進行了簽名,而「VeriSign Class 3 Public Primary Certification Authority」對「VeriSign Class 3 Secure Server CA」進行了簽名,可是最頂部的「VeriSign Class 3 Public Primary Certification Authority」則對本身進行了簽名。這是由於,這個證書自從NSS(網絡安全服務)庫中的certdata.txt 升級到1.4版以後就做爲「受信任的根證書頒發機構」(譯者注:參照微軟官方翻譯)被編譯到了Mozilla產品中(火狐瀏覽器)。這段信息是由網景公司的Robert Relyea於2000年9月6日提交的,並隨附如下注釋:
「由僅存的NSS編譯了框架。包含一個在線的certdata.txt文檔,其中包含了咱們受信的跟證書頒發機構(一旦咱們得到了其餘受信機構的許可會陸續將他們添加進去)」。
這個舉動有着至關長遠的影響,由於這些證書的有效日期是從1996年1月28日到2028年1月1日。
肯·湯普遜(Ken Thompson)在他的《對深信不疑的信任》(譯者注:Reflections on Trusting Trust是肯湯普遜1983年得到圖靈獎時的演說)的演說中解釋的很好:你最終仍是要絕對信任某一人,在這個問題上沒有第二條路可走。在本文的例子中,咱們就毫無保留的信任Robert Relyea作了一個正確的決定。咱們一樣但願Mozilla在本身軟件中加入「受信任根證書頒發機構」這種行爲也是合理的吧。
這裏須要注意的是:這一系列的證書和簽名只是用來造成一個信任鏈。在公共互聯網上,VeriSign的根證書被火狐瀏覽器徹底信任的時間遠早於你接觸互聯網。在一個公司中,你能夠建立本身的受信任的根證書頒發機構並把它安裝到任何人的計算機中。
相對的,你也能夠購買VeriSign公司的業務,下降整個證書信任鏈的信任風險。經過第三方的認證機構(在這個例子裏是VeriSign公司)咱們能利用證書創建起信任關係。若是你有相似於「悄悄話」的安全途徑來傳遞一個祕密的key,那你也可使用一個預共享密鑰(PSK)來創建起信任關係。諸如TLS-PSK、或者帶有安全遠程密碼(SRP)的TLS擴展包都能讓咱們使用預共享密鑰。不行的是,這些擴展包在應用和支持方面遠遠比不上TLS,因此他們有的時候並不實用。另外,這些替代選項須要額外德爾安全途徑進行保密信息的傳輸,這一部分的開銷遠比咱們如今正在應用的TLS龐大。換句話說,這也就是咱們爲何不該用那些其餘途徑構建信任關係的緣由。
言歸正傳,咱們所須要的最後確認的信息就是在證書上的主機名跟咱們預想的是同樣的。Nelson Bolyard在SSL_AuthCertificate 函數中的註釋爲咱們解釋其中的緣由:
「SSL鏈接的客戶端確認證書正確,並檢查證書中所對應的主機名是否正確,由於這是咱們應對中間人攻擊的惟一方式!」 (譯者注:中間人攻擊是一種「間接」的入侵攻擊,這種攻擊模式是經過各類技術手段將受入侵者控制的一臺計算機虛擬放置在網絡鏈接中的兩臺通訊計算機之間,這臺計算機就稱爲「中間人」。)
1
2
3
|
/* 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! */
|
這樣的檢查是爲了防止中間人攻擊:由於咱們對整個信任鏈條上的人都採起了徹底信任的態度,認爲他們並不會進行黑客行爲,就像咱們的證書中所聲稱它是來自Amazon.com,可是假如他的真實來源並不是Amazon.com,那咱們可能就有被攻擊的危險。若是攻擊者使用域名污染(DNS cache poisoning)等技術對你的DNS服務器進行篡改,那麼你也許會把黑客的網站誤認爲是一個安全的受信網站(諸如Amazon.com),由於地址欄顯示的信息一切正常。這最後一步對證書頒發機構的檢查就是爲了防止這樣的事情發生。
如今咱們已經瞭解了Amazon.com的各項要求,而且知道了公共解密密鑰e和參數n。在通訊過程當中的任何一方也都知道了這些信息(佐證就是咱們經過Wireshark得到了這些信息)。如今咱們所須要作的事情就是生成一串竊密者/攻擊者都不能知道的隨機密碼。這並不像聽上去的那麼簡單。早在1996年,研究人員就發現了網景瀏覽器1.1的僞隨機數發生器僅僅利用了三個參數:當天的時間,進程ID和父進程ID。正如研究人員所指出的問題:這些用於生成隨機數的參數並不具備隨機性,並且他們相對來講比較容易被破解。
由於一切都是來源於這三個隨機數參數,因此在1996,利用當時的機器僅須要25秒鐘的時間就能夠破解一個SSL通訊。找到一種生成真正隨機數的方法是很是困難的,若是你不相信這一點,那就去問問Debian OpenSSL的維護工程師吧。若是隨機數的生成方式遭到破解,那麼創建在這之上的一系列安全措施都是毫無心義的。
在Windows操做系統中,用於加密目的隨機數都是利用一個叫作CryptGenRandom的函數生成的。這個函數的哈希表位對超過125個來源的數據進行抽樣!火狐瀏覽器利用CryptGenRandom函數和它自身的函數來構成它本身的僞隨機數發生器。(譯者注:之因此稱之爲僞隨機數是由於真正意義上的隨機數算法並不存在,這些函數仍是利用大量的時變、量變參數來經過複雜的運算生成相對意義上的隨機數,可是這些數之間仍是存在統計學規律的,只是想要找到生成隨機數的過程並不那麼容易)。
咱們並不會直接利用生成的這48字節的隨機密碼串,可是因爲不少重要的信息都是由他計算而來的,因此對隨機密碼串的保密就顯得格外重要。正如我以前所預料到的,火狐瀏覽器對隨機密碼串的保密十分嚴格,因此我不得不編譯了一個用於debug的版本。爲了觀察隨機密碼串,我還特意設置了SSLDEBUGFILE和SSLTRACE兩個環境變量。
其中,SSLDEBUGFILE顯示的就是隨機密碼串的值:
1
2
3
4
|
4456: SSL[131491792]: Pre-Master Secret [Len: 48]
03 01 bb 7b 08 98 a7 49 de e8 e9 b8 91 52 ec 81 ...{...I.....R..
4c c2 39 7b f6 ba 1c 0a b1 95 50 29 be 02 ad e6 L.9{......P)....
ad 6e 11 3f 20 c4 66 f0 64 22 57 7e e1 06 7a 3b .n.? .f.d"W~..z;
|
須要注意的是,這串數字從各類意義上來講都不是真正的隨機數,就拿它的前兩位來講:這就是根據TLS協議約定的TLS版本號(0301)。
咱們如今須要作的就是計算出Amazon.com所要求的密碼。由於Amazon.com但願使用「TLS_RSA_WITH_RC4_128_MD5」加密組,因此咱們使用RSA加密算法進行這一過程。你能夠將這48字節的隨機密碼串做爲初始參數,可是根據公共密鑰密碼標準(PKCS)#1 v1.5中的註釋,咱們須要用隨機數據將隨機密碼串填充到實際要求的參數大小(1024位二進制/128字節)。這樣的話攻擊者想要破解咱們的隨機密碼串就難上加難了。這也是咱們保障本身安全的最後一道防線,以防咱們在前面的步驟中犯了諸如重複使用密碼這樣的低級錯誤。若是咱們重複使用了隨機密碼串,因爲使用了隨機數填充,竊密者在網絡中攔截的也會是兩個不一樣的值。
一樣的,咱們很難直接觀察到火狐瀏覽器中的這一過程,因此我不得不在填充隨機數的函數中增長了debug的語句,使咱們可以觀察這一過程:
1 wrapperHandle = fopen("plaintextpadding.txt", "a"); 2 fprintf(wrapperHandle, "PLAINTEXT = "); 3 for(i = 0; i < modulusLen; i++) 4 { 5 fprintf(wrapperHandle, "%02X ", block[i]); 6 } 7 fprintf(wrapperHandle, "\r\n"); 8 fclose(wrapperHandle);
00 02 12 A3 EA B1 65 D6 81 6C 13 14 13 62 10 53 23 B3 96 85 FF 24 FA CC 46 11 21 24 A4 81 EA 30 63 95 D4 DC BF 9C CC D0 2E DD 5A A6 41 6A 4E 82 65 7D 70 7D 50 09 17 CD 10 55 97 B9 C1 A1 84 F2 A9 AB EA 7D F4 CC 54 E4 64 6E 3A E5 91 A0 06 00 03 01 BB 7B 08 98 A7 49 DE E8 E9 B8 91 52 EC 81 4C C2 39 7B F6 BA 1C0A B1 95 50 29 BE 02 AD E6 AD 6E 11 3F20 C4 66 F0 64 22 57 7E E1 06 7A 3B
火狐瀏覽器使用這個值計算出 ,咱們能夠看到它顯示在「客戶端交換密鑰」(Client Key Exchange)的記錄中:
在這個過程的最後,火狐瀏覽器會發送一個不加密的信息:一條「Change Cipher Spec」記錄:
經過這種方式:火狐瀏覽器要求Amazon.com在後面的通訊過程當中使用約定的加密方式傳輸信息。
若是咱們正確完成了以前的過程,而且各方都得到了48字節(256二進制位)的隨機密碼串。從Amazon.com的角度來看,這裏還有一些信任問題:隨機密碼串是由客戶端生成的,並無將任何服務器信息或者以前約定的信息加入其中。這一點,咱們會經過生成主密鑰的方式加以完善。根據協議規範約定,這個的計算過程爲:
1
|
master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random)
|
pre_master_secret就是咱們以前傳送的隨機密碼串,」master secret」是一串ASCII碼(例如:6d 61 73 74 65 72……),再鏈接上在客戶端問候和服務器問候(來自Amazon的)的信息。
PRF是在規範中約定的僞隨機函數,它將密鑰、ASCII碼標籤、哈希值整合在一塊兒。各有一半的參數分別使用MD5和SHA-1獲取哈希值。這是一種十分明智的作法,即便是想要單單破解相對簡單MD5和SHA-1也不是那麼容易的事情。並且這個函數會將返回值傳給自身直至迭代到咱們須要的位數。
利用這個函數,咱們生成了48字節的主密鑰:
4C AF 20 30 8F4C AA C5 66 4A 02 90 F2 AC 10 00 39 DB 1D E0 1F CB E0 E0 9D D7 E6 BE 62 A4 6C 18 06 AD 79 21 DB 82 1D 53 84 DB 35 A7 1F C1 01 19
如今,各方面已經有了主密鑰,根據協議約定,咱們須要利用PRF生成這個會話中所須要的各類密鑰,稱之爲「密鑰塊」(key block):
1
|
key_block = PRF(SecurityParameters.master_secret, "key expansion", SecurityParameters.server_random + SecurityParameters.client_random);
|
密鑰塊用於構成如下密鑰:
1
2
3
4
5
6
|
client_write_MAC_secret[SecurityParameters.hash_size]
server_write_MAC_secret[SecurityParameters.hash_size]
client_write_key[SecurityParameters.key_material_length]
server_write_key[SecurityParameters.key_material_length]
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]
|
由於咱們使用了相似於高級加密標準(AES)的密碼流代替了分組密碼咱們就不須要初始化向量(IVs)了。所以咱們只須要雙方的兩個16字節(128二進制位)的消息認證碼(Message Authentication Code,MAC),由於MD5的哈希值就是16字節的。此外,雙方也須要16字節(128二進制位)的RC4碼。因此咱們總共須要從密碼塊得到2*16 + 2*16 = 64字節的數據。
運行PRF,咱們能獲得如下值:
1
2
3
4
|
client_write_MAC_secret = 80 B8 F6 09 51 74 EA DB 29 28 EF 6F 9A B8 81 B0
server_write_MAC_secret = 67 7C 96 7B 70 C5 BC 62 9D 1D 1F 4A A6 79 81 61
client_write_key = 32 13 2C DD 1B 39 36 40 84 4A DE E5 6C 52 46 72
server_write_key = 58 36 C4 0D 8C 7C 74 DA 6D B7 34 0A 91 B6 8F A7
|
客戶端最後一次送出的握手信息是「結束信息」。這條信息保證了沒有人篡改握手信息,而且咱們已經知曉所必須的密鑰。客戶端將整個握手過程的所有信息都放入一個名爲「handshake_messages」的緩衝區。咱們能經過僞隨機函數利用主密鑰、「client finished」標籤、MD5和SHA-1的哈希值生成12字節的「區別數據」(verify_data):
1
|
verify_data = PRF(master_secret, "client finished", MD5(handshake_messages) + SHA-1(handshake_messages)) [12]
|
咱們在這個結果前面加上0x14(用於表示結束信息)和00 00 0c(用於表示verify_data 有12字節)。就像之後全部的加密過程同樣,咱們要在加密以前確保原始數據沒有被篡改。由於咱們使用的是「TLS_RSA_WITH_RC4_128_MD5」密碼組,這就意味着咱們須要使用MD5哈希函數。
有些人一聽到MD5函數就會嗤之以鼻,由於其自身的確存在一些缺陷。我本身固然也不會推薦這種算法。可是TLS的聰明之處就在於他並不直接使用MD5函數,只是利用哈希值的版原本校驗數據。只就意味着咱們並未直接應用到MD5(m):
HMAC_MD5(Key, m) = MD5((Key ⊕ opad) ++ MD5((Key ⊕ ipad) ++ m)
(其中,⊕表示的是異或運算)
在實際中:
HMAC_MD5(client_write_MAC_secret, seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment));
正如你所見,咱們在函數中使用了一個根據明文(在這裏明文叫作「TLSCompressed」)編號的序號(seq_num)。這個序號的做用就是爲了阻止攻擊者在數據流中間插入以前被其截獲的信息。若是發生了這樣的攻擊,序號就能清楚的警告咱們數據中的異常。一樣的,這個序號也能幫助咱們發現攻擊者從數據流中剔除的數據。
剩下的工做只剩下啊加密這些數據了!
咱們以前協商過的密碼組是「TLS_RSA_WITH_RC4_128_MD5」。這就意味着咱們須要使用RC4(Ron`s code 4)加密規則進行數據流的加密。羅納德李威斯特(Ron Rivest)開發了這種基於一個256字節的Key產生隨機加密效果的算法。這個算法簡單到你幾分鐘就能記住。
首先,RC4生成一個256字節的數組S,並用0-255填充。接下來的工做就是將須要將KEY混合插入進數組中並反覆迭代。你能夠編寫一個狀態機,利用它來闡釋隨機字節。爲了產生隨機字節,咱們須要將S數組打亂,如圖所示:
爲了加密一個字節,咱們將其與隨機字節進行異或運算。記住:一位二進制數與1做異或運算會翻轉(譯者注:即1^1=0;1^0=1)。由於咱們利用的是隨機數,因此從統計學的角度來說,有一半的數被翻轉。這種隨機翻轉的現象就是咱們加密數據的有效方法。正如你所見,這並不複雜,並且計算速度十分快,我認爲這也是Amazon.com選擇它的緣由之一。
記得咱們有「client_write_key」和「server_write_key」嗎?這就意味咱們須要兩個RC4實例:一個用來加密瀏覽器向服務器傳送的數據,一個用來解密服務器向瀏覽器傳送的數據。
「client_write_key」最初的幾個字節是7E 20 7A 4D FE FB 78 A7 33 …若是咱們對這些字節和未加密的數據頭以及版本信息(「14 00 00 0C98 F0 AE CB C4 …」)進行異或運算,咱們就能獲得在Wireshark中看到的加密信息了:
服務器端作的幾乎是相同的事情。它們發送了一個密鑰協議的說明和一個包含所有握手過程的結束信息,其中有結束信息的解密版本。所以,這種機制就保證了客戶端和服務器能成功的解密信息。
如今,從咱們點擊了按鈕以後已通過去了220毫秒,咱們終於爲應用層作好了準備!如今,咱們發送的普通的HTTP數據流會經過TLS層的加密實例進行加密,在服務器的解密實例進行解密。並且TLS會對數據進行哈希校驗,以保證數據內容的準確性。
在這個時候,整個的握手過程就結束了。咱們的TLS記錄內容如今有了23條(0x17)。加密數據以「17 03 01」開頭,表示了記錄類型和TLS版本,後面緊跟着加密數據的大小和哈希校驗值。
加密的數據的明文以下:
1
2
3
4
5
6
7
8
9
10
|
GET /gp/cart/view.html/ref=pd_luc_mri HTTP/1.1
Host: www.amazon.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.10) Gecko/2009060911 Minefield/3.0.10 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
...
|
在Wireshark中顯示以下:
惟一有趣的地方是序號是按照記錄來增加,這條記錄是1,下一條就是2。
服務器端利用「server_write_key」作着一樣的事情。咱們能看到服務器的相應結果,包括程序開頭的指示位:
解密後的信息以下:
1
2
3
4
5
6
|
HTTP/1.1 200 OK
Date: Wed, 10 Jun 2009 01:09:30 GMT
Server: Server
...
Cneonction: close
Transfer-Encoding: chunked
|
這就是一個來自Amazon負載平衡服務器的普通HTTP迴應:包含了非描述性的服務器信息「Server: Server」和一個拼錯了的「Cneonction: close」。
TLS層在應用層的下面,因此軟件和服務器可以像正常的HTTP傳輸那樣進行工做,惟一的區別就是傳輸的數據會被TLS層進行加密。
OpenSSL是一個應用很廣的TLS開源庫。
整個鏈接會一直保持,除非有一方提出了「關閉警告(closure alert)」而且關閉了鏈接。若是咱們在鏈接斷開後的短期內再次提出鏈接請求,咱們可使用以前使用過的key來進行鏈接,從而避免一次新的握手過程。(這個要取決於服務器端key的有效時間。)
須要注意的是:應用程序能夠發送任何數據,可是HTTPS的特殊之處在於WEB應用的普遍普及。要知道還有很是多的基於TCP/IP而且使用TLS進行數據加密的協議(如FTPS,sSMTP)。使用TLS要比你本身發明一種是數據加密方案便捷的多。何況,你所使用的安全協議必定要足夠安全。
TLS RFC的文檔包含了更多的信息,有須要的朋友們能夠本身查閱,咱們在這裏只是簡單的介紹了其中的過程和原理,觀察了這220毫秒內發生在火狐瀏覽器和Amazon服務器之間發生的故事:由Amazon.com基於速度和安全的綜合考慮選擇的「TLS_RSA_WITH_RC4_128_MD5」密碼組在HTTPS鏈接創建過程當中的所有流程。
正如咱們所看到的那樣,若是有人能對Amazon服務器的參數n進行因式分解獲得p和q的話,那他就能破解所有的基於亞馬遜證書的安全通訊。因此Amazon爲這個參數設置了有效期以防止這種事情的發生:
在咱們提供的密碼族中,有一組密碼組「TLS_DHE_RSA_WITH_AES_256_CBC_SHA」使用了Diffie-Hellman密鑰交換,並所以能提供良好的前向安全特性。這就意味着若是有人破解了交換密鑰的數學運算方式,他們也不能利用這個來破解其餘的會話。可是他的一個劣勢在於其運算需求更大的數字和更高的運算能力。AES算法在不少密碼組中都出現了,它與RC4的不一樣之處在於它每次使用的是16字節的「塊」而RC4使用的是單字節。由於其key最高能到256位二進制位,因此通常認爲它比RC4的安全性更高。
在短短的220毫秒的時間裏,兩個節點經過互聯網鏈接起來,而且利用一系列手段創建起了互信機制,構建了加密算法,進行加密數據的傳輸。
正是由於如此,咱們故事的主人公才能在Amazon上買到他想要的牛奶!
(譯者注:做者全部相關的程序已經提交到Github上,地址:https://github.com/moserware/TLS-1.0-Analyzer/tree/master)