1、引言
本文正好借此機會,以Netty編寫的IM聊天加密為例,為入門者理清什么是PKI體系、什么是SSL、什么是OpenSSL、以及各類證書和它們間的關(guān)系等,并在文末附上簡短的Netty代碼實示例,希望能助你通俗易懂地快速理解這些知識和概念!
補充說明:本文為了讓文章內(nèi)容盡可能言簡意賅、通俗易懂,盡量不深入探討各個技術(shù)知識和概念,感興趣的讀者可以自行查閱相關(guān)資料進一步學習。
2、什么是PKI?
我們需要先了解一下公鑰和私鑰的加密標準體系PKI。
2.1 基本概念

PKI的全稱是Public Key Infrastructure,是指支持公鑰管理體制的基礎(chǔ)設(shè)施,提供鑒別、加密、完整性和不可否認性服務(wù)。
通俗講:PKI是集機構(gòu)、系統(tǒng)(硬件和軟件)、人員、程序、策略和協(xié)議為一體,利用公鑰概念和技術(shù)來實現(xiàn)和提供安全服務(wù)的、普適性的安全基礎(chǔ)設(shè)施。
在公鑰密碼中,發(fā)送者用公鑰(加密密鑰)加密,接收者用私鑰(解密密鑰)解密。公鑰一般是公開的,不再擔心竊聽,這解決了對稱密碼中的密鑰配送問題。但是接收者依然無法判斷收到的公鑰是否合法(有可能是中間人假冒的)。
事實上,僅靠公鑰密碼本身,無法防御中間人攻擊。于是,需要(認證機構(gòu))對公鑰進行簽名,從而確認公鑰沒有被篡改。加了數(shù)字簽名的公鑰稱為公鑰證書,一般簡稱證書。
有了證書來認證,可以有效防御中間人攻擊,隨之帶來了一系列非技術(shù)性工作。
例如:誰來發(fā)證書?如何發(fā)證書?不同機構(gòu)的證書怎么互認?紙質(zhì)證書作廢容易,數(shù)字證書如何作廢?解決這些問題,需要制定統(tǒng)一的規(guī)則,即PKI體系。
PKI體系是通過頒發(fā)、管理公鑰證書的方式為終端用戶提供服務(wù)的系統(tǒng),最核心的元素是證書。
圍繞證書構(gòu)成了PKI體系的要素:
1)使用PKI的用戶;
2)頒發(fā)證書的機構(gòu)(Certificate Authority,CA);
3)保存證書的倉庫。
總之:PKI是一個總稱,既包括定義PKI的基礎(chǔ)標準,也包括PKI的應(yīng)用標準。
2.2 PKI體系現(xiàn)狀
事實上PKI已經(jīng)有兩代了。
第一代的PKI標準主要是由美國RSA公司的公鑰加密標準PKCS、國際電信聯(lián)盟的ITU-T X.509、IETF的X.509、WAP和WPKI等標準組成。但是因為第一代PKI標準是基于抽象語法符號ASN.1進行編碼的,實現(xiàn)起來比較復雜和困難,所以產(chǎn)生了第二代PKI標準。
第二代PKI標準是由微軟、VeriSign和webMethods三家公司在2001年發(fā)布的基于XML的密鑰管理規(guī)范也叫做XKMS。
事實上現(xiàn)在CA中心使用的最普遍的規(guī)范還是X.509系列和PKCS系列。
X.509系列主要由X.209、X.500和X.509組成,其中X.509是由國際電信聯(lián)盟(ITU-T)制定的數(shù)字證書標準。在X.500基礎(chǔ)上進行了功能增強,X.509是在1988年發(fā)布的。
X.509證書由用戶公共密鑰和用戶標識符組成。此外還包括版本號、證書序列號、CA標識符、簽名算法標識、簽發(fā)者名稱、證書有效期等信息。
而PKCS是美國RSA公司的公鑰加密標準,包括了證書申請、證書更新、證書作廢表發(fā)布、擴展證書內(nèi)容以及數(shù)字簽名、數(shù)字信封的格式等方面的一系列相關(guān)協(xié)議。它定義了一系列從PKCS#1到PKCS#15的標準。
其中最常用的是PKCS#7、PKCS#12和PKCS#10。PKCS#7 是消息請求語法,常用于數(shù)字簽名與加密,PKCS#12是個人消息交換與打包語法主要用來生成公鑰和私鑰(題外話:iOS程序員對PKCS#12不陌生,在實現(xiàn)APNs離線消推送時就需要導出.p12證明,正是這個)。PKCS#10是證書請求語法。
3、什么是SSL?
3.1 基本概念

SSL(全稱 Secure Socket Layer)安全套接層是網(wǎng)景公司(Netscape)率先采用的網(wǎng)絡(luò)安全協(xié)議。它是在傳輸通信協(xié)議(TCP/IP)上實現(xiàn)的一種安全協(xié)議,采用公開密鑰技術(shù)。
通俗地說:SSL被設(shè)計成使用TCP來提供一種可靠的端到端的安全服務(wù),它不是單個協(xié)議,而是二層協(xié)議。低層是SSL記錄層,用于封裝不同的上層協(xié)議,另一層是被封裝的協(xié)議,即SSL握手協(xié)議,它可以讓服務(wù)器和客戶機在傳輸應(yīng)用數(shù)據(jù)之前,協(xié)商加密算法和加密密鑰,客戶機提出自己能夠支持的全部加密算法,服務(wù)器選擇最適合它的算法。
SSL特點是:它與應(yīng)用層協(xié)議獨立無關(guān)。上層的應(yīng)用層協(xié)議(例如:HTTP、FTP、Telnet等)能透明的建立于SSL協(xié)議之上。SSL協(xié)議在應(yīng)用層協(xié)議通信之前就已經(jīng)完成加密算法、通信密鑰的協(xié)商以及服務(wù)器認證工作。在此之后應(yīng)用層協(xié)議所傳送的數(shù)據(jù)都會被加密,從而保證通信的私密性。

3.2 與TLS的關(guān)系
SSL是網(wǎng)景公司(Netscape)設(shè)計,但IETF將SSL作了標準化,即RFC2246,并將其稱為TLS(Transport Layer Security),其最新版本是RFC5246、版本1.2。
實際上:TLS是IETF在SSL3.0基礎(chǔ)上設(shè)計的,相當于SSL的后續(xù)版本。所以我們通常都是SSL/TLS放一起說。
4、什么是OpenSSL?
4.1 基本概念

OpenSSL是一個開放源代碼的軟件庫,應(yīng)用程序可以使用這個包來進行安全通信,它包括代碼、腳本、配置和過程的集合。例如:如果您正在編寫一個需要復雜安全加密的軟件,那么只有添加一個安全加密庫才有意義,這樣您就不必自己編寫一大堆復雜的加解密函數(shù)(而且密碼學本身很復雜,要寫好它們并不容易)。
其主要庫是以 C 語言所寫成,實現(xiàn)了基本的加密功能,實現(xiàn)了 SSL 與 TLS 協(xié)議。
OpenSSL整個軟件包大概可以分成三個主要功能部分:
1)SSL協(xié)議庫;
2)應(yīng)用程序;
3)密碼算法庫。
OpenSSL的目錄結(jié)構(gòu)自然也是圍繞這三個功能部分進行規(guī)劃的。
OpenSSL 可以運行在 OpenVMS、 Microsoft Windows 以及絕大多數(shù)類 Unix 操作系統(tǒng)上。
5.2 具體來說
密鑰和證書管理是PKI的一個重要組成部分,OpenSSL為之提供了豐富的功能,支持多種標準。
OpenSSL實現(xiàn)了ASN.1的證書和密鑰相關(guān)標準,提供了對證書、公鑰、私鑰、證書請求以及CRL等數(shù)據(jù)對象的DER、PEM和BASE64的編解碼功能。
OpenSSL提供了產(chǎn)生各種公開密鑰對和對稱密鑰的方法、函數(shù)和應(yīng)用程序,同時提供了對公鑰和私鑰的DER編解碼功能。并實現(xiàn)了私鑰的PKCS#12和PKCS#8的編解碼功能。
OpenSSL在標準中提供了對私鑰的加密保護功能,使得密鑰可以安全地進行存儲和分發(fā)。
在此基礎(chǔ)上,OpenSSL實現(xiàn)了對證書的X.509標準編解碼、PKCS#12格式的編解碼以及PKCS#7的編解碼功能。并提供了一種文本數(shù)據(jù)庫,支持證書的管理功能,包括證書密鑰產(chǎn)生、請求產(chǎn)生、證書簽發(fā)、吊銷和驗證等功能。
5.3發(fā)展歷程
OpenSSL 計劃在 1998 年開始,其目標是發(fā)明一套自由的加密工具,在互聯(lián)網(wǎng)上使用。
OpenSSL 以 Eric Young 以及 Tim Hudson 兩人開發(fā)的 SSLeay 為基礎(chǔ),隨著兩人前往 RSA 公司任職,SSLeay 在 1998 年 12 月停止開發(fā)。因此在 1998 年 12 月,社群另外分支出 OpenSSL,繼續(xù)開發(fā)下去。

▲ 上圖為 Tim Hudson
OpenSSL 管理委員會當前由 7 人組成有 13 個開發(fā)人員具有提交權(quán)限(其中許多人也是 OpenSSL 管理委員會的一部分)。只有兩名全職員工(研究員),其余的是志愿者。
該項目每年的預(yù)算不到 100 萬美元,主要依靠捐款。 TLS 1.3 的開發(fā)由 Akamai 贊助。
5.4 下載方法
OpenSSL可以從其官網(wǎng)上下載,地址是:https://www.openssl.org/source/,感興趣的讀者可以自行下載安裝研究。
6、各類證書
6.1 證書類型
操作過證書的朋友可能會對各種證書類型眼花繚亂,典型的體現(xiàn)就是各種不同的證書擴展名上,一般來說會有DER、CRT、CER、PEM這幾種證書的擴展名。

以下是最常見的幾種:
1)DER文件:表示證書的內(nèi)容是用二進制進行編碼的;
2)PEM文件:是一個文本文件,其內(nèi)容是以“ - BEGIN -” 開頭的,Base64編碼的字符;
3)CRT和CER文件:基本上是等價的,他們都是證書的擴展,也是文本文件,不同的是CRT通常用在liunx和unix系統(tǒng)中,而CER通常用在windows系統(tǒng)中。并且在windows系統(tǒng)中,CER文件會被MS cryptoAPI命令識別,可以直接顯示導入和/或查看證書內(nèi)容的對話框;
4)KEY文件:主要用來保存PKCS#8標準的公鑰和私鑰。
6.2 常用OpenSSL命令
下面的命令可以用來查看文本證書內(nèi)容:
openssl x509 -incert.pem -text -noout
openssl x509 -incert.cer -text -noout
openssl x509 -incert.crt -text -noout
下面的命令可以用來查看二進制證書內(nèi)容:
openssl x509 -incert.der -inform der -text -noout
下面是常見的PEM和DER相互轉(zhuǎn)換。
PEM到DER的轉(zhuǎn)換:
openssl x509 -incert.crt -outform der-out cert.der
DER到PEM的轉(zhuǎn)換:
openssl x509 -incert.crt -inform der -outform pem -out cert.pem
補充說明:上述命令中用到的openssl程序,就是本文中提到的OpenSSL開源庫提供的程序。
7、Netty中的聊天加密代碼示例
7.1 關(guān)于Netty

Netty是一個Java NIO技術(shù)的開源異步事件驅(qū)動的網(wǎng)絡(luò)編程框架,用于快速開發(fā)可維護的高性能協(xié)議服務(wù)器和客戶端,事實上用Java開發(fā)IM系統(tǒng)時,Netty是幾乎是首選。
有關(guān)Netty的介紹我就不啰嗦了,如果不了解那就詳讀以下幾篇:
《史上最強Java NIO入門:擔心從入門到放棄的,請讀這篇!》
《Java的BIO和NIO很難懂?用代碼實踐給你看,再不懂我轉(zhuǎn)行!》
《新手入門:目前為止最透徹的的Netty高性能原理和框架架構(gòu)解析》
《史上最通俗Netty框架入門長文:基本介紹、環(huán)境搭建、動手實戰(zhàn)》
基它有關(guān)Netty的重要資料:
7.2 啟動SSL Server代碼示例
事實上這個標題是不對的,Netty中啟動的server還是原來那個server,只是對發(fā)送的消息進行了加密解密處理。也就是說添加了一個專門進行SSL操作的Handler。
netty中代表ssl處理器的類叫做SslHandler,它是SslContext工程類的一個內(nèi)部類,所以我們只需要創(chuàng)建好SslContext即可通過調(diào)用newHandler方法來返回SslHandler。
讓服務(wù)器端支持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的實現(xiàn)有兩種方式,默認情況下使用的是OpenSSL,如果OpenSSL不可以,那么將會使用JDK的實現(xiàn)。
要創(chuàng)建SslContext,可以調(diào)用SslContextBuilder.forServer或者SslContextBuilder.forClient方法。
這里以server為例,看下創(chuàng)建流程。
SslContextBuilder有多種forServer的方法,這里取最簡單的一個進行分析:
publicstaticSslContextBuilder forServer(File keyCertChainFile, File keyFile) {
????returnnewSslContextBuilder(true).keyManager(keyCertChainFile, keyFile);
}
該方法接收兩個參數(shù):
1)keyCertChainFile是一個PEM格式的X.509證書文件;
2)keyFile是一個PKCS#8的私鑰文件。
熟悉OpenSSL的童鞋應(yīng)該知道使用openssl命令可以生成私鑰文件和對應(yīng)的自簽名證書文件。
具體openssl的操作可以查看我的其他文章,這里就不詳細講解了。
除了手動創(chuàng)建證書文件和私鑰文件之外,如果是在開發(fā)環(huán)境中,大家可能希望有一個非常簡單的方法來創(chuàng)建證書和私鑰文件,netty為大家提供了SelfSignedCertificate類。
看這個類的名字就是知道它是一個自簽名的證書類,并且會自動將證書文件和私鑰文件生成在系統(tǒng)的temp文件夾中,所以這個類在生產(chǎn)環(huán)境中是不推薦使用的。默認情況下該類會使用OpenJDK's X.509來生成證書的私鑰,如果不可以,則使用 Bouncy Castle作為替代。
7.3 啟動SSL Client代碼示例
同樣的在client中支持SSL也需要創(chuàng)建一個handler。
客戶端的SslContext創(chuàng)建代碼如下:
// 配置 SSL.
finalSslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
上面的代碼我們使用了一個InsecureTrustManagerFactory.INSTANCE作為trustManager。
什么是trustManager呢?
當客戶端和服務(wù)器端進行SSL連接的時候,客戶端需要驗證服務(wù)器端發(fā)過來證書的正確性。
通常情況下,這個驗證是到CA服務(wù)器中進行驗證的,不過這樣需要一個真實的CA證書環(huán)境,所以在測試中,我們使用InsecureTrustManagerFactory,這個類會默認接受所有的證書,忽略所有的證書異常。
當然:CA服務(wù)器也不是必須的,客戶端校驗的目的是查看證書中的公鑰和發(fā)送方的公鑰是不是一致的,那么對于不能聯(lián)網(wǎng)的環(huán)境,或者自簽名的環(huán)境中,我們只需要在客戶端校驗證書中的指紋是否一致即可。
netty中提供了一個FingerprintTrustManagerFactory類,可以對證書中的指紋進行校驗。
該類中有個fingerprints數(shù)組,用來存儲安全的授權(quán)過的指紋信息。通過對比傳入的證書和指紋,如果一致則校驗通過。
使用openssl從證書中提取指紋的步驟如下:
openssl x509 -fingerprint -sha256 -inmy_certificate.crt
8、小結(jié)一下
上面我們對Netty聊天用到的加密技術(shù)和相關(guān)概念進行了梳理,我來簡單這些概念之間的關(guān)系。
這些概念之間的關(guān)系,簡單來說就是:
1)PKI:是一套加密體系和標準的合集,它是理論方案;
2)SSL:是利用了PKI理論體系,針對Socket網(wǎng)絡(luò)這個場景設(shè)計的一套安全通信標準,屬于是PKI的一個具體應(yīng)用場景;
3)OpenSSL:是PKI體系及SSL標準的算法和代碼實現(xiàn),它包括了具體的開源代碼、工具程序等;
4)各種證書:是在SSL或其它基于PKI體系的安全協(xié)議標準中需要使用的到一些加密憑證文件等。
而具體到Netty中的聊天加密,那就是應(yīng)用了上述的PKI體系,基于SSL協(xié)議,在OpenSSL等開源庫的幫助下實現(xiàn)的安全程序。
9、參考資料
[1]?公鑰基礎(chǔ)設(shè)施(PKI)國際標準進展
[4]?OpenSSL是什么軟件
[5]?netty系列之對聊天進行加密
[6]?跟著源碼學IM