mmtls——微信安全通信协议分析
背景
作为腾讯公司开发的一款即时通信软件,微信支持跨通信运营商、跨操作系统的信息交互。微信原有的安全通信协议基于登陆时派发的 SessionKey,但由于SessionKey 只在应用层保护了请求的数据部分,因此仍然存在通过请求包头部的用户id和请求业务id进行映射关联分析的风险。同时,原有的密码学协议和算法与最新成果有一定差距,安全性存在一定不足。
综上,微信需要一套能够加密保护客户端到服务器之间所有通信数据、对开发人员透明的安全通信协议,由此基于TLS1.3的mmtls
诞生了。
目标
系统的安全性、可用性、性能等指标之间往往存在复杂的相互影响,因此mmtls
协议需要有以下特点:
- 安全性。协议需要保证通信数据不被窃听、篡改、重放和伪造。
- 低延迟。由于该协议为即时通信提供安全服务,因此需要保证数据传输没有明显的延迟。
- 可扩展性。为了提高灵活性和安全性,协议需要及时添加安全强度更高的组件,并禁用被攻破的组件
- 可用性。微信用户多,业务繁重,因此需要考虑极端情况下的可用性,以确保通信
协议设计
微信使用TCP
协议进行报文传输,分为长连接和短连接两种。前者在mmtls
基础上使用私有协议,后者则是HTTP
协议加上mmtls
。mmtls
在微信通信层次中介于应用层和网络连接层,类似于TLS
在HTTP
与TCP
之间的位置,这样不会影响其他网络协议。
同样与TLS
相似的,mmtls
有三个子协议:Record
协议、Handshake
协议和Alert
协议,其中Alert
协议和Handshake
协议是Record
协议的上层协议,因此在Record
协议包中有专门的一个字段,用来确定当前Record
数据包上层协议是Handshake
、Alert
还是应用层协议。
Handshake协议
Handshake
协议的主要任务是完成Client
与Server
的握手协商,让通信双方安全地获得一致的对称密钥,以进行后续的通信数据加密传输。
TLS1.3有两种1-RTT
的密钥协商方式(1-RTT ECDHE
、 1-RTT PSK
)和4种0-RTT
的密钥协商方式(0-RTT PSK
、0-RTT ECDH
、0-RTT PSK-ECDHE
、0-RTT ECDH-ECDHE
),而基于TLS1.3的mmtls
结合了微信网络连接特点,在保证安全性和性能的前提下,只保留了三种密钥协商方式(1-RTT ECDHE
、1-RTT PSK
、0-RTT PSK
)——长连接在建立TCP
连接后会发送一个数据包用于验证长连接的连通性(中间路由可能不支持私有协议),因此使用1-RTT ECDHE
和1-RTT PSK
;短连接则使用0-PSK
,以减少时延,提高用户体验。
1-RTT
ECDHE
ECDH
密钥交换协议包含两个过程——密钥生成与密钥协商。通信双方分别生成一对公私钥,并交换公钥;之后通过收到的公钥和自己的私钥计算出共享密钥,以对明文进行对称加密,实现加密通信。但这个过程存在中间人攻击的风险,即通信双方实际上是与攻击者分别协商出共享密钥,但却以为自己是和通信目标进行了协商。
以上问题的产生是由于通信双方无法确定报文是否来自目的端,因此在密钥协商过程中需要加入身份的认证:基于MAC
的对称认证和基于数字签名的非对称认证。mmtls
协议采用了后一种被称为ECDSA
的算法,通信双方进行密钥协商时,分别签名公钥,收到消息后首先验证签名,签名无误才进行下一步的密钥协商。
具体过程如下:
- 客户端发送
client_pub_key
- 服务器生成
server_pub_key
、server_pri_key
- 服务器用
sign_key
对server_pub_key
签名 - 签名和
server_pub_key
发还客户端 - 客户端用内置的
verify_key
验证验证签名
由于只用一方的签名即可抵抗中间人攻击,因此mmtls
只对Server
进行认证,不考虑Client
的完整性。
PSK
PSK
是ECDH
握手中Server
通过安全信道下发的内容,其中包含对称密钥key
和key
的密文ticket{key}
。
在协商时,Client
将ticket{key}
发给Server
,由于只有Server
能够解密,因此Server
用解密后的key
计算协商数据的MAC
完成认证。以上过程使用的都是对称算法,因此性能要优于ECDH
。
0-RTT PSK
在1-RTT PSK
握手之前,Client
已经得到了一个对称加密密钥,因此可以直接用这个密钥加密数据,和ticket{key}
一起发送给Server
,因此节约了一个轮次。
更多的细节
0-RTT PSK
的前向保密性。由于加密数据的安全性依赖于长期保存的密钥ticket
,因此如果ticket
泄露,基于ticket
保护的所有数据都将失去保密性,因此可以在0-RTT PSK
协商过程中同时完成ECDHE
密钥协商,即0-RTT PSK-ECDHE
。Verify_key
的下发问题。在1-RTT ECDHE
中,Verify_key
用于客户端验证签名,类似与RSA
签名验证中公钥的作用,因此此密钥的下发问题就类似于公钥的派发问题。TLS
采用证书链派发公钥证书,但为了减少额外的时间和带宽等资源消耗,且微信客户端是腾讯发布的程序,因此将Verify_key
内置于客户端。sign_key
泄露问题。如果Server
的sign_key
泄露,则任何攻击者都可伪装为Server
,因此需要及时地更新Verify_key
,也就是及时撤销公钥。TLS
通过CRL
和OCSP
撤销公钥,但均存在延迟问题,微信通信的安全性无法获得保障。同样的,由于Verify_key
内置于客户端,必要时其实可以通过强制升级客户端的方式完成Verify_key
的更新。
Record协议
经过握手,通信双方获得了一致的对称密钥。TLS1.3 只能使用集成加密和消息认证码的算法进行加密。因此基于 TLS1.3 的mmtls
选择AES-GCM
作为认证加密算法——AES
进行加密,采用Counter
模式计算GMAC
进行认证。
密钥扩展
由于 TLS1.3 要求通信双方不能采用完全一致的对称密钥,因此通过握手获得的密钥必须使用密码扩展组件HKDF
进行扩展变换,获得对称加密参数:
Client Write MAC Key
Server Write MAC Key
Client Write Encryption Key
Server Write Encryption Key
Client Write IV
Server Write IV
参数的计算
通过握手获得的密钥,根据握手过程的不同分为两种:Static Secret(SS)
和Ephemeral Secret(ES)
。
1 | //1-RTT ECDHE |
扩展组件HKDF
定义了两个函数——HKDF-Extract(salt, initial-keying-material)
和HKDF-Expand(pseudorandom key, info, out_key_length)
——以保证扩展结果具有伪随机性。前者保证熵足够均匀,足够伪随机,后者实现密钥扩展,其中参数pseudorandom key
一般是HKDF-Extract
的返回值,参数info
包括了握手消息的哈希、表示密钥用途的字符串常量和扩展结果长度。
扩展结果
因为使用AES-GCM
作为认证加密算法,所以经过密钥扩展,mmtls
最终获得四个参数,得到长度为2*encrypt_key_length + 2*iv_length
长度的数据:
1 | client_write_key = key_block[0...encrypt_key_length-1] |
抗重放
由于1-RTT
握手方式的一个数据包不包含具体的数据,因此Client
与Server
共同决定序列号起点,之后对每一个数据包编号后认证加密,便可实现抗重放的功能。
对于0-RTT
握手方式,mmtls
提出基于Client
与Server
的时间序列抵抗重放攻击,即保证超过一段时间的重放包被Server
处理,而该段时间内的重放包在logic
层参与下进行处理。
总结
微信安全通信协议mmtls
在 TLS1.3 的基础上实现,通过ECDH
协商密钥,通过ECDHA
验证签名,实现通信双方握手,之后HKDF
组件进行密钥扩展,获得真正的对称密钥,认证加密算法AES-GCM
对数据包进行处理,保证微信通信的安全、轻量和高性能。
参考资料
TLS协议分析与现代加密通信协议设计
微信交互协议和加密模式研究
微信mmtls协议归纳和演示
基于 TCP 上 mmtls 安全传输实现
AES-GCM