netty系列之:對聊天進行加密

hjavn發表於2021-08-16

工具與資源中心

幫助開發者更加高效的工作,提供圍繞開發者全生命週期的工具與資源
developer.aliyun.com/tool?spm=a1z3...

在之前的文章中,我們講到了怎麼使用netty建立聊天室,但是這樣的簡單的聊天室太容易被竊聽了,如果想要在裡面說點悄悄話是很不安全的,怎麼辦呢?學過密碼學的朋友可能就想到了一個解決辦法,聊天的時候對訊息加密,處理的時候再對訊息解密即可。

當然在netty中上述的工作都不需要我們手動來實現,netty已經提供了支援SSL的channel供我們選擇,一起來看看吧。

在講netty的具體支援之前,我們需要先了解一下公鑰和私鑰的加密標準體系PKI。PKI的全稱是Public Key Infrastructure,也就是公鑰體系。用於規範公鑰私募進行加密解密的規則,從而便於不同系統的對接。

事實上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是個人訊息交換與打包語法主要用來生成公鑰和私鑰。PKCS#10是證照請求語法。

操作過證照的朋友可能會對證照的字尾眼花繚亂,一般來說會有DER、CRT、CER、PEM這幾種證照的字尾。

DER表示證照的內容是用二進位制進行編碼的。

PEM檔案是一個文字檔案,其內容是以“ – BEGIN -” 開頭的,Base64編碼的字元。

CRT和CER基本上是等價的,他們都是證照的擴充套件,也是文字檔案,不同的是CRT通常用在liunx和unix系統中,而CER通常用在windows系統中。並且在windows系統中,CER檔案會被MS cryptoAPI命令識別,可以直接顯示匯入和/或檢視證照內容的對話方塊。

KEY檔案,主要用來儲存PKCS#8標準的公鑰和私鑰。

下面的命令可以用來檢視文字證照內容:

openssl x509 -in cert.pem -text -noout
openssl x509 -in cert.cer -text -noout
openssl x509 -in cert.crt -text -noout

下面的命令可以用來檢視二進位制證照內容:

openssl x509 -in cert.der -inform der -text -noout

下面是常見的PEM和DER相互轉換:

PEM到DER
openssl x509 -in cert.crt -outform der-out cert.der
DER到PEM
openssl x509 -in cert.crt -inform der -outform pem -out cert.pem

事實上這個標題是不對的,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的方法,這裡取最簡單的一個進行分析:

public static SslContextBuilder forServer(File keyCertChainFile, File keyFile) {
return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile);
}

該方法接收兩個引數,keyCertChainFile是一個PEM格式的X.509證照檔案,keyFile是一個PKCS#8的私鑰檔案。

熟悉OpenSSL的童鞋應該知道使用openssl命令可以生成私鑰檔案和對應的自簽名證照檔案。

具體openssl的操作可以檢視我的其他文章,這裡就不詳細講解了。

除了手動建立證照檔案和私鑰檔案之外,如果是在開發環境中,大家可能希望有一個非常簡單的方法來建立證照和私鑰檔案,netty為大家提供了SelfSignedCertificate類。

看這個類的名字就是知道它是一個自簽名的證照類,並且會自動將證照檔案和私鑰檔案生成在系統的temp資料夾中,所以這個類在生產環境中是不推薦使用的。預設情況下該類會使用OpenJDK’s X.509來生成證照的私鑰,如果不可以,則使用 Bouncy Castle作為替代。

同樣的在client中支援SSL也需要建立一個handler。客戶端的SslContext建立程式碼如下:

// 配置 SSL.
final SslContext sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();

上面的程式碼我們使用了一個InsecureTrustManagerFactory.INSTANCE作為trustManager。什麼是trustManager呢?

當客戶端和伺服器端進行SSL連線的時候,客戶端需要驗證伺服器端發過來證照的正確性,通常情況下,這個驗證是到CA伺服器中進行驗證的,不過這樣需要一個真實的CA證照環境,所以在測試中,我們使用InsecureTrustManagerFactory,這個類會預設接受所有的證照,忽略所有的證照異常。

當然,CA伺服器也不是必須的,客戶端校驗的目的是檢視證照中的公鑰和傳送方的公鑰是不是一致的,那麼對於不能聯網的環境,或者自簽名的環境中,我們只需要在客戶端校驗證照中的指紋是否一致即可。

netty中提供了一個FingerprintTrustManagerFactory類,可以對證照中的指紋進行校驗。

該類中有個fingerprints陣列,用來儲存安全的授權過的指紋資訊。通過對比傳入的證照和指紋,如果一致則校驗通過。

使用openssl從證照中提取指紋的步驟如下:

openssl x509 -fingerprint -sha256 -in my_certificate.crt

通過設定client和server端的SSL handler,就可以實現客戶端和伺服器端的加密訊息傳輸。

本文的例子可以參考:learn-netty4

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章