【AES 算法】实现服务端 Java 加密,前端 JS 解密
目录
- 背景
- AES CBC 加解密算法
- 代码实现
- Java 生成 key 和 iv
- Java 加密 & 解密
- JavaScript 解密
- 小结
背景
我们的游戏充值平台马上要到货一批充值码,需要入库。之前充值码发奖相关的需求都是我做的,但在存储充值码的时候没有加密,是明文存储的。
现在的需求是,数据库中的充值码需要密文存储。这就涉及到:
- 提供一个新增充值码记录的接口,请求参数为明文,使用 Java 加密后把充值码密文存入数据库;
- 发送充值码邮件时,需要使用 Java 解密充值码,给用户发送明文;
- 前端展示充值码时,服务端传送密文,前端使用 JavaScript 解密,给用户展示明文;(纯属多此一举,因为 HTTPS 通信本来就是加密的。但业务方坚持要这样做。)
AES CBC 加解密算法
这是我第一次做加密相关的需求。一开始(几周前吧)图省事,想着在 StackOverflow 上搜一下,一两行代码就搞定了,不就是个加密嘛。后来发现,怎么都这么复杂啊,一直搜到我身心俱疲,也没找到简单的方法。
这回我静下心来,好好读了一篇介绍 AES 的文章,终于大体上搞明白了。
参考链接:Java AES Encryption and Decryption
简单来说就是,AES 分为很多模式,但大家基本上都用 CBC。
在 CBC 模式下,除了秘钥 key
之外,为了增强安全性,还需要一个 iv
。(最基础的 ECB 模式不需要 iv
,只需要 key
,但该模式不提倡使用)。
key
有 128、192、256 位三种选择,iv
固定是 128 位,因为加密块固定是 128 位,需要加密的信息需要先分成 128 位大小的块,如果最后一块不足 128 位需要填充到 128 位(padding)。实际上并不需要用户自己填充,指定参数就行。
代码实现
Java 生成 key 和 iv
生成一个新的 key
(默认 128 位):
public static SecretKey generateKey(int n) throws NoSuchAlgorithmException {KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(n);SecretKey key = keyGenerator.generateKey();return key;
}
生成一个新的 iv
:
public static IvParameterSpec generateIv() {byte[] iv = new byte[16];new SecureRandom().nextBytes(iv);return new IvParameterSpec(iv);
}
当然,生成之后最终是要用字符串的格式保存和传送 key
和 iv
的。下面使用 base64
格式保存:
// 使用上面的方法生成 key 并转换为 base64 格式
SecretKey key = EncryptUtils.generateKey(128);
String keyBase64 = Base64.getEncoder().encodeToString(key.getEncoded());// 使用上面的方法生成 iv 并转换为 base64 格式
IvParameterSpec ivParameterSpec = EncryptUtils.generateIv();
String ivBase64 = Base64.getEncoder().encodeToString(ivParameterSpec.getIV());
Java 加密 & 解密
首先,可以把 base64
格式的 key
和 iv
转换回 Java 中的类型(参考链接:Converting Secret Key into a String and Vice Versa)
代码如下:
// 导入 key
byte[] keyBytes = Base64.getDecoder().decode(keyBase64);
SecretKey key = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");// 导入 iv
byte[] ivBytes = Base64.getDecoder().decode(ivBase64);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
之后就是用 key
和 iv
来进行加密和解密了:
String algorithm = "AES/CBC/PKCS5Padding";// 加密,input 是要加密的明文,返回的是一个 base64 格式的密文:
public static String encrypt(String algorithm, String input, SecretKey key,IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,InvalidAlgorithmParameterException, InvalidKeyException,BadPaddingException, IllegalBlockSizeException {Cipher cipher = Cipher.getInstance(algorithm);cipher.init(Cipher.ENCRYPT_MODE, key, iv);byte[] cipherText = cipher.doFinal(input.getBytes());return Base64.getEncoder().encodeToString(cipherText);
}// 解密,cipherText 是 base64格式的密文
public static String decrypt(String algorithm, String cipherText, SecretKey key,IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,InvalidAlgorithmParameterException, InvalidKeyException,BadPaddingException, IllegalBlockSizeException {Cipher cipher = Cipher.getInstance(algorithm);cipher.init(Cipher.DECRYPT_MODE, key, iv);byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));return new String(plainText);
}
当然,实际使用时还是会做一些封装什么的,比如我把 cipher 单独存起来了。但大体就是这样了。
JavaScript 解密
JS 这边使用 CryptoJS
这个库来解密。
我没找到介绍 JS AES 加解密的特别好的文章,CryptoJS
的文档我感觉写的也不是很好。后来一路磕磕绊绊,看了好多个 StackOverflow 和 JSFiddle 之类的,花了得有俩小时,终于找到了解密方法:
参考链接:AES encryption using Java and decryption using Javascript
// 从 base64 格式导入 key 和 iv
var key = CryptoJS.enc.Base64.parse('nlCdv7/wqRIsf1iWzqz96Q==');
var iv = CryptoJS.enc.Base64.parse('n9CvQB/1quXtItsdhnel2g==');function decrypt(encrypted) {var cipherParams = CryptoJS.lib.CipherParams.create({// 从 base64 格式导入密文ciphertext: CryptoJS.enc.Base64.parse(encrypted)});// 解密return CryptoJS.AES.decrypt(cipherParams, key, {iv: iv,padding: CryptoJS.pad.Pkcs7,mode: CryptoJS.mode.CBC}).toString(CryptoJS.enc.Utf8);
}
小结
用 Java 进行 AES CBC 加密/解密还是比较简单的,之前只因我太急躁,错误地留下了“这件事很难”的印象。
先把加密的基本原理和流程搞清楚,再做就好多了。
【AES 算法】实现服务端 Java 加密,前端 JS 解密相关推荐
- 浅谈客户端与服务端的加密通讯(HTTPS/AES/RSA/RequestBodyAdviceAdapter/ResponseBodyAdvice)
目录 前言 HTTPS与SSL证书 AES对称加密 RSA非对称加密 AES + RSA 组合加密 服务端请求参数解密拦截器RequestBodyAdviceAdapter 服务端返回参数加密拦截器R ...
- 客户端JavaScript加密数据,服务端Java解密数据
原文:http://blog.csdn.net/peterwanghao/article/details/43303807 在普通的页面提交时,如果没有使用SSL,提交的数据将使用纯文本的方式发送.如 ...
- 区块链教程Fabric1.0源代码分析流言算法Gossip服务端二
区块链教程Fabric1.0源代码分析流言算法Gossip服务端二 Fabric 1.0源代码笔记 之 gossip(流言算法) #GossipServer(Gossip服务端) 5.2.commIm ...
- android binder - 客户端(c++层) 调用 服务端(java层),服务端回调客户端 例子
学习了: android binder - 客户端(java层) 调用 服务端(c++层) 例子 http://blog.csdn.net/ganyue803/article/details/4131 ...
- gossip 区块链_区块链教程Fabric1.0源代码分析流言算法Gossip服务端一兄弟连区块链教程-阿里云开发者社区...
区块链教程Fabric1.0源代码分析流言算法Gossip服务端一,2018年下半年,区块链行业正逐渐褪去发展之初的浮躁.回归理性,表面上看相关人才需求与身价似乎正在回落.但事实上,正是初期泡沫的渐退 ...
- java des加密与js解密
java代码:import java.io.IOException; import java.security.SecureRandom;import javax.crypto.Cipher; imp ...
- jsp使用rsa加密服务端Java解密
jsp 需要引入 jsencrypt.js 网站:https://www.bootcdn.cn/jsencrypt/ 可以进行获取下载 jsp代码 // 这里是从服务端获取的公钥 var public ...
- java websocket 实现_JAVA (Tomcat服务器)使用WebSocket实现服务端与HTML前端通信
在一个项目中要使用WebSocket技术来实现服务器与浏览器实时通信交互,在网上也找了许多资料.为了防止以后忘记具体的使用过程,下面我把自己的使用过程和方法记录下来方便自己以后使用. 项目背景: 基于 ...
- 游戏社区App (三):客户端与服务端的加密处理 和 登录
http请求数据无论是GET或者POST都可能会被抓包获取到数据.为了避免用户的敏感数据被窃取(比如密码),需要对数据进行加密处理. 一.相关名词解析 RSA:非对称加密. 会产生公钥和私钥,公钥在客 ...
最新文章
- 【Verilog HDL 训练】第 01 天
- python爬虫---从零开始(一)初识爬虫
- 关于外挂新手最常见的30个问题
- DeepFaceLab史上最快的环境搭建(虚拟环境)
- Apache与Tomcat使用
- linux usb 升级脚本,linux – 使用bash脚本更新CRON
- mysql naivcat执行存储过程_mysql使用navicat编写调用存储过程
- 验证必须是数字php,Element 中表单非必填数据项 必须为数字的验证问题
- 小米路由器dns辅服务器未响应,小米路由器频繁掉线的原因与解决办法
- 2021-10-07
- Django笔记:ORM模型
- ie版本过低提示升级ie的示例
- 计算机视觉之图像分割——Snake模型(1译文)
- arc093F Dark Horse
- cmake错误集锦:unkown arguments specified
- vue 记住密码下次自动登录
- 正则表达式(一)认识正则表达式
- POI-TL使用及工具类
- 【HCIA 03】华为静态路由配置
- 联想e480一键恢复小孔_联想自带一键恢复没用了怎么处理
热门文章
- Shader 材质BlenningPhong 代码模块
- U盘被删除的文件如何恢复?
- 痔疮后水肿该怎么办?
- C语言浮点数表示法 / float表示法(浮点数表示方法)
- Windows驱动学习(六)-- FSD钩子
- vueh5获取手机前置摄像头(安卓ios),input capture属性和navigator.mediaDevices
- 介词for和with 和of的用法_常用介词用法(for to with of)
- [嵌入式] 重温Mini2440(二)移植Linux-4.9.270
- ISO 光盘镜像 启动系统
- 国人被清朝误导300年