WebSocket数据加密——AES与RSA混合加密

WebSocket数据加密——AES与RSA混合加密之前在写“一套简单的web即时通讯”,写到第三版的时候没什么思路,正好微信公众号看到一篇讲API交互加密,于是就自己搞了一套AES与RSA混合加

大家好,欢迎来到IT知识分享网。

  前言

  之前在写“一套简单的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混合加密完整实例< https://www.cnblogs.com/huanzi-qch/p/10913636.html >),我们现在用它实现一下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数据加密——AES与RSA混合加密

WebSocket数据加密——AES与RSA混合加密

  后端解密前、解密后

WebSocket数据加密——AES与RSA混合加密

WebSocket数据加密——AES与RSA混合加密

  后端加密前、加密后

WebSocket数据加密——AES与RSA混合加密

WebSocket数据加密——AES与RSA混合加密

  前端解密前、解密后

WebSocket数据加密——AES与RSA混合加密

WebSocket数据加密——AES与RSA混合加密

  完整页面演示:

  好友上线在线系统通知没有问题

WebSocket数据加密——AES与RSA混合加密

  聊天没有问题

WebSocket数据加密——AES与RSA混合加密

  后记

  WebSocket的加密就先记录到这里,其他的后面再补充

版权声明

作者:huanzi-qch

出处:https://www.cnblogs.com/huanzi-qch

若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/62841.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信