抓包分析微信的消息,发现发送同样的内容,抓取到的数据包内容都不相同。这到底是怎么回事呢?
显然,微信并不是每次发送消息都跟服务器端约定秘钥(如果那样,性能和流量恐怕大家都不能接受)。那每次加密内容都不一样到底是怎样实现的呢?
基本思路分为两个部分
1、秘钥交换。微信(之后称客户端)每次与微信服务器(之后称服务端)建立TCP长连接后,首先进行握手操作(handshake),这个过程类似https或ssl的秘钥交换过程。
2、秘钥加盐。在每次发送消息是,客户端向秘钥加“盐 ”,再将“盐”随着消息发往服务端。而这个“盐”,往往是消息协议中随每次消息发送变化的合法内容。
貌似这两条有点抽象,后边会有具体步骤说明。在此之前,需要了解一个序列号(seq)的概念。
一般一条消息的数据协议如下图所示。包括header和body两部分。
其中header中有一个seq的字段,表示消息序列号。客户端每向服务端发送一条消息,seq 1。因此seq是一个每次发送消息都会变化的量(当然seq用途远不止用于加密)。
了解了seq的概念,我们来看看加密过程。
1、客户端生成随机数R1
2、客户端使用ECC(或者RSA)非对称加密算法,用公钥将R1加密发给服务端
3、服务端使用ECC(或者RSA) 算法私钥解密数据,得到R1
4、服务端生成随机数R2,采用AES算法加密R2,秘钥为R1。并将加密后的R2发给客户端。
5、客户端采用AES算法,以R1为秘钥,解密得到R2.
至此,客户端和服务端均得到R1,R2
6、客户端发送数据。每条消息会生成一个seq(传输协议中的seq),将R1,R2,seq按照各端约定的方式混合,生成msgKey。 msgKey=mix(R1,R2,seq)
7、使用msgKey为秘钥,用AES算法加密消息,并发送。
8、服务端收到消息,混合R1,R2,seq生成msgKey。完成解密。
这个过程,确保了每条消息加密秘钥都不一致。
此外,所采用的ECC(或RSA)的秘钥,跟客户端版本(clientVersion,参看消息协议图中header部分)关联。不同的客户端版本可以采用不同的秘钥。
最后,微信到底是不是这么做的呢?我不知道,我猜它是这么做的。
相关阅读
《IM系统如何调试TCP协议》
《一个海量在线用户即时通讯系统(IM)的完整设计》