使用 BoringSSL 優化 HTTPS 加密演算法選擇

Jerry Qu發表於2015-10-20

前不久,一位朋友在我部落格評論中,問到:類似於 Google 那樣電腦訪問使用 AES,手機訪問使用 CHACHA20 的演算法是怎麼實現的(詳情)。最近我研究了一下這個問題,現在我的部落格也支援這個特性了。今天抽空介紹一下我的實現步驟,供喜歡折騰的朋友們參考。

對稱內容加密

我們知道,每個 TLS 會話都是在握手階段通過非對稱加密得出對稱加密金鑰,而本次會話雙方一直會用這個金鑰進行流量的對稱加密。這樣做是出於效能考慮,畢竟對稱加密速度要快得多,更適合全流量使用。

對稱加密演算法有流式、分組兩種。RC4 就是一個常見的流式加密演算法,不過已被證實不再安全,應該停止使用。Google 推出了一種名為 ChaCha20-Poly1305 的流式加密新演算法,已經內建於各大平臺的 Chrome 之中。ChaCha20 除了更安全,還針對 ARM 做了優化,在移動裝置上使用速度更快、更省電。以下是它與 AES-GCM 在加密速度上的對比(via):

AES-GCM 是目前推薦使用的分組加密模式,它的缺點是計算量大,導致效能和電量開銷比較大。為此,Intel 推出了一個名為 AES NI(Advanced Encryption Standard new instructions)的 x86 指令集擴充套件,從硬體上提供對 AES 的支援(詳情)。Intel 自家 CPU 從 Westmere 平臺開始支援 AES-NI,目前在 PC 端 AES-NI 的普及率顯然很高。對於支援 AES-NI 的裝置來說,使用 AES-GCM 加密演算法無疑是最優選擇,以下是一份對比(測試使用支援 AES-NI 的 Intel Xeon E3-1220 V2 @ 3.10GHz,via):

可以看到,儘管純軟體實現的 ChaCha20 演算法已經十分優秀,但跟有 AES-NI 加持的 AES-GCM 比起來還是差距明顯。

綜上,我們很容易想到「僅針對支援 AES-NI 的終端使用 AES-GCM 演算法,否則使用 ChaCha20」無疑是一個非常完美的方案。

BoringSSL

之前文章介紹過,基於 LibreSSL 編譯 Nginx,可以輕鬆地使用 ChaCha20。但問題是一旦配置了 ChaCha20,只要終端支援,無論桌面裝置還是移動裝置都會使用它。

BoringSSL 是 Google 從 OpenSSL 拉出來的一個獨立發展的分支,目前跟 OpenSSL 相比已經有很多不同之處了。BoringSSL 支援了一種名為「等價加密演算法組(Equal preference cipher groups)」的配置(詳情),正好可以滿足我們這個需求。

基於 BoringSSL 編譯 Nginx 之後,可以像下面這樣配置 ssl_ciphers:

方括號之中的配置就是「等價加密演算法組」,用豎線隔開的兩種演算法,會被自動應用於最合適的場景(支援 AES-NI 優先使用 AES-GCM,否則優先使用 ChaCha20)。最後的 DES-CBC3-SHA 是為了支援 Windows XP 上的 IE8 而加上去的。

下圖中,同樣是訪問本部落格,左側是 Mac Chrome 的截圖,右側是 iPhone Chrome 的截圖:

可以看到,只有移動端才使用了 ChaCha20。

這裡簡單介紹一下「等價加密演算法組」的原理:我們知道客戶端建立 TLS 連線時,在傳送的 Client Hello 中會帶上自己支援的加密演算法,供服務端從中挑選。由於老舊客戶端會支援一些不安全的加密演算法,為了提高傳輸安全,通常會在服務端指定一個可用演算法列表,最終使用的加密型別取決於二者的交集,並按服務端優先順序取第一個;如果沒有交集,直接終止會話。在 Nginx 中這個功能通過將 ssl_prefer_server_ciphers 設定為 on 開啟。

那麼問題來了,對於同時支援 AES-GCM 和 ChaCha20 的 Chrome 來說,服務端列表無論把哪個放前面都會導致另外一個完全沒機會被選中。而「等價加密演算法組」的意義在於,等價組內的演算法具有相同優先順序。這樣,客戶端可以把想要優先使用的加密演算法放在前面。舉例說明二者的區別(開啟 ssl_prefer_server_ciphers 條件下):

  • 不支援等價組時,如果服務端列表是:A、B、C,瀏覽器 1 支援:A、B,最終使用 A;瀏覽器 2 支援 B、A,最終使用 A,瀏覽器 3 支援 C、A,最終使用 A;
  • 支援等價組時,如果服務端列表是:[A|B]、C,瀏覽器 1 支援:A、B,最終使用 A;瀏覽器 2 支援 B、A,最終使用 B,瀏覽器 3 支援 C、A,最終使用 A;

可以看到,開啟 ssl_prefer_server_ciphers 可以讓會話使用最安全的加密演算法(前提是服務端配置正確),而「等價加密演算法組」還可以讓瀏覽器有區域性調整的許可權。

補充一下通過 Mac Chrome 和 iPhone Chrome 分別訪問我的部落格傳送的加密演算法列表(使用 wireshark 抓包,在 Client Hello 握手中可獲得):

可以看到,Chrome 瀏覽器確實是會在不同平臺上傳送不同順序的演算法列表,只要服務端配置了「等價加密演算法組」就可以實現本文所描述的功能。

最後提醒大家:現階段 BoringSSL 不支援 OCSP Stapling。改用 BoringSSL 後,如果在 ssllabs 測試中發現這一項變成 off 不要吃驚。

詳細配置步驟

之所以把這部分內容放在最後,是因為折騰起來有點費勁,嫌麻煩的同學可以直接忽略之後所有內容。

以下步驟在我兩臺系統為 Ubuntu 14.04.3 的 VPS 上都能正常執行。如果你遇到了問題,請留言指出。

首先,獲取編譯所需的 Nginx 和 BoringSSL 原始碼,Nginx 從 1.7.4 開始支援 BoringSSL,這裡我直接使用最新版:

現在,當前目錄下應該有這兩個子目錄:

確認無誤後,還要做一些準備工作:

編譯 BoringSSL:

現在可以編譯 Nginx 了:

touch 後可以開始 make 和 make install 了。這期間可能還會遇到這樣一個報錯:

這是因為 BoringSSL 刪掉了這個變數。找到報錯檔案 src/event/ngx_event_openssl.c 中對應的位置:

刪掉這一行,或者加個判斷都可以解決問題:

其他應該沒什麼問題了。make install 之前記得先停掉 nginx 服務,不然很可能需要手動殺死之前的 nginx 程式。一切妥當後,參考前文修改 ssl_ciphers 並啟動服務,搞定收工!

本文連結:https://imququ.com/post/optimize-ssl-ciphers-with-boringssl.html參與評論
–EOF–

相關文章