在即時通信系統(IM)中,加密重要的通訊消息,是一個常見的需求。尤爲在一些政府部門的即時通訊軟件中(如稅務系統),對即時聊天消息進行加密是很是重要的一個功能,由於談話中可能會涉及到機密的數據。我在最新的GG 4.5中,增長了對即時聊天消息進行加密的功能,但這一功能並非強制的,能夠經過開關來進行控制。本文就從 爲何要加密消息、不加密有什麼風險開始提及,一直到把GG即時通訊系統中實現加密消息的完整實現介紹清楚。html
想要直接下載體驗的朋友請點擊:下載中心android
咱們知道全部的消息在底層是以bytep[]進行傳輸的,若是文字聊天消息不加密,表示的意思是:直接將string使用utf-8或者unicode編碼成byte[],而後,經過網絡進行傳送。若是在傳送過程當中的某個環節byte[]被惡意截取,則攔截者將byte[]使用utf-8或unicode進行解碼,便可看到原來string的內容。這個過程以下圖所示:算法
對於某些重要的消息而言,這樣明文傳輸的方式實在是太危險了。sql
將聊天消息加密的意思是:將string使用utf-8或unicode編碼成byte[]後,再作一次加密運算,獲得一個新的byte[],而後將這個新的byte[]經過網絡發送給對方;對方接收到byte[]後,先將其作解密運算,而後再用utf-8或unicode轉爲string。這個新過程以下圖所示:數據庫
這樣,即便在網絡傳送過程當中的某個環節byte[]被惡意截取了,攔截者也沒法正確的解析它,如此就規避了原來方案的風險。api
3DES(或稱爲Triple DES)是很是經常使用的對稱加密算法,是對DES算法的加強,它至關因而對每一個數據塊應用三次DES加密算法。服務器
在GG即時通訊系統 4.5的客戶端源碼中,Des3Encryption類是實現3DES算法的類,我是根據3DES的算法原理實現的,可能與某些標準的3DES算法實現細節不同,可是,使用其進行3DES加密、解密是徹底能正常運做的。網絡
能夠將Des3Encryption類做爲一個工具類,從GG即時通訊系統中抽離出來,複用在任何須要的地方。工具
如今咱們正式回到GG即時通訊系統的文字聊天邏輯上面來,看看GG是怎麼實現聊天消息的加密解密的。 post
1.準備工做
GG2014客戶端項目中,增長了Des3Encryption.cs文件,實現了3DES算法。
GlobalResourceManager類增長了加密組件的設置:
private static Des3Encryption des3Encryption = new Des3Encryption("abcd1234"); // null; /// <summary> /// 3DES加密。若是消息不須要加密,則返回null。 /// </summary> public static Des3Encryption Des3Encryption { get { return des3Encryption; } }
這裏有一個開關的功能,便可以開啓或關閉聊天消息加密功能。若是將des3Encryption設置爲null,就表示不啓用聊天消息加密。
2.發送聊天消息
在GG即時通訊系統中,聊天消息有兩類,一類是1對1的聊天,另外一類是羣聊天。若是啓用了加密,兩類聊天消息都須要作相應的處理,它們的流程是同樣的。
在獲得聊天內容後,先進行簡單的序列化,而後對序列化的結果進行3DES加密:(以1對1聊天的ChatForm窗口中的實現爲例,源碼的第866行)
ChatBoxContent content = this.chatBoxSend.GetContent(); byte[] buff = CompactPropertySerializer.Default.Serialize(content); byte[] encrypted = buff; if (GlobalResourceManager.Des3Encryption != null) { encrypted = GlobalResourceManager.Des3Encryption.Encrypt(buff); }
而後,將加密的結果經過IRapidPassiveEngine發送出去。
3.處理接收到的聊天消息
接收到1對1的聊天消息或是羣聊天消息後,首先要作的是解密,而後再反序列化:(以1對1聊天消息的實現爲例,MainFormPartial.cs文件中的源碼的第37行)
byte[] decrypted = info; if (GlobalResourceManager.Des3Encryption != null) { decrypted = GlobalResourceManager.Des3Encryption.Decrypt(info); } ChatBoxContent content = CompactPropertySerializer.Default.Deserialize<ChatBoxContent>(decrypted, 0);
以後,ChatBoxContent對象就能夠在聊天窗中顯示出來了。
4.處理離線消息
離線消息是當接收者再也不時,將該聊天消息暫存在服務器上,等接收者上線時,再發送給他。因此,離線消息的解密處理與普通聊天消息的處理是同樣的。(MainFormPartial.cs文件中的源碼的第86行)
if (informationType == InformationTypes.OfflineMessage) { byte[] bChatBoxContent = null; OfflineMessage msg = CompactPropertySerializer.Default.Deserialize<OfflineMessage>(info, 0); if (msg.InformationType == InformationTypes.Chat) //目前只處理離線的聊天消息 { sourceUserID = msg.SourceUserID; bChatBoxContent = msg.Information; byte[] decrypted = bChatBoxContent; if (GlobalResourceManager.Des3Encryption != null) { decrypted = GlobalResourceManager.Des3Encryption.Decrypt(bChatBoxContent); } ChatBoxContent content = CompactPropertySerializer.Default.Deserialize<ChatBoxContent>(decrypted, 0); } }
根據上面的流程描述,咱們能夠知道,在服務端看到的聊天消息是通過加密的,而GG在服務端有將聊天記錄存儲到數據庫中的功能,所以,數據庫中聊天內容那一列存儲的數據也是加密的。
在GG即時通訊系統中,服務端不須要查看聊天消息的真正內容,因此,服務端不須要使用到Des3Encryption類。
GG在客戶端本地也有存儲聊天記錄(使用Sqlite),與服務器上數據庫中存儲的不同的是,本地存儲的是明文的。因此,在查看聊天記錄時,要根據用戶選擇的是從本地查看仍是從服務器查看來決定是否須要對數據進行解密:(對應ChatRecordForm窗體,源碼177行)
byte[] decrypted = record.Content; if (this.skinRadioButton_Server.Checked) { if (GlobalResourceManager.Des3Encryption != null) { decrypted = GlobalResourceManager.Des3Encryption.Decrypt(decrypted); } } ChatBoxContent content = CompactPropertySerializer.Default.Deserialize<ChatBoxContent>(decrypted, 0);
GGTalk即時通訊系統是可在廣域網部署運行的C#開源即時通訊系統,2013.8.7發佈V1.0版本,至今最新是4.5版本,關於GG更詳細的介紹,能夠查看 可在廣域網部署運行的QQ高仿版 -- GG2014總覽。
源碼下載:GG-V4.5.rar 網盤下載更快
部署下載:GG V4.5 可直接部署版本 網盤下載更快
(壓縮包中有 《部署說明.txt》 和 建立數據庫的腳本 《GG2014.sql》)
自從GG4.4版本開始,GG增長了安卓版本,其運行界面截圖以下所示:
源碼下載:GG-android.rar 網盤下載更快
若要測試,請先部署服務端,而後修改安卓源碼中MainActivity中的服務器的IP和端口(以下圖所示),並從新編譯生成apk。
(若要和PC端聯合測試,請關閉PC端那邊的聊天消息加密功能:將PC客戶端項目的GlobalResourceManager類的 des3Encryption 成員賦值爲 null 便可!)
注:GG安卓版的源碼質量不是很高,屬於安卓初學者水平,不少地方有待改進,目前只是展現與PC打通的功能如何實現。若要將GG安卓版本的源碼用於正式項目中,建議先對其進行重構。