1、引言
在社區中,分享了很多篇基于Netty編寫的IM聊天入門文章(比如《跟著源碼學IM》系列、《基于Netty,從零開發IM》系列等),在這些文章中分享了各種IM通信算法原理和功能邏輯的實現。但是這樣簡單的IM聊天系統是比較容易被竊聽的,如果想要在里面說點悄悄話是不太安全的。
怎么辦呢?學過密碼學的朋友可能就想到了一個解決辦法,聊天的時候對消息加密,處理的時候再對消息進行解密。是的,道理就是這樣。
但密碼學本身的理論就很復雜,加上相關的知識和概念又太多太雜,對于IM入門者來說,想要快速理清這些概念并實現合適的加解密方案,是比較頭疼的。
本文正好借此機會,以Netty編寫的IM聊天加密為例,為入門者理清什么是PKI體系、什么是SSL、什么是OpenSSL、以及各類證書和它們間的關系等,并在文末附上簡短的Netty代碼實示例,希望能助你通俗易懂地快速理解這些知識和概念!
補充說明:本文為了讓文章內容盡可能言簡意賅、通俗易懂,盡量不深入探討各個技術知識和概念,感興趣的讀者可以自行查閱相關資料進一步學習。
學習交流:
(本文已同步發布于:http://www.52im.net/thread-4104-1-1.html)
2、相關文章
- 《即時通訊安全篇(一):正確地理解和使用Android端加密算法》
- 《即時通訊安全篇(二):探討組合加密算法在IM中的應用》
- 《即時通訊安全篇(三):常用加解密算法與通訊安全講解》
- 《即時通訊安全篇(四):實例分析Android中密鑰硬編碼的風險》
- 《即時通訊安全篇(五):對稱加密技術在Android平臺上的應用實踐》
- 《即時通訊安全篇(六):非對稱加密技術的原理與應用實踐》
- 《即時通訊安全篇(十):IM聊天系統安全手段之通信連接層加密技術》
- 《即時通訊安全篇(十一):IM聊天系統安全手段之傳輸內容端到端加密技術》
3、什么是PKI?
我們需要先了解一下公鑰和私鑰的加密標準體系PKI。
3.1 基本概念
PKI的全稱是Public Key Infrastructure,是指支持公鑰管理體制的基礎設施,提供鑒別、加密、完整性和不可否認性服務。
通俗講:PKI是集機構、系統(硬件和軟件)、人員、程序、策略和協議為一體,利用公鑰概念和技術來實現和提供安全服務的、普適性的安全基礎設施。
在公鑰密碼中,發送者用公鑰(加密密鑰)加密,接收者用私鑰(解密密鑰)解密。公鑰一般是公開的,不再擔心竊聽,這解決了對稱密碼中的密鑰配送問題。但是接收者依然無法判斷收到的公鑰是否合法(有可能是中間人假冒的)。
事實上,僅靠公鑰密碼本身,無法防御中間人攻擊。于是,需要(認證機構)對公鑰進行簽名,從而確認公鑰沒有被篡改。加了數字簽名的公鑰稱為公鑰證書,一般簡稱證書。
有了證書來認證,可以有效防御中間人攻擊,隨之帶來了一系列非技術性工作。
例如:誰來發證書?如何發證書?不同機構的證書怎么互認?紙質證書作廢容易,數字證書如何作廢?解決這些問題,需要制定統一的規則,即PKI體系。
PKI體系是通過頒發、管理公鑰證書的方式為終端用戶提供服務的系統,最核心的元素是證書。
圍繞證書構成了PKI體系的要素:
- 1)使用PKI的用戶;
- 2)頒發證書的機構(Certificate Authority,CA);
- 3)保存證書的倉庫。
總之:PKI是一個總稱,既包括定義PKI的基礎標準,也包括PKI的應用標準。
3.2 PKI體系現狀
事實上PKI已經有兩代了。
第一代的PKI標準主要是由美國RSA公司的公鑰加密標準PKCS、國際電信聯盟的ITU-T X.509、IETF的X.509、WAP和WPKI等標準組成。但是因為第一代PKI標準是基于抽象語法符號ASN.1進行編碼的,實現起來比較復雜和困難,所以產生了第二代PKI標準。
第二代PKI標準是由微軟、VeriSign和webMethods三家公司在2001年發布的基于XML的密鑰管理規范也叫做XKMS。
事實上現在CA中心使用的最普遍的規范還是X.509系列和PKCS系列。
X.509系列主要由X.209、X.500和X.509組成,其中X.509是由國際電信聯盟(ITU-T)制定的數字證書標準。在X.500基礎上進行了功能增強,X.509是在1988年發布的。
X.509證書由用戶公共密鑰和用戶標識符組成。此外還包括版本號、證書序列號、CA標識符、簽名算法標識、簽發者名稱、證書有效期等信息。
而PKCS是美國RSA公司的公鑰加密標準,包括了證書申請、證書更新、證書作廢表發布、擴展證書內容以及數字簽名、數字信封的格式等方面的一系列相關協議。它定義了一系列從PKCS#1到PKCS#15的標準。
其中最常用的是PKCS#7、PKCS#12和PKCS#10。PKCS#7 是消息請求語法,常用于數字簽名與加密,PKCS#12是個人消息交換與打包語法主要用來生成公鑰和私鑰(題外話:iOS程序員對PKCS#12不陌生,在實現APNs離線消推送時就需要導出.p12證明,正是這個)。PKCS#10是證書請求語法。
4、什么是SSL?
4.1 基本概念
SSL(全稱 Secure Socket Layer)安全套接層是網景公司(Netscape)率先采用的網絡安全協議。它是在傳輸通信協議(TCP/IP)上實現的一種安全協議,采用公開密鑰技術。
通俗地說:SSL被設計成使用TCP來提供一種可靠的端到端的安全服務,它不是單個協議,而是二層協議。低層是SSL記錄層,用于封裝不同的上層協議,另一層是被封裝的協議,即SSL握手協議,它可以讓服務器和客戶機在傳輸應用數據之前,協商加密算法和加密密鑰,客戶機提出自己能夠支持的全部加密算法,服務器選擇最適合它的算法。
SSL特點是:它與應用層協議獨立無關。上層的應用層協議(例如:HTTP、FTP、Telnet等)能透明的建立于SSL協議之上。SSL協議在應用層協議通信之前就已經完成加密算法、通信密鑰的協商以及服務器認證工作。在此之后應用層協議所傳送的數據都會被加密,從而保證通信的私密性。
4.2 與TLS的關系
SSL是網景公司(Netscape)設計,但IETF將SSL作了標準化,即RFC2246,并將其稱為TLS(Transport Layer Security),其最新版本是RFC5246、版本1.2。
實際上:TLS是IETF在SSL3.0基礎上設計的,相當于SSL的后續版本。所以我們通常都是SSL/TLS放一起說。
5、什么是OpenSSL?
5.1 基本概念

OpenSSL是一個開放源代碼的軟件庫,應用程序可以使用這個包來進行安全通信,它包括代碼、腳本、配置和過程的集合。例如:如果您正在編寫一個需要復雜安全加密的軟件,那么只有添加一個安全加密庫才有意義,這樣您就不必自己編寫一大堆復雜的加解密函數(而且密碼學本身很復雜,要寫好它們并不容易)。
其主要庫是以 C 語言所寫成,實現了基本的加密功能,實現了 SSL 與 TLS 協議。
OpenSSL整個軟件包大概可以分成三個主要功能部分:
OpenSSL的目錄結構自然也是圍繞這三個功能部分進行規劃的。
OpenSSL 可以運行在 OpenVMS、 Microsoft Windows 以及絕大多數類 Unix 操作系統上。
5.2 具體來說
密鑰和證書管理是PKI的一個重要組成部分,OpenSSL為之提供了豐富的功能,支持多種標準。
OpenSSL實現了ASN.1的證書和密鑰相關標準,提供了對證書、公鑰、私鑰、證書請求以及CRL等數據對象的DER、PEM和BASE64的編解碼功能。
OpenSSL提供了產生各種公開密鑰對和對稱密鑰的方法、函數和應用程序,同時提供了對公鑰和私鑰的DER編解碼功能。并實現了私鑰的PKCS#12和PKCS#8的編解碼功能。
OpenSSL在標準中提供了對私鑰的加密保護功能,使得密鑰可以安全地進行存儲和分發。
在此基礎上,OpenSSL實現了對證書的X.509標準編解碼、PKCS#12格式的編解碼以及PKCS#7的編解碼功能。并提供了一種文本數據庫,支持證書的管理功能,包括證書密鑰產生、請求產生、證書簽發、吊銷和驗證等功能。
5.3 發展歷程
OpenSSL 計劃在 1998 年開始,其目標是發明一套自由的加密工具,在互聯網上使用。
OpenSSL 以 Eric Young 以及 Tim Hudson 兩人開發的 SSLeay 為基礎,隨著兩人前往 RSA 公司任職,SSLeay 在 1998 年 12 月停止開發。因此在 1998 年 12 月,社群另外分支出 OpenSSL,繼續開發下去。
▲ 上圖為 Tim Hudson
OpenSSL 管理委員會當前由 7 人組成有 13 個開發人員具有提交權限(其中許多人也是 OpenSSL 管理委員會的一部分)。只有兩名全職員工(研究員),其余的是志愿者。
該項目每年的預算不到 100 萬美元,主要依靠捐款。 TLS 1.3 的開發由 Akamai 贊助。
5.4 下載方法
OpenSSL可以從其官網上下載,地址是:https://www.openssl.org/source/,感興趣的讀者可以自行下載安裝研究。
6、各類證書
6.1 證書類型
操作過證書的朋友可能會對各種證書類型眼花繚亂,典型的體現就是各種不同的證書擴展名上,一般來說會有DER、CRT、CER、PEM這幾種證書的擴展名。
以下是最常見的幾種:
- 1)DER文件:表示證書的內容是用二進制進行編碼的;
- 2)PEM文件:是一個文本文件,其內容是以“ - BEGIN -” 開頭的,Base64編碼的字符;
- 3)CRT和CER文件:基本上是等價的,他們都是證書的擴展,也是文本文件,不同的是CRT通常用在liunx和unix系統中,而CER通常用在windows系統中。并且在windows系統中,CER文件會被MS cryptoAPI命令識別,可以直接顯示導入和/或查看證書內容的對話框;
- 4)KEY文件:主要用來保存PKCS#8標準的公鑰和私鑰。
6.2 常用OpenSSL命令
下面的命令可以用來查看文本證書內容:
openssl x509 -incert.pem -text -noout
openssl x509 -incert.cer -text -noout
openssl x509 -incert.crt -text -noout
下面的命令可以用來查看二進制證書內容:
openssl x509 -incert.der -inform der -text -noout
下面是常見的PEM和DER相互轉換。
PEM到DER的轉換:
openssl x509 -incert.crt -outform der-out cert.der
DER到PEM的轉換:
openssl x509 -incert.crt -inform der -outform pem -out cert.pem
補充說明:上述命令中用到的openssl程序,就是本文中提到的OpenSSL開源庫提供的程序。
7、Netty中的聊天加密代碼示例
7.1 關于Netty
Netty是一個Java NIO技術的開源異步事件驅動的網絡編程框架,用于快速開發可維護的高性能協議服務器和客戶端,事實上用Java開發IM系統時,Netty是幾乎是首選。
有關Netty的介紹我就不啰嗦了,如果不了解那就詳讀以下幾篇:
《史上最強Java NIO入門:擔心從入門到放棄的,請讀這篇!》
《Java的BIO和NIO很難懂?用代碼實踐給你看,再不懂我轉行!》
《新手入門:目前為止最透徹的的Netty高性能原理和框架架構解析》
《史上最通俗Netty框架入門長文:基本介紹、環境搭建、動手實戰》
基它有關Netty的重要資料:
Netty-4.1.x 源碼 (在線閱讀版)
Netty-4.1.x API文檔 (在線查閱版)
7.2 啟動SSL Server代碼示例
事實上這個標題是不對的,Netty中啟動的server還是原來那個server,只是對發送的消息進行了加密解密處理。也就是說添加了一個專門進行SSL操作的Handler。
netty中代表ssl處理器的類叫做SslHandler,它是SslContext工程類的一個內部類,所以我們只需要創建好SslContext即可通過調用newHandler方法來返回SslHandler。
讓服務器端支持SSL的代碼:
ChannelPipeline p = channel.pipeline();
SslContext sslCtx = SslContextBuilder.forServer(...).build();
p.addLast("ssl", sslCtx.newHandler(channel.alloc()));
讓客戶端支持SSL的代碼:
ChannelPipeline p = channel.pipeline();
SslContext sslCtx = SslContextBuilder.forClient().build();
p.addLast("ssl", sslCtx.newHandler(channel.alloc(), host, port));
netty中SSL的實現有兩種方式,默認情況下使用的是OpenSSL,如果OpenSSL不可以,那么將會使用JDK的實現。
要創建SslContext,可以調用SslContextBuilder.forServer或者SslContextBuilder.forClient方法。
這里以server為例,看下創建流程。
SslContextBuilder有多種forServer的方法,這里取最簡單的一個進行分析:
publicstaticSslContextBuilder forServer(File keyCertChainFile, File keyFile) {
returnnewSslContextBuilder(true).keyManager(keyCertChainFile, keyFile);
}
該方法接收兩個參數:
- 1)keyCertChainFile是一個PEM格式的X.509證書文件;
- 2)keyFile是一個PKCS#8的私鑰文件。
熟悉OpenSSL的童鞋應該知道使用openssl命令可以生成私鑰文件和對應的自簽名證書文件。
具體openssl的操作可以查看我的其他文章,這里就不詳細講解了。
除了手動創建證書文件和私鑰文件之外,如果是在開發環境中,大家可能希望有一個非常簡單的方法來創建證書和私鑰文件,netty為大家提供了SelfSignedCertificate類。
看這個類的名字就是知道它是一個自簽名的證書類,并且會自動將證書文件和私鑰文件生成在系統的temp文件夾中,所以這個類在生產環境中是不推薦使用的。默認情況下該類會使用OpenJDK's X.509來生成證書的私鑰,如果不可以,則使用 Bouncy Castle作為替代。
7.3 啟動SSL Client代碼示例
同樣的在client中支持SSL也需要創建一個handler。
客戶端的SslContext創建代碼如下:
// 配置 SSL.
finalSslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
上面的代碼我們使用了一個InsecureTrustManagerFactory.INSTANCE作為trustManager。
什么是trustManager呢?
當客戶端和服務器端進行SSL連接的時候,客戶端需要驗證服務器端發過來證書的正確性。
通常情況下,這個驗證是到CA服務器中進行驗證的,不過這樣需要一個真實的CA證書環境,所以在測試中,我們使用InsecureTrustManagerFactory,這個類會默認接受所有的證書,忽略所有的證書異常。
當然:CA服務器也不是必須的,客戶端校驗的目的是查看證書中的公鑰和發送方的公鑰是不是一致的,那么對于不能聯網的環境,或者自簽名的環境中,我們只需要在客戶端校驗證書中的指紋是否一致即可。
netty中提供了一個FingerprintTrustManagerFactory類,可以對證書中的指紋進行校驗。
該類中有個fingerprints數組,用來存儲安全的授權過的指紋信息。通過對比傳入的證書和指紋,如果一致則校驗通過。
使用openssl從證書中提取指紋的步驟如下:
openssl x509 -fingerprint -sha256 -inmy_certificate.crt
8、小結一下
上面我們對Netty聊天用到的加密技術和相關概念進行了梳理,我來簡單這些概念之間的關系。
這些概念之間的關系,簡單來說就是:
- 1)PKI:是一套加密體系和標準的合集,它是理論方案;
- 2)SSL:是利用了PKI理論體系,針對Socket網絡這個場景設計的一套安全通信標準,屬于是PKI的一個具體應用場景;
- 3)OpenSSL:是PKI體系及SSL標準的算法和代碼實現,它包括了具體的開源代碼、工具程序等;
- 4)各種證書:是在SSL或其它基于PKI體系的安全協議標準中需要使用的到一些加密憑證文件等。
而具體到Netty中的聊天加密,那就是應用了上述的PKI體系,基于SSL協議,在OpenSSL等開源庫的幫助下實現的安全程序。
9、參考資料
[1] 公鑰基礎設施(PKI)國際標準進展
[2] 一篇文章讓你徹底弄懂SSL/TLS協議
[3] 什么是OpenSSL?它有什么用途
[4] OpenSSL是什么軟件
[5] netty系列之對聊天進行加密
[6] 跟著源碼學IM
[7] 基于Netty,從零開發IM
[8] TCP/IP詳解(全網唯一在線閱讀版)
[9] 快速理解TCP協議一篇就夠
[10] Netty-4.1.x 源碼(在線閱讀版)
[11] Netty-4.1.x API文檔(在線版)
(本文已同步發布于:http://www.52im.net/thread-4104-1-1.html)