前言
每次我们使用微信,有时候会想自己发的消息会不会被其他人监听到。特别是给自己的女朋友,发私密消息。还有就是微信管理员,会不会看你的私密消息。如果管理员想看你的,那么你也没办法。那有没一种方法,就是微信管理员也看不到你的私密消息内容。
上面说的管理员,实际就是服务端。而我们使用IM的人就是客户端,也就是要实现一个只有两个客户端能看到消息内容,即使是服务端也看不到。那么就更能保证用户的消息的隐私和信息安全。
上面说的要实现这个功能就是端到端加密,我们一起看看IM如何实现端到端加密。
端到端加密(End-to-End Encryption, E2EE)是一种安全通信的方式,其中只有通信的两个端点(通常是两个终端设备,例如用户的手机或计算机)能够理解或解密消息,而在通信路径的中间节点,包括服务提供者,都无法直接访问或理解消息内容。(Whatsapp支持)
实现思路
我们知道将消息加密,那么客户端A发送到服务端,服务端再转发到客户端B链路都是加密发送。那么不就是都是加密传输了,如果服务端也不知道密钥。那么服务端也就是看不到消息,解密后内容了。消息内容用对称加密AES,进行加解密。
这个前提,一个方案那么密钥发送线下传递。比如小明和小红,线下面对面。大家一起协商下密钥多少。大家都用这个密钥加解密,那么就可以愉快的进行私密聊天。
那万一,小明和小红是网友呢。也就是说彼此不能见面,那么怎么传递密钥。
上面的AES加密过程是需要将密钥加密后发送给对方,也就是一方需要加密,而另一方只需要解密。发明一种密码加密用加密密钥,解密用解密密钥。发送者只需要加密密钥(公钥),解密者只需要解密密钥(私钥)。那么我将公钥发给对方,让他用AES密钥用公钥密码的公钥加密后发给我,而我再用私钥解密。
我们这边需要使用到是非对称加密。
非对称加密解决密钥配送问题(key distribution problem,密钥分发问题)。
这边涉及到两个密码学知识对称密码AES和非对称加密RSA,如果大家对这两个密码不是很熟悉。可以参考下笔者密码学总结,实现开放接口验签和加密这篇文章。
整体思路,就是客户端A生成RSA公私钥,客户端A将公钥发给客户端B。客户端B收到公钥,生成AES密钥,同时将AES密钥用收到的公钥进行加密。客户端B将加密后的AES密钥发给客户端A .客户端A收到RSA加密后的密钥。客户端A,用私钥解密,获得了AES密钥对。
在AES密钥传输过程中,因为除了客户端A其他人都不知道RSA私钥。也就无法知道AES密钥了。
代码实现
1.客户端A生成RSA公私钥,将公钥发给客户端B
public void openE2EE(long userId) {
final ChatChannelDTO chatChannelDTO = CHAT_CHANNEL_MAP.get(userId);
final String[] keyPair = JiDigitUtil.genKeyPair(JiDigitUtil.RSA_ALGORITHM);
String publicKeyBase64 = keyPair[0];
String privateKeyBase64 = keyPair[1];
chatChannelDTO.setPublicKey(publicKeyBase64);
chatChannelDTO.setPrivateKey(privateKeyBase64);
chatChannelDTO.setEncryptType(CommonStatusEnum.DISABLE.getStatus());
privateMessage(publicKeyBase64, ChatMessageTypeEnum.RSA_PUBLIC_KEY.getCode(), userId);
}
2.客户端B收到公钥,生成AES密钥,同时将AES密钥用收到的公钥进行加密
case RSA_PUBLIC_KEY:
if (Objects.equals(clientInfo.getDeviceType(), DeviceTypeEnum.MOBILE.getCode())) {
// 收到RSA公钥,那么生成E2EE 密钥发给对方。作为通信密钥
final String publicKey = chatSendMessage.getMessageContent();
final String secretKey = JiDigitUtil.genSecretKey(JiDigitUtil