IM电竞IM聊天系统安全手段之传输内容端到端加密技术
发布时间:2022-09-11 16:00:07

  本文由融云技术团队分享,原题“互联网通信安全之端到端加密技术”,内容有较多修订和改动。

  在上篇《IM聊天系统安全手段之通信连接层加密技术》中,分享了关于通信连接层加密的相关技术和实践,包括在传输即时通信消息时启用 TLS 链路加密(保证消息在到达服务器前无法被窃听和篡改)、使用 CA 认证机制(杜绝中间人攻击)等。

  本篇将围绕IM传输内容的安全问题,以实践为基础,为你分享即时通讯应用中的“端到端”加密技术。

  - 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》- 开源IM框架源码:(备用地址点此)

  《即时通讯安全篇(五):对称加密技术在Android平台上的应用实践》

  《即时通讯安全篇(八):你知道,HTTPS用的是对称加密还是非对称加密?》

  《即时通讯安全篇(九):为什么要用HTTPS?深入浅出,探密短连接的安全性》

  《即时通讯安全篇(十一):IM聊天系统安全手段之传输内容端到端加密技术》(

  上篇中提到的连接层加密技术,这是提升IM客户端到服务器之间数据传输的安全性手段,但是这并不能解决用户间的通信隐私性以及安全性风险。

  因为在将数据传输到服务器之后,所有有权访问此服务器的人,包括员工、供应商及其他有关人员(甚至黑客),都有可能读取到用户的数据。

  有鉴于此,端到端加密技术在即时通讯IM领域被广泛应用,包括WhatsApp、Signal、Telegram 等国外即时通信软件中都有使用。

  说到端到端加密,我们首先想到的解决方案是:在发送端发送消息前对整个消息进行加密,接收端接收到消息后进行解密。

  事实上:这确实是端到端加密中消息收发的简化版解决方案,只是我们在实际应用中要更加复杂,效果也更加安全。

  对于端到端加密,我们需要先解决的前置安全问题是:如何安全地传递用于消息加解密的密钥。

  答案是:用非对称加密的方式传输密钥(与 SSL / TLS 中安全交换密钥的方式类似)。

  实际上:大部分即时通信软件中的端到端加密都采用生成共享密钥的方式来传输会话密钥。这是为什么呢?

  这就涉及到 DH 算法(即 Diffie-Hellman 密钥交换算法),关于DH算法的资料,有兴趣可以详读《Diffie-Hellman密钥协商算法》,限于篇幅,这里不专门讨论。

  Diffie-Hellman 密钥交换算法的安全性依赖于这样一个事实:虽然计算以一个素数为模的指数相对容易,但计算离散对数却很困难。对于大的素数,计算出离散对数几乎是不可能的。

  1)如果采用 RSA、ECC 等公钥加密私钥解密的方式传输密钥,需要在创建会话时生成临时密钥,并通过对方公钥加密后传输到接收端。

  这就需要完全保证消息的可靠性,如果该消息在任何一个环节中丢失或损坏,则后续通信都无法进行。或者,需要采用更为可靠的传输方案,通常做法为需要接收端在线,通过各种确认来保证这个可靠性。

  而采用共享密钥的方式则只需要知道对方的公钥IM电竞,IM电竞就可以完成生成共享密钥,并不一定需要对方在线)

  我们结合对于 DH 算法(即 Diffie-Hellman 密钥交换算法)这种共享密钥方式的认知(即公钥可随意公开),先设计一个简单的端到端消息加密的过程。

  接下来,我们针对这个简单方案存在的各种安全隐患问题,进行逐步分析和优化。

  全称Message Authentication Code,即消息认证码(带密钥的Hash函数)。在密码学中,MAC是通信实体双方使用的一种验证机制,是保证消息数据完整性的一种工具。

  从感观上,这就像一个棘轮,棘轮就是一种特殊的齿轮,他只能往一个方向转下去,而不能往回转。

  在技术上,做到只能往一个方向转下去,而不能往回转,是达到前向安全的关键。这就保证了,如果某一轮的密钥被破解出来,但前面的密钥是无法计算出来的,也就是前面的消息无法被解密。

  KDF 全称(Key derivation function) 密钥导出函数,用于从一个原始的密钥导出一个或多个密钥。本质上就是 Hash 函数,通常用来将短密码变成长密码。另外 KDF 需要加“盐”(salt),用于防彩虹表IM电竞,出于 Hash 的特性im新闻,这个“盐”的长度至少要大于 Hash 结果长度。

  KDF 棘轮就是运用 KDF 算法,设计出一种密钥不断变化的效果,流程如下:

  我们可以设计出一个安全更新盐的方法。我们在证书服务器增加一个临时公钥证书,这个临时证书是按照接收双方标识构建的临时公钥对,即每个人的每个单人会话都具备一个临时公钥。每进行一个消息轮回,就更新一次己方的临时公钥,同时根据另外一方的临时公钥和己方的私钥进行协商,并将协商出的密钥作为盐,使得 KDF 棘轮算法生成的消息密钥具有后向安全性。

  那么我们就可以规定创建新的二人会话时,发起方首先生成一个新的临时 DH 公私钥对,并向服务器上传自己的临时 DH 公钥;其次发送方用接收方公布的长期公钥与自己的临时私钥协商出密钥作为消息加密的密钥,对消息进行加密;最后接收方首次接收到消息后用自己的长期公钥和发送方的临时私钥计算得出消息密钥,并在首次回复消息时生成临时公私钥,同时上传临时公钥。

  2)已签名的预共享密钥(Signed Pre Key):一个中期的符合 DH 协议的密钥对,用户注册时创建,由身份密钥签名,并定期进行轮换,此密钥可能是为了保护身份密钥不被泄露;

  3)一次性预共享密钥(One-Time Pre Keys):一次性使用的 Curve25519 密钥对队列,安装时生成,不足时补充。

  所有人都要将这 3 种密钥对的公钥上传到服务器上,以便其他人发起会话时使用。

  假如 Alice 要给 Bob 发送消息,首先要和 Bob 确定消息密钥,流程大致如下:

  2)Alice 从服务器获取 Bob 的三种密钥对的公钥:身份密钥对IPK-B、已签名的预共享密钥 SPK-B、一次性预共享密钥 OPK-B;

  3)Alice 开始使用 DH 协议计算协商密钥,要引入参数包括:自己创建的两个密钥对的私钥,以及 Bob 的三个公钥。然后用类似排列组合的方式,将自己的私钥与对方的公钥分别带入 DH 算法计算。

  但是 DH 这个密钥太长,不适合作为消息密钥,所以对这个初始密钥进行一次 KDF 计算,以衍生出固定长度的消息密钥 S:

  在即时通讯场景中,除了二人之间的聊天以外,还有一个重要的场景就是群聊,那么群聊时的多人消息如何做端到端加密呢?

  而 Signal Protocol 在群组聊天中的设计与二人聊天又有所不同,由于群聊的保密性要求相对低一些,只采用了 KDF 链棘轮+公钥签名来进行加密通讯以保障加密的前向安全。

  2)每个群组成员用向其它成员单独加密发送链密钥(Chain Key)和签名公钥。此时每一个成员都拥有群内所有成员的链密钥和签名公钥;

  3)当一名成员发送消息时,首先用 KDF 链棘轮算法生成的消息密钥加密消息,然后使用私钥签名,再将消息发给服务器,由服务器发送给其它成员;

  4)其它成员收到加密消息后,首先使用发送人的签名公钥验证,验证成功后,使用相应的链密钥生成消息密钥,并用消息密钥解密;

  5)当群组成员离开时,所有的群组成员都清除自己链密钥和签名公钥并重新生成,再次单独发给每一位成员。这样操作,离开的成员就无法查看群组内的消息了。

  上面我们介绍了即时通信中二人聊天和群组聊天的端到端加密全部过程。但是正常情况下端到端消息加密只是加密消息的实际负载部分(即只加密消息“体”部分),而消息的控制层则不会被加密,因为消息转发服务器需要根据控制信息进行消息转发或路由(否则肯定大大影响IM底层的路由和通信效率,因为需要反复加密解密)。