WebSocket数据加密——AES与RSA混合加密
前言
之前在写“一套简单的web即时通讯”,写到第三版的时候没什么思路,正好微信公众号看到一篇讲API交互加密,于是就自己搞了一套AES与RSA混合加密,无意中产生应用在WebSocket想法,好在思路都差不多,稍微改动一下就能实现,特意写这篇博客记录下来
WebSocket是HTML5 开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于 TCP 传输协议,并复用 HTTP 的握手通道。
1、建立连接,客户端通过 HTTP 请求与服务端协商升级协议。协议升级完成后,后续的数据交换则遵照 WebSocket 的协议。
2、数据交换,建立连接后,后续的操作通过TCP协议以数据帧的格式传输交换。
通过TCP进行数据交换,不像http请求,websocket数据交换在浏览器上使用开发者工具(F12)是看不到数据,但使用抓包软件仍然可以获取到这些TCP传输数据,例如Charles和Fiddler等,以明文的方式直接传输很容易被第三方监听,因此,我觉得是有必要的
前面我们实现了一套AES与RSA混合加密(详情请戳:前后端API交互数据加密——AES与RSA混合加密完整实例),我们现在用它实现一下WebSocket数据加密
思路、代码
工具类我们直接用之前API加密那一套就行,操作也与之前的API加密类似,发送前加密、收到数据后解密再交给业务处理,有个地方要注意的是,我们在进行消息转发时,要用的是接收方的前端公钥进行加密
按照我们目前的规则,项目启动后生成后端密钥对,访问登录页面时响应后端公钥给前端,前端保存到后端RSA公钥并存到sessionStorage,每个页面都引入头部head.html(使用thymeleaf的<script th:replace="head::static"></script>),在head里面获取前端RSA公钥密码、AES的key,并放到window,这些都跟之前的一样,不需要改变,需要改动的是下面这些:
建立WebSocket连接时,将当前用户的前端公钥发送到后端,后端进行Map保存
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
//因为是url的方式传值,公钥中的/需要进行转换一下,传到后端再转回来(PS:因为生成的公钥里是不存在","的,所以这里转成逗号)
websocket = new WebSocket("ws://localhost:10086/websocket/" + userId + "/" + window.jsPublicKey.replace(/\//g,","));
} else {
console.error("不支持WebSocket");
}
/**
* WebSocket服务
*/
@Component
@ServerEndpoint(value = "/websocket/{userId}/{publicKey}", configurator = MyEndpointConfigure.class)
public class WebSocketServer {
//省略其他代码 /**
* 登录用户的前端公钥Map集合(其实应该放在Redis)
*/
private static Map<Session, String> loginPublicKeyList = new HashMap<Session, String>(); /**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId, @PathParam("publicKey") String publicKey) {
//省略其他代码 //设置前端公钥,因为是url的方式传值,公钥中的/需要进行转换一下,传到后端再转回来,然后将每个用户的前端公钥存储起来
loginPublicKeyList.put(session,publicKey.replaceAll(",", "/")); }
}
前端发送前加密
//发送消息
function send(but) {
//业务操作不变,省略代码 //先加密
let aesKey = aesUtil.genKey();
let data = {
data: aesUtil.encrypt(JSON.stringify({
"type": "1",
"toUser": {"userId": toUserId},
"fromUser": {"userId": fromUserId},
"message": message,
"date": nowTime
}), aesKey),//AES加密后的数据
aesKey: rsaUtil.encrypt(aesKey, sessionStorage.getItem('javaPublicKey')),//后端RSA公钥加密后的AES的key
publicKey: window.jsPublicKey//前端公钥
};
websocket.send(JSON.stringify(data)); //业务操作不变,省略代码
}
后端收到后先解密
/**
* 服务器接收到客户端消息时调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
try {
//jackson
ObjectMapper mapper = new ObjectMapper();
//jackson 序列化和反序列化 date处理
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//JSON字符串转 HashMap
HashMap map = mapper.readValue(message, HashMap.class); //先解密
String data = (String) map.get("data");
String aesKey = (String) map.get("aesKey"); //后端私钥解密的到AES的key
byte[] plaintext = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(aesKey), RsaUtil.getPrivateKey());
aesKey = new String(plaintext); //RSA解密出来字符串多一对双引号
aesKey = aesKey.substring(1, aesKey.length() - 1); //AES解密得到明文data数据
String decrypt = AesUtil.decrypt(data, aesKey); //JSON字符串转 HashMap
HashMap hashMap = mapper.readValue(decrypt, HashMap.class); //得到hashMap,下面的业务操作跟前面的一样,这里就不贴出来了 } catch (Exception e) {
e.printStackTrace();
}
}
后端发送之前先加密,这里要用消息接收方的前端公钥进行加密
/**
* 封装一个send方法,发送消息到前端
*/
private void send(Session session, String message) {
try {
//发送前加密
//每次响应之前随机获取AES的key,加密data数据
String key = AesUtil.getKey();
String data = AesUtil.encrypt(message, key);
//用前端的公钥来解密AES的key,并转成Base64,注意:这里需要用接收方的前端公钥进行加密,从loginPublicKeyList集合获取
String aesKey = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(key.getBytes(), loginPublicKeyList.get(session)));
//发送过去的是AES加密后的data,跟RSA加密后的aesKey
session.getBasicRemote().sendText("{\"data\":\"" + data + "\",\"aesKey\":\"" + aesKey + "\"}");
} catch (Exception e) {
e.printStackTrace();
}
}
前端收到消息后先解密
//接收到消息的回调方法
websocket.onmessage = function (event) {
let data = eval("(" + event.data + ")");
//先解密
let msgObj = aesUtil.decrypt(data.data, rsaUtil.decrypt(data.aesKey, window.jsPrivateKey)); //业务操作不变,省略代码 };
效果演示
按照程序流程:
前端加密前、加密后

后端解密前、解密后


后端加密前、加密后


前端解密前、解密后


完整页面演示:
好友上线在线系统通知没有问题

聊天没有问题

后记
WebSocket的加密就先记录到这里,其他的后面再补充
WebSocket数据加密——AES与RSA混合加密的更多相关文章
- 前后端API交互数据加密——AES与RSA混合加密完整实例
前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用R ...
- AES和RSA混合加密技术在网络数据传输中的应用
原文:http://www.fx361.com/page/2017/0110/519967.shtml 摘要:文章通过分析和比较AES加密算法和RsA加密算法的实现过程和各自的特点, ...
- iOS开发之 AES+Base64数据混合加密与解密
2016-04-08 09:03 编辑: liubinqww 分类:iOS开发 来源:liubinqww 投稿 4 889 "APP的数据安全已经牵动着我们开发者的心,简单的MD5/ ...
- AES和RSA的加密过程通过面向对象的方式写成一个类,封装起来
# 面向对象的方式 实现加密方法 from Crypto.Cipher import AES from Crypto import Random from binascii import b2a_he ...
- 学习加密(四)spring boot 使用RSA+AES混合加密,前后端传递参数加解密
学习加密(四)spring boot 使用RSA+AES混合加密,前后端传递参数加解密 技术标签: RSA AES RSA AES 混合加密 整合 前言: 为了提高安全性采用了RS ...
- Android接口安全 - RSA+AES混合加密方案
转载请注明出处: http://blog.csdn.net/aa464971/article/details/51034462 本文以Androidclient加密提交数据到Java服务端后进行解密为 ...
- 我的Android进阶之旅------>Android采用AES+RSA的加密机制对http请求进行加密
前言 未加密的抓包截图 加密之后的抓包截图 基本需求及概念 AES算法 AES基本原理及算法流程 AES算法流程 RSA算法 RSA算法基本原理及流程 RSA算法实现流程 AES与RSA相结合数据加密 ...
- When I see you again(加密原理介绍,代码实现DES、AES、RSA、Base64、MD5)
关于网络安全的数据加密部分,本来打算总结一篇博客搞定,没想到东西太多,这已是第三篇了,而且这篇写了多次,熬了多次夜,真是again and again.起个名字:数据加密三部曲,前两部链接如下: 整体 ...
- 自己写的AES和RSA加密解密工具
package com.sdyy.common.utils; import java.security.Key; import java.security.KeyFactory; import jav ...
随机推荐
- [C]#include和链接
概述 对于刚接触C语言的同学来说,通常对“在文件中用#include预处理操作符引入文件”和“编译时链接多个文件”这两个操作会有所混淆,这个文章主要为了解析一下它们的区别. #include预处理操作 ...
- a是什么??
//解决Linux下默认中文字体乱码问题 QFont font("simsun", 11, QFont::Normal, false); a.setFont(font);
- Httpclient4.5.*HttpClient请求,对于新建httpclient实例时保持会话
package net.bill99.httpconsel; import java.io.IOException; import java.util.*; import java.util.Map. ...
- 服务监控之 Spring Boot Admin.
一.概述 开始阅读这篇文章之前,建议先阅读下<SpringBoot 之Actuator>,该篇文章提到 Spring Boot Actuator 提供了对单个Spring Boot的监控 ...
- opencv加载图片imread失败的原因
用简单的imshow函数加载图片,报加载失败的异常,显示没有将图片加载到内存中.原因是在配置环境是同时将*lib与*d.lib都入了附加依赖项,而项目的生成方式选择的是debug,*lib在*d.li ...
- PAT 1013 Battle Over Cities DFS深搜
It is vitally important to have all the cities connected by highways in a war. If a city is occupied ...
- 执行超大的.sql文件处理,100M++
sqlcmd的命令参数如下: 1 [-U 登录 ID] [-P 密码] 2 [-S 服务器] [-H 主机名] [-E ...
- Netty服务端Channel的创建与初始化
Netty创建服务端Channel时,从服务端 ServerBootstrap 类的 bind 方法进入,下图是创建服务端Channel的函数调用链.在后续代码中通过反射的方式创建服务端Channel ...
- ubuntu 中安装jenkins,基于docker运行jenkins
本文是在ubuntu环境下安装jenkins,jenkins运行在docker容器中,至于docker如何安装,本文不再描述,大家可以上网查询下,如何安装docker,下面先放上一个使用jenkin ...
- JavaScript 是如何运行的?
摘要: 理解JS执行原理. 原文:JavaScript 是如何运行的? 作者:hengg Fundebug经授权转载,版权归原作者所有. 什么是JavaScript? 我们来确认一下JavaScrip ...