对app请求的参数和响应进行rsa加密和解密
功能实现流程图:
参考:
链接: https://www.cnblogs.com/throwable/p/9471938.html.
链接: https://www.cnblogs.com/whcghost/p/5657594.html.
链接: https://blog.csdn.net/charry0110/article/details/109495035.
链接: https://www.cnblogs.com/elaron/archive/2013/04/09/3010375.html.
链接: https://blog.csdn.net/qq_35164962/article/details/102704880.
功能说明:
主要是为了实现对app的请求的参数和响应进行加密,本次只做了json格式参数的post请求的加解密。主要考虑到get参数加密后太长会超过url长度限制。上传下载的请求也没有加解密,主要担心加解密时间太长。
除了对参数和响应内容进行加密外,还进行了时间戳的过期检查操作和签名验证的操作,来防止伪造请求访问。但是只对时间戳拼接一个头的字符串进行签名,并没有使用参数内容进行签名,想想只要破解不了我的加密方式,我用时间戳来签名应该就够了。
主要实现代码
json请求接收器
package com.wx.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wx.exception.BadRequestException;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonInputMessage;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Date;/****/
//@ControllerAdvice(basePackages = "com.wx.*.controller")
@ControllerAdvice
public class CustomRequestBodyAdvice extends RequestBodyAdviceAdapter {private final ObjectMapper objectMapper;public CustomRequestBodyAdvice(ObjectMapper objectMapper) {this.objectMapper = objectMapper;}@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return RsaProperties.enSwitch;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) throws IOException {String content = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));EncryptModel in = objectMapper.readValue(content, EncryptModel.class);//先校验一下时间戳是否过期,如果时间戳和后台当前时间相差超过5分钟,则请求判定为失效if (Math.abs(in.getTimestamp() - new Date().getTime()) > RsaProperties.deadlineMins * 1000 * 60) {throw new BadRequestException("请求已过期!");}String inRawSign = in.getTimestamp() + "";String inSign = null;try {inSign = ShaUtils.SINGLETON.sha(inRawSign);} catch (Exception e) {throw new IllegalArgumentException("验证参数签名失败!");}if (!inSign.equals(in.getSign())) {throw new IllegalArgumentException("验证参数签名失败!");}String decryptedData = null;try {decryptedData = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, in.getData());} catch (Exception e) {throw new IllegalArgumentException("参数解密失败:" + in.getData(), e);}ByteArrayInputStream inputStream = new ByteArrayInputStream(URLDecoder.decode(decryptedData, "UTF-8").getBytes(Charset.forName("UTF-8")));return new MappingJacksonInputMessage(inputStream, inputMessage.getHeaders());}
}
响应解析器
package com.wx.security;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice;import java.net.URLEncoder;/****/
@ControllerAdvice
public class CustomResponseBodyAdvice extends JsonViewResponseBodyAdvice {private final ObjectMapper objectMapper;public CustomResponseBodyAdvice(ObjectMapper objectMapper) {this.objectMapper = objectMapper;}@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return RsaProperties.enSwitch;}@Overrideprotected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,MethodParameter returnType, ServerHttpRequest request,ServerHttpResponse response) {if (request.getMethod() == HttpMethod.POST) {EncryptModel out = new EncryptModel();out.setTimestamp(System.currentTimeMillis());String decryptedData = null;try {final String jsonStr = objectMapper.writeValueAsString(bodyContainer.getValue());decryptedData = RsaUtils.encryptByPublicKey(RsaProperties.publicKey, URLEncoder.encode(jsonStr, "UTF-8"));out.setData(decryptedData);} catch (Exception e) {throw new IllegalArgumentException("返回对象加密失败:" + bodyContainer.toString(), e);}String rawSign = out.getTimestamp() + "";try {out.setSign(ShaUtils.SINGLETON.sha(rawSign));} catch (Exception e) {throw new IllegalArgumentException("返回对象签名失败:" + rawSign);}bodyContainer.setValue(out);}}
}
加密后接收对象
package com.wx.security;/*** @Author: 遛猫达人* @Description: TODO* @DateTime: 2021/8/9 11:44**/
public class EncryptModel {private String data;private Long timestamp;private String sign;public String getData() {return data;}public void setData(String data) {this.data = data;}public Long getTimestamp() {return timestamp;}public void setTimestamp(Long timestamp) {this.timestamp = timestamp;}public String getSign() {return sign;}public void setSign(String sign) {this.sign = sign;}
}
签名工具:
package com.wx.security;import org.apache.commons.codec.binary.Hex;import java.security.MessageDigest;public enum ShaUtils {/*** SINGLETON*/SINGLETON;private static final String SECRET = "xxx";private static final String CHARSET = "UTF-8";/*** @param raw* @return* @throws Exception*/public String sha(String raw) throws Exception {MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");messageDigest.update((SECRET + raw).getBytes(CHARSET));return Hex.encodeHexString(messageDigest.digest());}}
前端加密
post(api, data, options) {var newData = {};if(store.state.enSwitch){let jsonData = JSON.stringify(data);// console.log(jsonData)newData.timestamp = new Date().getTime();let shaContent = sha_prefix + newData.timestamp;newData.sign =sha256(shaContent);newData.data = encrypt(encodeURIComponent(jsonData));}else{newData = data}return this.getRequest(api, newData, options, 'POST') },
前端解密
//解密post请求响应if(store.state.enSwitch && method === 'POST'){let sign =sha256(sha_prefix + res.data.timestamp);if(sign !== res.data.sign){reject(res)uni.showToast({icon: 'none',title: "签名校验不通过"})return false}let resData = decrypt(res.data.data);// console.log(api+":"+decodeURIComponent(resData))res.data = JSON.parse(decodeURIComponent(resData))}
注意:使用前端加解密工具前先要执行以下命令安装相关依赖包:
npm install jsencrypt
npm install js-sha256
npm i encryptlong -S
前端加解密工具:
import JSEncrypt from 'encryptlong'const publicKey = 'xxx'const privateKey = 'yyyy'// 加密
export function encrypt(txt) {const encryptor = new JSEncrypt()encryptor.setPublicKey(publicKey) // 设置公钥,注意此处不要再设置私钥return encryptor.encryptLong(txt) // 对需要加密的数据进行加密
}// 解密
export function decrypt(txt) {const encryptor = new JSEncrypt()encryptor.setPrivateKey(privateKey)// 设置私钥,注意此处不要再设置公钥return encryptor.decryptLong(txt)
}
后端加解密工具:
package com.wx.security;import org.apache.commons.codec.binary.Base64;import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;/*** @author* @description Rsa 工具类,公钥私钥生成,加解密* @date**/
public class RsaUtils {private static final String SRC = "1123456";public static final String RSA = "RSA";/** *//*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/** *//*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;public static final String CHARSET_NAME = "UTF-8";/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {System.out.println("\n");RsaKeyPair keyPair = generateKeyPair();System.out.println("公钥:" + keyPair.getPublicKey());System.out.println("私钥:" + keyPair.getPrivateKey());System.out.println("\n");test1(keyPair);System.out.println("\n");}/*** 公钥加密私钥解密*/private static void test1(RsaKeyPair keyPair) throws Exception {System.out.println("***************** 公钥加密私钥解密开始 *****************");String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC);String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);System.out.println("加密前:" + RsaUtils.SRC);System.out.println("加密后:" + text1);System.out.println("解密后:" + text2);if (RsaUtils.SRC.equals(text2)) {System.out.println("解密字符串和原始字符串一致,解密成功");} else {System.out.println("解密字符串和原始字符串不一致,解密失败");}System.out.println("***************** 公钥加密私钥解密结束 *****************");}/*** 私钥解密** @param privateKeyText 私钥* @param text 待解密的文本* @return /* @throws Exception /*/public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {byte[] keyBytes = Base64.decodeBase64(privateKeyText);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(RSA);Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateK);byte[] encryptedData = Base64.decodeBase64(text);int inputLen = encryptedData.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段解密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_DECRYPT_BLOCK) {cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return new String(decryptedData, CHARSET_NAME);}/*** <p>* 公钥加密* </p>** @param str 源数据* @param publicKey 公钥(BASE64编码)* @return* @throws Exception*/public static String encryptByPublicKey(String publicKey, String str)throws Exception {byte[] data = str.getBytes(CHARSET_NAME);byte[] keyBytes = Base64.decodeBase64(publicKey);X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(RSA);Key publicK = keyFactory.generatePublic(x509KeySpec);// 对数据加密Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, publicK);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return Base64.encodeBase64String(encryptedData);}/*** 构建RSA密钥对** @return /* @throws NoSuchAlgorithmException /*/public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);keyPairGenerator.initialize(1024);KeyPair keyPair = keyPairGenerator.generateKeyPair();RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());return new RsaKeyPair(publicKeyString, privateKeyString);}/*** RSA密钥对对象*/public static class RsaKeyPair {private final String publicKey;private final String privateKey;public RsaKeyPair(String publicKey, String privateKey) {this.publicKey = publicKey;this.privateKey = privateKey;}public String getPublicKey() {return publicKey;}public String getPrivateKey() {return privateKey;}}
}
碰到的问题:
- rsa加密字符串的长度最大为117个字节,所以前后端都必须使用分段加密和分段解密
- 如果加密内容有中文,一定要先用js的encodeURIComponent方法或者java的URLEncoder.encode方法编码后再加密,然后用js的decodeURIComponent方法或者java的URLDecoder.decode方法解密,否则直接对中文加解密时偶尔会失败。
- 公钥和私钥对的生成一定要小心,我刚开始使用命令在linux上生成的公钥和私钥,但是前端能加密,后端不能解密,后来发现生成的密钥对类型有问题,后端代码无法用它来解密,然后改成了用后端代码来生成密钥对。
- 前端加解密方法中官方网站推荐把公钥和私钥都设置到JSEncrypt对象中,再进行加解密,说这样可以提高加解密的概率。但是我这样设置了之后java后台代码无法解密,然后改成了前端加密就只设置公钥,前端解密就只设置私钥就正常了。
对app请求的参数和响应进行rsa加密和解密相关推荐
- 返回结果乱码_Spring请求参数和响应结果全局加密和解密(1)
阅读文本大概需要25分钟. 前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数 ...
- springmvc 全局编码_SpringMVC请求参数和响应结果全局加密和解密
前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数据报文包括三个部分: 1.t ...
- Spring请求参数和响应结果全局加密和解密(1)
阅读文本大概需要25分钟. 前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数 ...
- java rsa加密解密_前端实现对请求参数进行RSA加密amp;解密,针对字符串过长进行分段加密amp;分段解密的处理...
前言 在需求开发中,为了安全起见,我们都会难免遇到需要对一些敏感参数进行加密或者解密.所以,今天给大家分享的就是使用jsencrypt对请求参数进行RSA加密与解密,发这篇文章其实主要因为近期我的一位 ...
- 唯品会app请求头参数authorization的逆向分析与算法还原
声明:本文内容仅供学习交流,严禁用于商业用途,否则由此产生的一切后果均与作者无关.如有冒犯,请联系我删除. 一.说明 app版本: v7.45.6 下载地址:aHR0cHM6Ly93d3cud2FuZ ...
- App 请求校验/加密方式总结
为什么80%的码农都做不了架构师?>>> 最近对几款App(橙光, 闪艺),微信小程序(链家,汽车之家,)进行破解,学习了请求的 加签.验签/加密.解密方式,特做总结. 常见的 ...
- IW会话参数、请求信息、及其响应信息
目录 IW会话参数.请求信息.及其响应信息 一.IW新会话参数 1.MS Edge浏览器: 2.Delphi FMX APP: 二.IW请求信息-App客户端 三.IW请求信息及其响应信息-App客户 ...
- 存储http请求返回参数_前端学习需要知道的 HTTP 知识(1/7)
基础知识 场景 我们打开浏览器,输入网址(比如 https://www.bilibili.com/),然后我们就可以看到 b 站的 Web 页面,Web 页面当然不能凭空显示出来.根据 Web 浏览器 ...
- HTTP请求报文和HTTP响应报文(转)
原文地址:http://blog.csdn.net/zhangliang_571/article/details/23508953 HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串, ...
最新文章
- java批处理框架采集端_使用Spring Batch批处理框架(参考)
- 大数据可视化模板_最佳大数据可视化技术
- c++ 四舍五入保留两位小数_Excel中保留小数点位数
- 我去,还在这样读写 excel 这也太低效了吧,好办法来了
- cv2.cornerHarris()详解 python+OpenCV 中的 Harris 角点检测
- 【BZOJ3226】【codevs2297】校门外的区间,线段树
- cocos creator 安卓原生平台环境_cocos creator原生平台下载文件并保存到本地
- [转载] numpy.inf
- python中怎么定义二维数组_如何在Python中定义二维数组
- H5网页去除苹果手机底部白边
- 自动驾驶 Automotive SPICE(ISO/IEC 15504) 和CMMI有什么不同?
- MATLAB中round函数的使用
- 前端xlsx插件简单说明
- PHP时间戳与日期的相互转换
- 小米红米1S 电信/联通版 专用TWRP2.8.1.1中文版 (全屏触摸/支持MTP挂载内外置存储)...
- 调用腾讯地图 输入地址获取经纬度
- HTTP请求偶尔失败(21秒后超时) - 问题排查
- 绘制3D海水温盐密度曲面(matplotlib)
- 计算机选北航还是南科大,今年高考招生“卷中卷”,大一变高四,北航四大试验班,南科大……...
- windows安装rabbitmq详细实例
热门文章
- “360安全卫士优化后,输入法图标丢失”解决方法
- 近世代数 笔记和题型连载:第一章(代数系统引入)
- 你好,三角形(LearnOpenGL With Qt)
- Nginx尚硅谷学习笔记
- BIM在国内建筑全生命周期的20个典型应用
- 微信菜单 html页面添加的,微信公众号添加菜单栏外部链接(微信添加外部链接方法)...
- arcgis10.0及以上版本,使用arcpy加载在arctoolbox中批量添加同一图层
- 如何在5天内学会Vue?聊聊我的学习方法!
- 分享一些我的学习方法
- 对于语句:f=(3.0,4.0,5.0),(2.0,1.0,0.0);的判断中,( )是正确的。