mmtls
架构
网络层次架构
mmtls协议内部架构
Handshake协议 — 安全地协商出对称加密密钥
对称密码算法相较于非对称密码算法,运算速度快,更适合于长段的通信加密。
但对称密码算法的一大问题在于:如何通过不安全信道,使得通信双方安全地获得一致的对称密钥,以进行加密数据传输?
可以借助非对称密码算法来解决这个问题,例如RSA Key Exchange,Diffie-Hellman Key Exchange。
RSA密钥协商无法保证PFS(perfect forward secrecy) ,因此TLS1.3弃用了RSA,只使用DHKE。
“认证密钥协商+对称加密传输”这种混合加密结构,是绝大多数加密通信协议的通用结构。
Rount-trip time (RTT): 网络请求从起点到目的地然后再回到起点所花费的时长
-
1-RTT ECDHE
Diffie-Hellman密钥协商容易遭受到中间人攻击,因此需要加入身份认证,可以通过数字签名算法来实现。
在微信的应用场景中,只需要保证一端的认证,对server端认证即可。又由于server端固定(微信服务器),微信的签名公钥都是同一个,直接将签名公钥硬编码写进客户端就行,而无需证书链方式来验证签名公钥的可信性,这样能省去一大笔麻烦。
-
1-RTT PSK
通信双方之前已经1-RTT ECDHE过,且在加密通信(中间人无法看到明文)的过程中,服务端下发了一个PSK结构:PSK{key, ticker{key}}。key是可以用来做下一次会话的对称加密密钥,ticket_key{key}是用服务端保存的ticket_key对key进行加密后的密文。
server端回一个MAC,可以保证服务端的真实性:
- 一方面,只有服务端有ticket_key,能对ticket_key{key}正确解密拿到key
- 另一方面,只有拿到key才能生成正确的MAC
前面两种协商的一次RTT都是不带业务数据的,全是协商数据,是否有协商方案能在协商的过程中顺便安全地将业务数据发送给对端?
-
0-RTT ECDH-ECDHE
要求客户端提前知道服务端的static_svr_pub_key
-
0-RTT PSK
在1-RTT PSK握手之前,客户端已经有了对称加密密钥key,直接拿着这个加密业务数据,并将其和握手协商数据一起发送给服务端即可。
-
0-RTT PSK-ECDHE
几个小问题:
-
用于数字签名的公钥verify_key如何下发?
公钥分发问题,在微信的应用场景中,可以通过将verify_key内置在客户端中/
-
如果用于数字签名的私钥sign_key泄漏?
若是证书,那么就是公钥撤销问题。但微信中没用到证书,可以强制升级客户端。
-
签名的内容?
sign(Client_Random + svr_pub_key + Server_Random),可以保证这个签名值和一次握手一一对应,防止该签名值用于另外一次握手。
结合微信自身的业务特点,最终选择了1-RTT ECDHE、1-RTT PSK、0-RTT PSK
微信目前有两个数据传输通道:
- 基于HTTP协议的短连接
- 基于私有协议的长连接
Record协议 — 使用对称加密密钥进行安全的通信
-
认证加密
加密算法只能提供机密性,还需要保证传输过程中的完整性,所以需要MAC来做认证。
Encrypt-then-MAC是学术界目前公认最安全的。
mmtls使用AES-GCM。
-
密钥扩展
协商出来的pre_master_key,经过HKDF变换,得到4个key:
- client_write_key
- server_write_key
- client_write_IV
- server_write_IV
疑惑:
TLS1.3明确要求通信双方使用的对称加密Key不能完全一样,否则在一些对称加密算法下会被完全攻破。找找看特例情况?
设计文档中说:对于AES-GCM,MAC Key和Encryption Key可以统一用一个?? 2020.06.08更新:似乎这么操作挺常见的
-
防重放
实际上,mmtls的做法是将sequence number作为构造AES-GCM算法参数nonce的一部分,利用AES-GCM的算法特性,只要AES-GCM认证解密成功就可以确保sequence number符合预期。
这个sequence number有点迷惑
微信没有在mmtls层实现0-RTT的防重放。