在即时通信系统(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安卓版本的源码用于正式项目中,建议先对其进行重构。