SSL通信涉及兩方的參與者,通常採用的模型是Client/Server。如果我們開發Client端產品(比如瀏覽器),可能會和多方的Server產品對接。那麼問題來了,雙方是如何知道對方使用了國密算法呢?這個問題非常重要,只有雙方採用同樣的加密標準,才能正常通信。難道採用“天王蓋地虎,寶塔鎮河妖”的接頭暗號?
在揭開這個謎底之前,我們先了解一下OID(Object Identifier,對象標誌符),然後再聊一聊密碼套件(Cipher Suite),就可以慢慢理解SSL通信雙方是如何接上頭的。當然,整個SSL通信涉及到很多標準和協議,不是一篇文章能講完的,所以本文只是拋磚引玉,探討一些基本的概念。可能對於資深人士而言,不值得一提,但我初入門時,也是摸索了好久,才慢慢能深入到細節。
OID
OID是由ISO/IEC、ITU-T國際標準化組織上世紀80年代聯合提出的標識機制,其野心很大,爲任何類型的對象(包括實體對象、虛擬對象和組合對象)進行全球唯一命名。通過唯一的編碼,我們就可以識別出對象。但要爲所有對象進行唯一命名,其難度和工作量都很大,所以它採用了分層樹形結構。
OID對應於“OID樹”或層次結構中的一個節點,該節點是使用ITU的OID標準X.660正式定義的。樹的根包含以下三個起點:
0:ITU-T
1:ISO
2:ITU-T/ISO聯合發佈
樹中的每個節點都由一系列由句點分隔的整數表示。比如,表示英特爾公司的OID如下所示:
1.3.6.1.4.1.343
對應於OID樹中的含義:
1 ISO
1.3 識別組織
1.3.6 美國國防部
1.3.6.1 互聯網
1.3.6.1.4 私人
1.3.6.1.4.1 IANA企業編號
1.3.6.1.4.1.343 英特爾公司
這裏採用分而治之的策略,解決編碼重複問題。樹中的每個節點均由分配機構控制,該機構可以在該節點下定義子節點,併爲子節點委託分配機構。
在上面的例子中,根節點“1”下的節點號由ISO分配,“1.3.6”下的節點由美國國防部分配,“1.3.6.1.4.1”下的節點由IANA分配,“1.3.6.1.4.1.343”下的節點由英特爾公司分配,依此類推。只要有需求,可以一直往下分配下去,也解決了編碼不夠的問題。
在實際應用中,ISO/IEC國際標準化機構維護頂層OID標籤,各個國家負責該國家分支下的OID分配、註冊和解析等工作,實現自我管理和維護。
相應的,針對國密,國家密碼局也定義了各類對象的標識符:
詳細可參考 GM/T 0006-2012 這份標準。
像"1.2.156.10197.1.100"這種字符串,人讀起來比較直觀,但對於計算機處理而言,卻是大大的不方便,要知道字符串處理的效率非常低,所以在程序代碼中,對OID又進行了一次編碼。
在GmSSL源碼中,原始的OIDs定義在objects.txt文件中,在文件的尾部,我們可以看到國密的相關定義:
# GM/T
member-body 156 : ISO-CN : ISO CN Member Body
ISO-CN 10197 : oscca
oscca 1 : sm-scheme
sm-scheme 103 1 : SSF33-ECB : ssf33-ecb
sm-scheme 103 2 : SSF33-CBC : ssf33-cbc
!Cname ssf33-ofb128
sm-scheme 103 3 : SSF33-OFB : ssf33-ofb
!Cname ssf33-cfb128
sm-scheme 103 4 : SSF33-CFB : ssf33-cfb
sm-scheme 103 5 : SSF33-CFB1 : ssf33-cfb1
sm-scheme 103 6 : SSF33-CFB8 : ssf33-cfb8
sm-scheme 103 7 : SSF33-CBC-MAC : ssf33-cbc-mac
sm-scheme 102 1 : SM1-ECB : sm1-ecb
sm-scheme 102 2 : SM1-CBC : sm1-cbc
!Cname sm1-ofb128
sm-scheme 102 3 : SM1-OFB : sm1-ofb
!Cname sm1-cfb128
sm-scheme 102 4 : SM1-CFB : sm1-cfb
sm-scheme 102 5 : SM1-CFB1 : sm1-cfb1
sm-scheme 102 6 : SM1-CFB8 : sm1-cfb8
# SM2 OIDs
sm-scheme 301 : sm2p256v1
sm-scheme 301 1 : sm2sign
sm-scheme 301 2 : sm2exchange
sm-scheme 301 3 : sm2encrypt
通過 objects.pl 腳本,生成 obj_data.h文件,這個纔是在代碼中使用到的OID編碼。
boringssl也類似,不過其採用了 go 語言編寫的轉換腳本。
密碼套件
僅僅定義了OID還不夠,因爲國密並不是一個單一的標準,包含了很多加密、解密、哈希等算法,可以形成很多種組合,不能簡單假定對方採用了國密就可以建立通信。
在SSL通信開始,雙方就需要進行協商,採用何種算法進行通信。這裏需要重點理解密碼套件(CipherSuite)的概念。
密碼套件是一系列密碼學算法的組合,主要包含多個密碼學算法:
-
身份驗證算法。
-
密碼協商算法。
-
加密算法或者加密模式。
-
HMAC算法的加密基元。
-
PRF算法的加密基元,需要注意的是,不同的TLS/SSL協議版本、密碼套件,PRF算法最終使用的加密基元和HMAC算法使用的加密基元是不一樣的。
密碼套件的構成如下圖所示:
密碼套件決定了本次連接採用哪一種加密算法、密鑰協商算法、HMAC算法,協商的結果就是雙方都認可的密碼套件。
密碼套件協商過程有點類似於客戶採購物品的過程,客戶(客戶端)在向商家(服務器端)買東西之前需要告知商家自己的需求、預算,商家瞭解用戶的需求後,根據用戶的具體情況(比如客戶願意接受的價格,客戶期望物品的使用年限)給用戶推薦商品,只有雙方都滿意了,交易才能完成。對於TLS/SSL協議來說,只有協商出密碼套件,才能進行下一步的工作。
除了現有國際上標準的密碼套件,國密還定義了一系列的密碼套件:
代碼可以參考 gmtls.h 文件,也可以使用GMSSL的命令查看所支持的密碼套件:
openssl ciphers -V | column -t
與國密相關的密碼套件有:
0xE1,0x07 - ECDHE-SM2-WITH-SMS4-GCM-SM3 TLSv1.2 Kx=ECDH Au=SM2 Enc=SMS4GCM(128) Mac=AEAD
0xE1,0x02 - ECDHE-SM2-WITH-SMS4-SM3 TLSv1.2 Kx=ECDH Au=SM2 Enc=SMS4(128) Mac=SM3
0xF1,0x20 - ECDHE-PSK-WITH-SMS4-CBC-SM3 TLSv1 Kx=ECDHEPSK Au=PSK Enc=SMS4(128) Mac=SM3
0xE0,0x17 - SM9-WITH-SMS4-SM3 GMTLSv1.1 Kx=SM9 Au=SM9 Enc=SMS4(128) Mac=SM3
0xE0,0x15 - SM9DHE-WITH-SMS4-SM3 GMTLSv1.1 Kx=SM9DHE Au=SM9 Enc=SMS4(128) Mac=SM3
0xE0,0x13 - SM2-WITH-SMS4-SM3 GMTLSv1.1 Kx=SM2 Au=SM2 Enc=SMS4(128) Mac=SM3
0xE0,0x11 - SM2DHE-WITH-SMS4-SM3 GMTLSv1.1 Kx=SM2DHE Au=SM2 Enc=SMS4(128) Mac=SM3
0xE0,0x1A - RSA-WITH-SMS4-SHA1 GMTLSv1.1 Kx=RSA Au=RSA Enc=SMS4(128) Mac=SHA1
0xE0,0x19 - RSA-WITH-SMS4-SM3 GMTLSv1.1 Kx=RSA Au=RSA Enc=SMS4(128) Mac=SM3
0xF1,0x01 - PSK-WITH-SMS4-CBC-SM3 SSLv3 Kx=PSK Au=PSK Enc=SMS4(128) Mac=SM3
-
第一列:數值代表密碼套件的編號,每個密碼套件的編號由IANA定義。
-
第二列:代表密碼套件的名稱,雖然密碼套件編號是一致的,不同的TLS/SSL協議實現其使用的名稱可能是不一樣的。
-
第三列:表示該密碼套件適用於哪個TLS/SSL版本的協議。
-
第四列:表示密鑰協商算法。
-
第五列:表示身份驗證算法。
-
第六列:表示加密算法、加密模式、密鑰長度。
-
第七列:表示HMAC算法。其中AEAD表示採用的是AEAD加密模式(比如AES128-GCM),無須HMAC算法。
值得注意的是,這裏的編碼又沒有采用OID,這也是開發過程中需要注意的,不同的地方使用了不同標準規範,需要在開發中翻閱相應的協議和規範。
可以看出,GmSSL並沒有實現所有的國密的密碼套件,但同時又擴充了幾個標準未定義的密碼套件,比如ECDHE-SM2-WITH-SMS4-GCM-SM3、ECDHE-SM2-WITH-SMS4-SM3等。這就體現出協商的重要性了,對雙方所支持的密碼套件取一個交集,從中選擇一個。如果不存在交集,協商也就不成功。注意,協商過程中服務器端可能會禁用一些不太安全的密碼套件(比如歷史遺留的一些現今已不太安全的算法),這時即使雙方都支持,也可能協商不成功。
我們可以測試服務器是否支持某個特定的密碼套件:
openssl s_client -cipher "ECDHE-SM2-WITH-SMS4-SM3" -connect sm2test.ovssl.cn:443 -tls1_2 -servername sm2test.ovssl.cn
當然,如何協商出密碼套件,涉及到SSL通信協議細節,如果開發中涉及到,就需要翻閱相應的RFC,這裏不做過多展開。
小結
本文簡單介紹了OID和密碼套件的概念,這對於理解SSL通信有很重要的幫助,如果從事SSL開發,開始面對一大堆的協議和標準,可能會懵圈,希望本文能給你一個探索國密的入口通道。在和第三方服務進行對接時,由於對這些概念理解不深,也是掉了很多坑,希望本文對你有所幫助。
如果大家有什麼問題,歡迎交流。