Devlog#07|E2EE 制度的初步建立
虽然我并没有做出什么承诺,但是大家在宣传片底下很想要的两个功能,一个是端到端加密,另一个是联邦化。
其实第一个之前也做过,但是因为各种原因所有丢掉了。在更新迭代之后的技术力积累,为加密功能的复兴打下了技术基础。
加密算法
不知道在多少个版本开始,聊天上方的 AppBar 多出了一把锁头,默认是划掉的状态,意思是消息在未加密的状态下明文发送(还是会经过 HTTPS 加密)和存储。点一下会变成加密状态。
如上图,此时的消息输入框前面也会有一把锁,代表消息在发送的时候经过 RSA 算法非对称加密。在服务器也如此存储。
在低版本还只支持明文消息的时候可以看到加密的消息是以 Base64 编码过后的加密数据发出来的,非常安全。
密钥交换
为了让其他用户看到我们的加密消息,在不知道第几个版本还加入了另一个机制,由 HyperNet.Passport 管理的密钥交换。具体流程如下:
- 客户端发现需要一个密钥来解密但是本地不存在
- 向服务器发送一个
kex.ask的 WebSocket 包要求密钥,内容包含:keypair_id一个唯一 UUID 代表着这个密钥对user_id代表发这条消息的用户,一般情况下谁发这条消息谁会拥有这个密钥
- 服务器在收到
kex.ask包后转发给所有在线的该用户客户端 - 对方客户端在收到
kex.ask的时候会返回一个kex.ack包,内容包含:keypair_id同上user_id同上public_key可以用来解密的密钥(实质上是 RSA 的私钥)private_key可以用来加密信息的密钥,只会在同用户的请求内包含(实质上是 RSA 的公钥)client_id谁请求的密钥,让服务器知道发回给谁(会在转发请求密钥的时候自动添加)
- 服务器收到
kex.ack包后转发回查询密钥的客户端 - 客户端保存密钥,解密信息
- 完成
以上的内容可以看出为什么解密信息需要双方都在线,维持 WS 连接。服务器不存储任何的密钥信息,只是内存中的过客而已,并且很快就会被 GC 回收放别的数据。
不知道你觉得这个过程的安全性是几分呢?虽然密钥还是明文的状态传输的(会经过 WSS 加密),但是应该是没什么问题的,对吧……
密钥存储
不知道在第几个版本之后,本地数据库多出了 sn_keypair 一个表,用来保管所有的密钥对,包括自己的。用来加密和解密。
你可以在帐号 > 密钥对看到你收到的所有密钥,有私钥的密钥可以打勾来使用它签名,没有密钥的新客户端在第一次启动时会自动生成一个。
遗憾和不足
因为密钥交换需要双方在线的缘故,所以加密状态不会保存下来。而且平时也不鼓励用户去使用加密状态, 会给之后查阅聊天记录的人带来困扰。并且加密信息也无法预览和被未来的搜索功能搜索到。
相比于 Matrix 的整个聊天加密,我的实现更像是 TG 的 Secret Chat 还是什么(不常用 TG,有错欢迎指正)只有在线的时候才回交换密钥。所以用户离线了自然就读不到消息了。
不过这样也方便了做群组内的加密,不像 WhatsApp 一样只有私聊可以加密。
那么今天就聊这么多,Thanks for you reading!
是时候回去写写官网了