IM使用在群聊加密

1,659 阅读3分钟
原文链接: xiaozhuanlan.com

说到群聊加密,就算现在运用最广泛的Telegram也只是做到私聊群聊加密,远远还没做到群聊加密和广播群加密,而我们已经实现了这些。(号称绝对安全不是白话来的。)
这节就先介绍一下群聊加密使用的方案。

DH算法加密原理基础

DH 算法是怎么加密的呢? 过程比较复杂,首先我们假设发送方是 A,接受方是 B。A 首先生成公钥和密钥,将公钥发送出去,B 接收到 A发送的公钥,然后利用该公钥生成自己的公钥和密钥,再将自己的公钥 发送给 A,这个时候 A 拥有了自己的公钥,密钥和 B 的公钥,B 拥有了自己的公钥密钥和 A 的公钥。

之后, A 就可以使用 A自己的密钥 + B的公钥 获取到本地的密钥,B也是如此,这个时候 A 和 B 生成的本地密钥其实是相同的,这样的话也就变成了用相同的密钥加密,用相同的密钥解密。而且这样的话,我们数据传递过程中传递的是 A 和 B 的公钥,就算被黑客截取了也无济于事,他们不可能凭借着公钥将数据解密,从而保证了数据的安全性。

以下公式
DH(A私钥+B公钥) = DH密钥 = DH(A公钥+B私钥)

1.A拥有:A私钥 ,A公钥,B公钥
2.A通过(A私钥+B公钥)生成DH密钥,然后加密消息,传递时(A公钥+加密消息)
3.消息传输后,B拥有:A公钥,B公钥,B私钥
4.B通过比对服务器传递的A公钥和传递过来的A公钥是否是同一个验证,对方是否同一个人。
5.B通过DH(A公钥+B私钥)生成DH密钥,然后使用DH密钥解密消息,获取解密后的消息
A端加密

A端加密
B端解密消息B端解密消息

基本流程分为三部分
1.生成群主非对称加密,包括(群主公钥,群主公钥,加密后的群密钥)
2.生成群员非成员加密,包括(群主公钥,邀请人(即群主)公钥,加密后的群密钥)
3.上传群主和群员公钥和群密钥到服务器记录和转发。私钥还是会保存到本地。
每个成员都有其唯一标识可能是openId,可能是手机号,也有可能是唯一的公钥,服务器通过唯一标识去转发。除了唯一标识外,个人信息都是加密的。

邀请其他用户到群内
1.获取群密钥(解密邀请信息后得到群密钥)
2.通过DH生成加密群员加密信息(群主公钥,邀请人公钥,加密后的群密钥)
3.请求服务器记录和转发
其过程基本和群创建加密图的左侧是一样的。

在创建群或者被邀请的时候,会收到加密信息,里面需要使用DH算法来解密消息,获取到群密钥。

发送群信息
1.通过DH算法获取群密钥
2.使用群密钥对聊天信息进行加密

private static Pair<String, String> encryptMessage(String plainText, byte[] groupPassword) {
        //产生64位随机数
        byte[] random = Util.getSecretBytes(64);
        //使用sha512来生成一次加密密钥
        byte[] oneTimePassword = EncryptUtils.encryptSHA512(byteMerger(groupPassword, random));
        //aes密钥
        byte[] aesKey256 = new byte[32];
        //便宜向量
        byte[] iv = new byte[16];
        //将加密密钥写到aeskey中
        System.arraycopy(oneTimePassword, 0, aesKey256, 0, 32);
        //再取出加密密钥中的48位开始的256位数作为偏移量
        System.arraycopy(oneTimePassword, 48, iv, 0, 16);
        Gson gson = new Gson();
        EncryptMessageBody encryptMessageBody = new EncryptMessageBody();
        encryptMessageBody.setPlainText(plainText);
        //设置签名
        encryptMessageBody.setSign(Base64.encodeBytes(EncryptUtils.encryptSHA512(plainText.getBytes())));
        String bodyString = gson.toJson(encryptMessageBody);
        //加密密钥加密消息后,返回加密消息和随机数
        return new Pair<>(Base64.encodeBytes(EncryptUtils.encryptAES(bodyString.getBytes(), aesKey256, CBC_MODE, iv)), Base64.encodeBytes(random));
    }

封装群信息到消息体传输