1、引言
對于IM聊天應用來說,為了提升安全性,對聊天消息加密是常規(guī)操作。
眾所周之,Netty是高性能的Java NIO網(wǎng)絡通信框架,因而用Netty來寫IM是再正常不過了。網(wǎng)上關于為Netty生成、以及使用SSL/TLS證書的文章有很多,但由于各種原因,生成的證書要么是Netty中無法讀取和使用,要么是代碼不全或不具體導致根本配不通SSL/TLS加密。
正好這段時間專門為 MobileIMSDK 生成了一套測試證書,順手把這個過程記錄了下來,分享給大家。
本文要分享的是如何使用OpenSSL生成在基于Netty的IM中真正可用的SSL/TLS證書,內(nèi)容包括:證書的創(chuàng)建、創(chuàng)建過程中的注意點,以及在Server端、Android端、iOS端、Java桌面端、H5端使用證書的代碼范例。
注:對于那些付費購買了第3方權威CA機構簽發(fā)的證書,他們都有相應的使用文檔,這就沒什么好說的。本文里的證書指的是不需要花錢的自簽名證書。
學習交流:
(本文已同步發(fā)布于:http://www.52im.net/thread-4142-1-1.html)
2、知識準備
► 如果你對IM系統(tǒng)毫無概念,建議先閱讀《零基礎IM開發(fā)入門(一):什么是IM系統(tǒng)?》系列文章,通俗易懂,適合小白。
► 如果你想系統(tǒng)學習IM開發(fā)相關的理論知識,比如網(wǎng)格編程、IM架構設計等,建議先閱讀《新手入門一篇就夠:從零開發(fā)移動端IM》。
► 如果你不了解Netty是什么,建議閱讀以下幾篇Netty的基礎入門好文章:
► 如果你已掌握IM理論知識,同時也對Netty基本掌握,正準備動手實戰(zhàn),則可以閱讀《基于Netty,從零開發(fā)IM》和《跟著源碼學IM》這個系列文章,有各種入門級實戰(zhàn)代碼,圖文并茂,適合學習。
► 如果你對IM、Netty已基本上手,但對IM安全方面的技術概念有點理不清,建議必讀《基于Netty的IM聊天加密技術學習:一文理清常見的加密概念、術語等》。
3、什么是Netty
Netty是一個Java NIO技術的開源異步事件驅(qū)動的網(wǎng)絡編程框架,用于快速開發(fā)可維護的高性能協(xié)議服務器和客戶端。往通俗了講,可以將Netty理解為:一個將Java NIO進行了大量封裝,并大大降低Java NIO使用難度和上手門檻的超牛逼框架。(引用自《史上最通俗Netty框架入門長文:基本介紹、環(huán)境搭建、動手實戰(zhàn)》)
PS:限于篇幅,對于Netty方面的入門知識就不再贅述,如有必要,請仔細跟著本文第二節(jié)“2、知識準備”里有關Netty的文章進行閱讀。
4、什么是OpenSSL
OpenSSL是一個開放源代碼的軟件庫,應用程序可以使用這個包來進行安全通信,它包括代碼、腳本、配置和過程的集合。其主要庫是以 C 語言所寫成,實現(xiàn)了基本的加密功能,實現(xiàn)了 SSL 與 TLS 協(xié)議。OpenSSL整個軟件包大概可以分成三個主要功能部分:SSL協(xié)議庫、應用程序、密碼算法庫。
PS:OpenSSL的介紹就點到為止,如有興趣,可仔細閱讀《基于Netty的IM聊天加密技術學習:一文理清常見的加密概念、術語等》。
5、下載和安裝OpenSSL
1)方法一:可以從OpenSSL的Github倉庫下載源碼自行編譯(源碼下載地址),對于一般使用者來說,自已編譯著實有點麻煩,不推薦這么玩。
2)方法一:也可以從這個網(wǎng)站下載第3方編譯好的OpenSSL安裝程序(安裝程序下載地址),這樣上手簡單快捷。具體可以參考《openssl安裝教程(windows7系統(tǒng),超詳細)》這篇文章。
3)方法一:也可以直接用下面附件里的安裝程序(這是我一直用的版本,版本較老,有興趣可直接下載使用):
Openssl-windows-0.9.8k(52im.net).rar (874.97 KB , 下載次數(shù): 1 , 售價: 1 金幣)
4)解決 “openssl.cnf找不到” 的問題:如果你安裝好OpenSSL后,使用時報“openssl.cnf找不到”或“計算機缺少openssl.cnf”等之類錯誤提示,可以下載下面這個 openssl.cnf文件。
openssl.cnf 文件附件下載:
openssl_conf(52im.net).rar (4.63 KB , 下載次數(shù): 1 , 售價: 1 金幣)
openssl.cnf 文件解壓縮后:
openssl.cnf文件配置使用:
以下是 openssl.cnf 文件的配置使用命令:(以我的安裝目錄為例)
C:\Openssl-windows-0.9.8k-out32dll>set OPENSSL_CONF=c:/WINDOWS/system32/openssl.cnf
準備就緒,接下來我們就可以開始生成SSL/TLS證書了!
6、生成Netty可用的SSL/TLS證書
6.1概述
經(jīng)過實踐,生成Netty可用的SSL/TLS證書需要4步:
- 1)創(chuàng)建私鑰證書;
- 2)將私鑰格式轉(zhuǎn)成pk8;
- 3)創(chuàng)建證書請求;
- 4)生成公鑰證書。
接下來,跟著本節(jié)內(nèi)容,一步步使用OpenSSL生成一個真正能在Netty中能使用的自簽名證書。
6.2第一步:創(chuàng)建私鑰證書
在CMD控制臺下執(zhí)行如下指令:(記得手動創(chuàng)建 netty 目錄)
openssl genrsa -des3 -out netty/netty-key.pem 1024
提示:以上指令中,如無“-des3”參數(shù),則Netty的代碼中使用時將報“File does not contain valid private key”等錯誤(如下圖所示)。
6.3第二步:將私鑰格式轉(zhuǎn)成pk8
在CMD控制臺下執(zhí)行如下指令:
openssl pkcs8 -innetty/netty-key2.pem -topk8 -out netty/netty-key2.pk8
提示1:如不轉(zhuǎn)pk8格式,則Netty的代碼中使用時會報以下錯誤:
提示2:如代碼中不為key加入密碼,則Netty的代碼中使用時會報以下錯誤:
提示3:Netty的代碼中使用時要加入上方生成Key證書時的密碼即可:
6.4第三步:創(chuàng)建證書請求
在CMD控制臺下執(zhí)行如下指令:
openssl req -new -out netty/netty-req2.csr -key netty/netty-key2.pem
提示:經(jīng)上指令中,Common Name指明的是證書綁定的域名,你可以用域名或ip,本次生成用了子域名。
6.5第四步:生成公鑰證書
在CMD控制臺下執(zhí)行如下指令:
openssl x509 -req -inca/ca-req2.csr -out netty/netty-cert2.crt -signkey netty/netty-key2.pem -days 3650
提示:out 參數(shù)生成的是.crt,而在前面的是.pem,這只是擴展名區(qū)別,內(nèi)容都一樣。
6.6最終成果
至此,我們已經(jīng)為Netty創(chuàng)建好了證書,接下來的章節(jié),就是分享如何讀取和使用這些證書的。
7、實戰(zhàn)代碼
7.1概述
本節(jié)將為你演示如何在基于Netty的IM中使用上節(jié)中生成的證書。
為了讓示例代碼更具實戰(zhàn)意義,本節(jié)的示例代碼將引用的是開源IM框架MobileIMSDK 的源碼,如果有興趣深入學習,可以從下面的開源倉庫中下載到MobileIMSDK的完整源碼。
7.2基于Netty的IM服務端如何開啟SSL/TLS
首先將上節(jié)中生成的證書,放置到你的IM服務端磁盤目錄下。以下截圖和示例代碼以MobileIMSDK的開源代碼為例。
我們可以將證書放到這個位置:
使用證書的示例代碼片段:(完整代碼詳見 ServerLauncherImpl.java)
/**
* 創(chuàng)建SslContext對象,用于開啟SSL/TLS加密傳輸。
*
* @return 如果成功創(chuàng)建則返回SslContext對象,否則返回null
*/
privatestaticSslContext createSslContext() {
try{
// 證書文件
InputStream certChainFile = ServerLauncherImpl.class.getResourceAsStream("certs/netty-cert2.crt");
// 私鑰文件(注意:Netty只支持.pk8格式)
InputStream keyFile = ServerLauncherImpl.class.getResourceAsStream("certs/netty-key2.pk8");
// 私鑰密碼
String keyPassword = "123456";
// 生成SslContext對象(為了方便理解,此處使用的是單向認證)
SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();
returnsslCtx;
} catch(Exception e) {
logger.warn("createSslContext()時出錯了,原因:"+e.getMessage(), e);
}
returnnull;
}
PS:如果你想自已動手完整運行一下,可以閱讀《MobileIMSDK的Demo使用幫助:Server端》。
接下來的內(nèi)容,我們將實現(xiàn)客戶端連接到使用SSL/TLS證書的Netty IM服務端。
7.3Android端如何開啟SSL/TLS
因為服務端已經(jīng)開啟了SSL/TLS加密,我們在開發(fā)IM的客戶端時,該如何啟用SSL/TLS呢(否則你未開啟SSL/TLS的客戶端肯定是連不上你的服務端的)?
這里為了方便示例,我們同樣以 MobileIMSDK的Android端開源代碼為例。
Android端開啟SSL/TLS加密的示例代碼片段:(完整代碼詳見 IMClientManager.java)
/**
* 創(chuàng)建SslContext對象,用于開啟SSL/TLS加密傳輸。
*
* @return 如果成功創(chuàng)建則返回SslContext對象,否則返回null
*/
publicSslContext createSslContext() {
SslContext sslContext = null;
try{
sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
Log.d(TAG, "【IMCORE-TCP】已開啟SSL/TLS加密(單向認證),且sslContext創(chuàng)建成功。");
} catch(Exception e) {
Log.w(TAG, "【IMCORE-TCP】創(chuàng)建sslContext時出錯,原因是:"+ e.getMessage(), e);
}
returnsslContext;
}
PS:如果你想自已動手完整運行一下,可以閱讀《MobileIMSDK的Demo使用幫助:Android版》。
7.4iOS端如何開啟SSL/TLS
同樣的,iOS端該如何開啟SSL/TLS呢?
這里我們依然以 MobileIMSDK的iOS端開源代碼為例(MobileIMSDK的iOS使用的是 CocoaAsyncSocket 網(wǎng)絡庫,如果你也是用的它,就可以直接參考了,因為開啟了SSL/TLS的CocoaAsyncSocket代碼跟未開啟加密的代碼用法差異較多,且這方面可以參考的資料較少)。
iOS端開啟SSL/TLS加密的示例代碼片段:(完整代碼詳見 LocalSocketProvider.m)
/**
* 當socket已經(jīng)完整連接并準備好讀和寫數(shù)據(jù)時,將調(diào)用此方法。
*/
- (void)socket:(MBGCDAsyncSocket *)socket didConnectToHost:(NSString*)host port:(uint16_t)port
{
if([ClientCoreSDK isENABLED_DEBUG])
NSLog(@"【IMCORE-TCP-SOCKET】成收到的了TCP的connect反饋, isConnected? %d、已開啟ssl加密? %d", [socket isConnected], [ClientCoreSDK isSSL]);
// 如果未開啟SSL加密傳輸,則正常進入連接完成后的代碼邏輯
if(![ClientCoreSDK isSSL]) {
[selfwhenDidConnect:socket];
}
// 如果已開啟SSL加密傳輸,則需要在回調(diào)中調(diào)用startTLS方法,以便實現(xiàn)跟服務端的SSL握手過程,
// 如果ssl握手成功,則會通過 socketDidSecure: 回調(diào)通知開發(fā)者
else{
// 配置 SSL/TLS 設置信息
NSMutableDictionary*settings = [NSMutableDictionarydictionaryWithCapacity:3];
// 允許自簽名證書手動驗證
[settings setObject:@YESforKey:GCDAsyncSocketManuallyEvaluateTrust];
// 經(jīng)測試,本項不設置并不影響SSL的啟用
// [settings setObject:@"此處填服務器IP地址" forKey:GCDAsyncSocketSSLPeerName];
// 如果不是自簽名證書,而是權威證書頒發(fā)機構注冊申請的證書,這個settings字典可不傳(將使用GCDAsyncSocket的默認配置)
[socket startTLS:settings];
}
}
/**
* 當SSL握手成功后(也就是上方調(diào)用startSSL:方法后),將調(diào)用此方法。
*/
- (void)socketDidSecure:(MBGCDAsyncSocket *)socket
{
[selfwhenDidConnect:socket];
}
/**
* Allows a socket delegate to hook into the TLS handshake and manually validate the peer it's connecting to.
*/
- (void)socket:(MBGCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void(^)(BOOLshouldTrustPeer))completionHandler
{
NSLog(@"【IMCORE-TCP-SOCKET】didReceiveTrust...");
// 以下沒有做更復雜的ssl證書驗證邏輯,如您需要實現(xiàn)更強大的雙向認證等邏輯,可以參考這里:
// [url=https://github.com/FuangCao/cavan/blob/338ca8c09d6c78c5b38b95c6ffe994241afcc96e/xcode/TestSSL/TestSSL/ViewController.m]https://github.com/FuangCao/cava ... SL/ViewController.m[/url]
if(completionHandler) {
completionHandler(YES);
}
}
說明:CocoaAsyncSocket中開啟SSL/TLS并不像Android和Java中那么簡單,它不只是幾行代碼的事,而是整個數(shù)據(jù)讀取邏輯的變化。
PS:如果你想自已動手完整運行一下,可以閱讀《MobileIMSDK的Demo使用幫助:iOS版》。
7.5Java桌面端如何開啟SSL/TLS
Java桌面端開啟SSL/TLS的代碼跟Android端是一樣。我們同樣以 MobileIMSDK的Java端開源代碼為例。
Java桌面端開啟SSL/TLS加密的示例代碼片段:(完整代碼詳見 IMClientManager.java)
/**
* 創(chuàng)建SslContext對象,用于開啟SSL/TLS加密傳輸。
*
* @return 如果成功創(chuàng)建則返回SslContext對象,否則返回null
*/
publicSslContext createSslContext() {
SslContext sslContext = null;
try{
sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
Log.d(TAG, "【IMCORE-TCP】已開啟SSL/TLS加密(單向認證),且sslContext創(chuàng)建成功。");
} catch(Exception e) {
Log.w(TAG, "【IMCORE-TCP】創(chuàng)建sslContext時出錯,原因是:"+ e.getMessage(), e);
}
returnsslContext;
}
PS:如果你想自已動手完整運行一下,可以閱讀《MobileIMSDK的Demo使用幫助:Java版》。
7.6H5端如何開啟SSL/TLS
我這里說的H5端,指的是能支持標準HTML5端WebSocket協(xié)議的PC瀏覽器端、手機移動端內(nèi)嵌的Web引擎等場景。
H5端能開啟SSL/TLS有兩個前提:
滿足以上兩點后,H5端什么代碼都不需改動,只需將請求url由“ws”改成“wss”:
8、參考資料
[1] MobileIMSDK開源工程源碼
[2] 史上最通俗Netty框架入門長文:基本介紹、環(huán)境搭建、動手實戰(zhàn)
[3] 基于Netty,從零開發(fā)IM
[4] 基于Netty的IM聊天加密技術學習:一文理清常見的加密概念、術語等
[5] IM聊天系統(tǒng)安全手段之通信連接層加密技術
[6] 通俗易懂:一篇掌握即時通訊的消息傳輸安全原理
[7] 探討組合加密算法在IM中的應用
[8] openssl安裝教程(windows7系統(tǒng),超詳細)
[9] WebSocket從入門到精通,半小時就夠!
(本文已同步發(fā)布于:http://www.52im.net/thread-4142-1-1.html)