HTTPS連線的前幾毫秒發生了什麼

xiniubook發表於2013-09-25

提示:英文原文寫於2009年,當時的Firefox和最新版的Firefox,介面也有很大改動。以下是正文。

花了數小時閱讀了如潮的好評,Bob最終迫不及待為他購買的托斯卡納全脂牛奶點選了“進行結算”,然後……

哇!剛剛發生了什麼?

在點選按鈕過後的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通訊了。

 

客戶端問候(Client Hello)

TLS將全部的通訊以不同方式包裹為“記錄”(Records)。我們可以看到,從瀏覽器發出的第一個位元組為0x16(十進位制的22),它表示了這是一個“握手”記錄。

接下來的兩個位元組是0x0301,它表示了這是一條版本為3.1的記錄,同時也向我們表明了TLS1.0實際上是基於SSL3.1構建而來的。

整個握手記錄被拆分為數條資訊,其中第一條就是我們的客戶端問候(Client Hello),即0x01。在客戶端問候中,有幾個需要著重注意的地方:

  •  隨機數:

在客戶端問候中,有四個位元組以Unix時間格式記錄了客戶端的協調世界時間(UTC)。協調世界時間是從1970年1月1日開始到當前時刻所經歷的秒數。在這個例子中,0x4a2f07ca就是協調世界時間。在他後面有28位元組的隨機數,在後面的過程中我們會用到這個隨機數。

  • SID(Session ID):

在這裡,SID是一個空值(Null)。如果我們在幾秒鐘之前就登陸過了Amazon.com,我們有可能會恢復之前的會話,從而避免一個完整的握手過程。

  • 密文族(Cipher Suites):

密文族是瀏覽器所支援的加密演算法的清單。整個密文族是由推薦的加密演算法“TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA”和33種其他加密演算法所組成。別擔心其他的加密演算法會出現問題,我們一會兒就會發現Amazon也沒有使用推薦的加密演算法。

  •  Server_name擴充套件:

通過這種方式,我們能夠告訴Amazon.com:瀏覽器正在試圖訪問https://www.amazon.com。這確實方便了很多,因為我們的TLS握手時間發生在HTTP通訊之前,而HTTP請求會包含一個“Host頭”,從而使那些為了節約成本而將數百個網站域名解析到一個IP地址上的網路託管商能夠分辨出一個網路請求對應的是哪個網站。傳統意義上的SSL同樣要求一個網站請求對應一個IP地址,但是Server_name擴充套件則允許伺服器對瀏覽器的請求授予相對應的證照。如果沒有其他的請求,Server_name擴充套件應該允許瀏覽器訪問這個IPV4地址一週左右的時間。

 

伺服器問候(Server Hello)

Amazon.com回覆的握手記錄由兩個比較大的包組成(2551位元組)。記錄中包含了0x0301的版本資訊,意味著Amazon同意我們使用TLS1.0訪問的請求。這條記錄包含了三條有趣的子資訊:

1.伺服器問候資訊(Server Hello)(2):

  1. 我們得到了伺服器的以Unix時間格式記錄的UTC和28位元組的隨機數。
  2. 32位元組的SID,在我們想要重新連線到Amazon.com的時候可以避免一整套握手過程。
  3. 在我們所提供的34個加密族中,Amazon挑選了“TLS_RSA_WITH_RC4_128_MD5”(0x0004)。這就意味著Amazon會使用RSA公鑰加密演算法來區分證照籤名和交換金鑰,通過RC4加密演算法來加密資料,利用Md5來校驗資訊。我們之後會深入的研究這一部分內容。我個人認為,Amazon選擇這一密碼組是有其自身的原因的。在我們所提供的密碼族中,這一加密組的加密方式是CPU佔用最低的,這就允許Amazon的每臺伺服器接受更多的連線。當然了,也許還有一個原因是,Amazon是在向這三種加密演算法的發明者Ron Rivest(羅恩·李·維斯特)致敬。

2.證照資訊(11):

這段巨大的資訊共有2464位元組,其證照允許客戶端在Amazon伺服器上進行認證。這個證照其實並沒有什麼奇特之處,你能通過瀏覽器瀏覽它的大部分內容。

3.伺服器問候結束資訊(14):

這是一個零位元組資訊,用於告訴客戶端整個“問候”過程已經結束,並且表明伺服器不會再向客戶端詢問證照。

 

校驗證照

此時,瀏覽器已經知道是否應該信任Amazon.com。在這個例子中,瀏覽器通過證照確認網站是否受信,它會檢查 Amazon.com 的證照,並且確認當前的時間是在“最早時間”2008年8月26日之後,在“最晚時間”2009年8月27日之前。瀏覽器還會確認證照所攜帶的公共金鑰已被授權用於交換金鑰。

為什麼我們要信任這個證照?

證照中所包含的簽名是一串非常長的大端格式的數字:

任何人都可以向我們傳送這些位元組,但我們為什麼要信任這個簽名?為了解釋這個問題,我們首先要回顧一些重要的數學知識:

RSA加密演算法的基礎介紹

人人常常會問,程式設計和數學之間有什麼聯絡?證照就為數學在程式設計領域的應用提供了一個實際的例子。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連線的客戶端確認證照正確,並檢查證照中所對應的主機名是否正確,因為這是我們應對中間人攻擊的唯一方式!” (譯者注:中間人攻擊是一種“間接”的入侵攻擊,這種攻擊模式是通過各種技術手段將受入侵者控制的一臺計算機虛擬放置在網路連線中的兩臺通訊計算機之間,這臺計算機就稱為“中間人”。)

這樣的檢查是為了防止中間人攻擊:因為我們對整個信任鏈條上的人都採取了完全信任的態度,認為他們並不會進行黑客行為,就像我們的證照中所聲稱它是來自Amazon.com,但是假如他的真實來源並非Amazon.com,那我們可能就有被攻擊的危險。如果攻擊者使用域名汙染(DNS cache poisoning)等技術對你的DNS伺服器進行篡改,那麼你也許會把黑客的網站誤認為是一個安全的受信網站(諸如Amazon.com),因為位址列顯示的資訊一切正常。這最後一步對證照頒發機構的檢查就是為了防止這樣的事情發生。

 

隨機密碼串(Pre-Master Secret)

現在我們已經瞭解了Amazon.com的各項要求,並且知道了公共解密金鑰e和引數n。在通訊過程中的任何一方也都知道了這些資訊(佐證就是我們通過Wireshark獲得了這些資訊)。現在我們所需要做的事情就是生成一串竊密者/攻擊者都不能知道的隨機密碼。這並不像聽上去的那麼簡單。早在1996年,研究人員就發現了網景瀏覽器1.1的偽隨機數發生器僅僅利用了三個引數:當天的時間,程式ID和父程式ID。正如研究人員所指出的問題:這些用於生成隨機數的引數並不具有隨機性,而且他們相對來說比較容易被破解。

因為一切都是來源於這三個隨機數引數,所以在1996,利用當時的機器僅需要25秒鐘的時間就可以破解一個SSL通訊。找到一種生成真正隨機數的方法是非常困難的,如果你不相信這一點,那就去問問Debian OpenSSL的維護工程師吧。如果隨機數的生成方式遭到破解,那麼建立在這之上的一系列安全措施都是毫無意義的。

在Windows作業系統中,用於加密目的隨機數都是利用一個叫做CryptGenRandom的函式生成的。這個函式的雜湊表位對超過125個來源的資料進行抽樣!火狐瀏覽器利用CryptGenRandom函式和它自身的函式來構成它自己的偽隨機數發生器。(譯者注:之所以稱之為偽隨機數是因為真正意義上的隨機數演算法並不存在,這些函式還是利用大量的時變、量變引數來通過複雜的運算生成相對意義上的隨機數,但是這些數之間還是存在統計學規律的,只是想要找到生成隨機數的過程並不那麼容易)。

我們並不會直接利用生成的這48位元組的隨機密碼串,但是由於很多重要的資訊都是由他計算而來的,所以對隨機密碼串的保密就顯得格外重要。正如我之前所預料到的,火狐瀏覽器對隨機密碼串的保密十分嚴格,所以我不得不編譯了一個用於debug的版本。為了觀察隨機密碼串,我還特地設定了SSLDEBUGFILE和SSLTRACE兩個環境變數。

其中,SSLDEBUGFILE顯示的就是隨機密碼串的值:

需要注意的是,這串數字從各種意義上來說都不是真正的隨機數,就拿它的前兩位來說:這就是根據TLS協議約定的TLS版本號(0301)。

 

密碼交換(Trading Secret)

我們現在需要做的就是計算出Amazon.com所要求的密碼。因為Amazon.com希望使用“TLS_RSA_WITH_RC4_128_MD5”加密組,所以我們使用RSA加密演算法進行這一過程。你可以將這48位元組的隨機密碼串作為初始引數,但是根據公共金鑰密碼標準(PKCS)#1 v1.5中的註釋,我們需要用隨機資料將隨機密碼串填充到實際要求的引數大小(1024位二進位制/128位元組)。這樣的話攻擊者想要破解我們的隨機密碼串就難上加難了。這也是我們保障自己安全的最後一道防線,以防我們在前面的步驟中犯了諸如重複使用密碼這樣的低階錯誤。如果我們重複使用了隨機密碼串,由於使用了隨機數填充,竊密者在網路中攔截的也會是兩個不同的值。

同樣的,我們很難直接觀察到火狐瀏覽器中的這一過程,所以我不得不在填充隨機數的函式中增加了debug的語句,使我們能夠觀察這一過程:

在這個例子中,完整的填充後的隨機密碼串為:

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在後面的通訊過程中使用約定的加密方式傳輸資訊。

 

獲得主金鑰(Master Secret)

如果我們正確完成了之前的過程,並且各方都獲得了48位元組(256二進位制位)的隨機密碼串。從Amazon.com的角度來看,這裡還有一些信任問題:隨機密碼串是由客戶端生成的,並沒有將任何伺服器資訊或者之前約定的資訊加入其中。這一點,我們會通過生成主金鑰的方式加以完善。根據協議規範約定,這個的計算過程為:

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):

金鑰塊用於構成以下金鑰:

因為我們使用了類似於高階加密標準(AES)的密碼流代替了分組密碼我們就不需要初始化向量(IVs)了。因此我們只需要雙方的兩個16位元組(128二進位制位)的訊息認證碼(Message Authentication Code,MAC),因為MD5的雜湊值就是16位元組的。此外,雙方也需要16位元組(128二進位制位)的RC4碼。所以我們總共需要從密碼塊獲得2*16 + 2*16 = 64位元組的資料。

執行PRF,我們能得到以下值:

 

準備加密!

客戶端最後一次送出的握手資訊是“結束資訊”。這條資訊保證了沒有人篡改握手資訊,並且我們已經知曉所必須的金鑰。客戶端將整個握手過程的全部資訊都放入一個名為“handshake_messages”的緩衝區。我們能通過偽隨機函式利用主金鑰、“client finished”標籤、MD5和SHA-1的雜湊值生成12位元組的“區別資料”(verify_data):

我們在這個結果前面加上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)。這個序號的作用就是為了阻止攻擊者在資料流中間插入之前被其截獲的資訊。如果發生了這樣的攻擊,序號就能清楚的警告我們資料中的異常。同樣的,這個序號也能幫助我們發現攻擊者從資料流中剔除的資料。

剩下的工作只剩下啊加密這些資料了!

 

RC4加密

我們之前協商過的密碼組是“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版本,後面緊跟著加密資料的大小和雜湊校驗值。

加密的資料的明文如下:

在Wireshark中顯示如下:

唯一有趣的地方是序號是按照記錄來增長,這條記錄是1,下一條就是2。

伺服器端利用“server_write_key”做著同樣的事情。我們能看到伺服器的相應結果,包括程式開頭的指示位:

解密後的資訊如下:

這就是一個來自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

相關文章