今年国庆前夕接手一个外部项目,说是要保障接口数据安全,数据安全相对容易些,接口安全嘛emmmmm, 这个要考虑加解密算法、白名单之类的问题了。于是打算今天搞一期接口安全为题的成长之路番外篇。
为什么要考虑接口安全
想必各位大佬们写一个非核心的业务接口不用花很多时间精力吧,但大家有没有考虑过一个问题:我们在开发一些系统,尤其是对公、对客系统时(如小程序、公众号等),一旦接口数据被劫持、篡改甚至恶意非法远程调用接口,那么后果将不堪设想。这时候就要仔细考虑一下接口的安全问题了。
接口安全问题有哪些
- 接口访问权限问题,我所了解的有登录访问权限、角色访问权限和行为访问权限
登录访问权限:
所谓的登录访问权限是指只有认证过的用户才可以有权访问接口,拒绝未认证用户的访问
常见的解决方式为shiro、spring security 框架,一劳永逸解决登录访问问题。
不常见的解决方式为aop、拦截器自定义登录检验服务模块。角色访问权限:
所谓的角色访问权限是指只有相关的角色才有权访问,角色信息在用户登录认证之后就可获取,也就是说不同角色的用户登录后就被赋予了不同的角色访问权限。就比如说可以给某一系统设置超级管理员和普通员工两个角色。
根据粒度不同解决方式分为注解+aop和拦截器形式【上面两种访问权限如何实现的先挖个坑,下期来填】
行为访问权限:
所谓的行为访问权限是指限制某一角色进行某一行为的操作。
- 接口数据安全问题,分为传入参数安全问题和返回参数安全问题。
传入参数安全问题:
传入参数一般是指请求参数,一般是指请求体参数
@RequestMapping("test") public ResponseData test(@RequestBody String param1) { .... }
这里的param1就是传入参数
返回参数
返回参数一般是指接口返回前端的参数,如
public ResponseData test(@RequestBody String param1) { .... }
data: {errcode:'200',data:{}, errmsg:'插入成功!'}
这里的data就是返回参数
处理此类问题最好的办法就是进行前后端加解密。
- 接口访问ip地址问题
为防止出现DDOS分布式攻击,防止出现代理访问的情况
接口数据安全问题加密方式
- AES 对称加密(生成密钥形式进行加解密,加密密钥和解密密钥为同一把)
特点:加解密效率高,适合加解密数据量大的内容,但安全性不高,无法认证。 - RSA 非对称加密 (根据算法生成公钥、私钥,公钥加密,私钥解密)
特点:加解密效率低【因为公私钥长度较长且为模运算】,适合加解密数据量小的内容,安全性高,具备认证功能。 - AES + RSA 混合加密(先用AES 对明文进行加密,再用RSA公钥对AES密钥进行加密,返回密文和RSA加密后密钥)
特点:结合了方法1、2的优点,较好的规避了方法1、2的缺点
介绍AES+RSA算法的实现原理
注:rsa 公钥加密,rsa私钥解密
AES+RSA算法后端代码
借鉴了一下这位大佬的代码
https://blog.csdn.net/qq_36360181/article/details/111871712
rsa加解密算法代码
import com.alibaba.fastjson.JSONObject;
import com.zygswo.test1.common.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* rsa 加解密工具类
* @author 章瑜亮
*/
@Slf4j
public class RsaEncryptUtil<T> {
/**
* 加密算法
*/
private static final String ALGO_NAME = "RSA";
/**
* RSA密钥长度必须是64的倍数,在512~65536之间。默认是1024
*/
public static final int KEY_SIZE = 2048;
/**
* 算法默认为RSA/NONE/PKCS1Padding,未验证
*/
public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
/**
* 加密请求返回对象的公钥
*/
private static final String PUBLIC_KEY = "";
/**
* 解密请求返回对象的私钥(放在配置文件中)
*/
private static final String PRIVATE_KEY = "";
/**
* 注意只能生成一次
* @return 私钥公钥密钥对
*/
private static Map<String,String> generateKeyPairs() {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGO_NAME);
generator.initialize(KEY_SIZE); //rsa长度
KeyPair keyPair = generator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, String> map = new ConcurrentHashMap<>();
map.put("public_key", Base64.getEncoder().encodeToString(publicKey.getEncoded()));
map.put("private_key",Base64.getEncoder().encodeToString(privateKey.getEncoded()));
return map;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
log.error(e.getMessage());
}
return null;
}
/**
* 给AES密钥解密,再调用AES解密
* @param privatekey 解密用的rsa私钥
* @param encodedText 密文
* @param aesKeyEncrypt rsa加密后的AES密钥
* @return 解密后的明文
*/
public static String decryptionRsaAndAes(String privatekey, String encodedText,String aesKeyEncrypt){
// 解密
String aesKeyDecrypt = decodeString(privatekey, aesKeyEncrypt);
//解密后aes密钥
String data = AesEncryptUtil.decryptStringBySecret(encodedText, aesKeyDecrypt);
return data;
}
/**
* 调用AES加密明文(JSON OBJECT),再用RSA算法加密AES密钥
* @param data 明文
* @return 解密后的明文
*/
public static <T> Map<String, String> encryptionRsaAndAes(T data) {
//加密明文
log.info("-------------------data=" + JSONObject.toJSONString(data) + "---------------------");
String encodedText = AesEncryptUtil.encryptString(JSONObject.toJSONString(data), AesEncryptUtil.SYMMETRY_ENCRYPT.AES);
log.info("-------------------encodedText=" + encodedText + "---------------------");
// 加密后aes密钥
String aesKeyEncrypt = encodeString(PUBLIC_KEY, AesEncryptUtil.DEFAULT_SECRET);
//解密后aes密钥
Map<String, String> map = new ConcurrentHashMap<>();
map.put("encodedText", encodedText);
map.put("aesKeyEncrypt",aesKeyEncrypt);
return map;
}
/**
* 生成公私钥的算法(只生成一次)
* @param args 参数
*/
// public static void main(String[] args) {
// Map<String,String> keyPairs = generateKeyPairs();
// String publicKey = keyPairs.get("public_key");
// String privateKey = keyPairs.get("private_key");
// log.info("----------------publicKey = " + publicKey + "-------------------");
// log.info("----------------privateKey = " + privateKey + "-------------------");
// String encodedStr = encodeString(publicKey, "123456");
// log.info("----------------base64Str = " + encodedStr + "-------------------");
// String result = decodeString(privateKey, encodedStr);
// log.info("----------------final result = " + result + "-------------------");
// }
/**
* 加密算法
* @param needEncodeText 需要加密的字符串
* @param publicKey 加密密钥(公钥)
*/
private static String encodeString(String publicKey, String needEncodeText) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//将字符串转PublicKey对象
// bytes 为base64处理后的字节码,所以要先base64Encoder一下
byte[] data = Base64.getDecoder().decode(publicKey.getBytes());
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance(ALGO_NAME);
PublicKey publicKey1 = fact.generatePublic(spec);
//初始化加密工具
cipher.init(Cipher.ENCRYPT_MODE, publicKey1);
//将需要加密的字串转字节码
byte[] bytes = needEncodeText.getBytes();
// 加密
byte[] resultBytes = cipher.doFinal(bytes);
//转base64(不清楚是否需要判断为空)
return Base64.getEncoder().encodeToString(resultBytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeySpecException |
InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
log.error(e.getMessage());
throw new MyException(e.getMessage());
}
}
/**
* 解密算法
* @param needDecodeText 需要解密的字符串
* @param privateKey 解密密钥(私钥)
*/
private static String decodeString(String privateKey, String needDecodeText) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//将字符串转PublicKey对象
// bytes 为base64处理后的字节码,所以要先base64Encoder一下
byte[] data = Base64.getDecoder().decode(privateKey.getBytes());
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance(ALGO_NAME);
PrivateKey privateKey1 = fact.generatePrivate(spec);
//初始化加密工具
cipher.init(Cipher.DECRYPT_MODE, privateKey1);
//将需要加密的字串转字节码
//base64转byte
byte[] bytes = Base64.getDecoder().decode(needDecodeText.getBytes());
// 解密
byte[] resultBytes = cipher.doFinal(bytes);
return resultBytes == null ? null : new String(resultBytes);
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
throw new MyException("解密失败,请检查加解密密钥");
}
}
}
aes加解密算法代码
import com.zygswo.test1.common.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
/**
* aes加密
*/
@Slf4j
public class AesEncryptUtil {
//DES算法要有一个随机数源,因为Random是根据时间戳生成的有限随机数,比较容易破解,所以在这里使用SecureRandom
private static SecureRandom secureRandom = new SecureRandom();
//AES默认加密key, 长度固定为16位,代表128bit的密钥
static final String DEFAULT_SECRET = "自定义加密key";
//字符集
private static final String CHARSET = "UTF-8";
/**
* 功能描述: <br>
* 〈字符串解密〉<密钥参数>
*/
public static String decryptStringBySecret(String source, String secret) {
try {
byte[] sourceBytes = Base64.getDecoder().decode(source);
byte[] secretBytes = secret.getBytes(CHARSET);
byte[] encryptBytes = decrypt(sourceBytes, secretBytes, SYMMETRY_ENCRYPT.AES);
return new String(encryptBytes, CHARSET);
} catch (Exception e) {
e.printStackTrace();
throw new MyException("解密失败,请检查加解密密钥");
}
}
/**
* 功能描述: <br>
* @param source 要加密的文本
* @param encryptType 加密类型(AES,DES)
* 〈字符串加密〉
*/
public static String encryptString(String source, SYMMETRY_ENCRYPT encryptType) {
try {
byte[] sourceBytes = source.getBytes(CHARSET);
byte[] secretBytes = DEFAULT_SECRET.getBytes(CHARSET);
byte[] encryptBytes = encrypt(sourceBytes, secretBytes, encryptType);
return Base64.getEncoder().encodeToString(encryptBytes);
} catch (InvalidKeyException | NoSuchAlgorithmException | UnsupportedEncodingException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeySpecException e) {
e.printStackTrace();
throw new MyException(e.getMessage());
}
}
/**
* 功能描述: <br>
* 〈字符串解密〉
*/
public static String decryptString(String source, SYMMETRY_ENCRYPT encryptType) {
try {
byte[] sourceBytes = Base64.getDecoder().decode(source);
byte[] secretBytes = DEFAULT_SECRET.getBytes(CHARSET);
byte[] encryptBytes = decrypt(sourceBytes, secretBytes, encryptType);
return new String(encryptBytes, CHARSET);
} catch (InvalidKeyException | NoSuchAlgorithmException | UnsupportedEncodingException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeySpecException e) {
e.printStackTrace();
throw new MyException(e.getMessage());
}
}
/**
* 功能描述: <br>
* 〈使用原始密钥数据转换为SecretKey对象〉
*/
private static SecretKey getSecretKey(byte[] keyBytes, String type) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
if ("DES".equals(type)) {
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(type);
// 创建一个DESKeySpec对象
DESKeySpec dks = new DESKeySpec(keyBytes);
// 将DESKeySpec对象转换成SecretKey对象
return keyFactory.generateSecret(dks);
}
return new SecretKeySpec(keyBytes, type);
}
/**
* 功能描述: <br>
* 〈DES加密〉
*/
private static byte[] encrypt(byte[] contentArray, byte[] keyArray, SYMMETRY_ENCRYPT encryptType) throws InvalidKeyException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeySpecException {
return symmetryDE(contentArray, keyArray, Cipher.ENCRYPT_MODE, encryptType);
}
/**
* 功能描述: <br>
* 〈DES解密〉
*/
private static byte[] decrypt(byte[] encryptArray, byte[] keyArray, SYMMETRY_ENCRYPT encryptType) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
return symmetryDE(encryptArray, keyArray, Cipher.DECRYPT_MODE, encryptType);
}
/**
* 功能描述: <br>
* 〈Cipher 加密解密操作〉
*/
private static byte[] symmetryDE(byte[] contentArray, byte[] keyArray, int mode, SYMMETRY_ENCRYPT encryptType) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
//获取密钥对象
SecretKey secretKey = getSecretKey(keyArray, encryptType.getType());
//获取真正执行加/解密操作的Cipher
Cipher cipher = Cipher.getInstance(encryptType.getEncrypt());
//用密匙初始化Cipher对象
cipher.init(mode, secretKey, secureRandom);
//执行加/解密操作
return cipher.doFinal(contentArray);
}
/**
* 功能描述: <br>
* 〈加密方式〉
*
* @author Blare
* @date 2019/12/11
*/
enum SYMMETRY_ENCRYPT {
AES("AES", "AES/ECB/PKCS5Padding"),
DES("DES", "DES/ECB/PKCS5Padding");
SYMMETRY_ENCRYPT(String type, String encrypt) {
this.type = type;
this.encrypt = encrypt;
}
private String type;
private String encrypt;
public String getType() {
return type;
}
public String getEncrypt() {
return encrypt;
}
}
}
注意事项
之前尝试过用拦截器去进行接口传入参数解密的操作但是发现由于拦截器是在dispatcherServlet之后,调用controller之前进行操作,与@RequestBody注解一起使用会报steam closed 异常,因此最好使用过滤器进行处理。
过滤器代码:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 过滤器
* @author 章瑜亮
*/
@Component
@Slf4j
@WebFilter(filterName = "requestBodyFilter", urlPatterns = "/*", asyncSupported = true)
public class RequestBodyFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws ServletException, IOException{
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在chain.doFiler方法中传递新的request对象
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
此外,为了解决请求只能拿取一次的情况,实现了HttpServletRequestWrapper这个类
代码如下:
import com.alibaba.fastjson.JSONObject;
import com.zygswo.test1.common.exception.MyException;
import com.zygswo.test1.common.util.RsaEncryptUtil;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* 确保request可以多次读取
* @author 章瑜亮
*/
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 私钥
*/
private String privateKey = "";
/**
* 请求体
*/
private byte[] body;
public RequestWrapper(HttpServletRequest request) {
super(request);
// 获取requestbody中的数据
body = getBodyString(request);
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 定义内存中的输入流
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
// 使用内存输入流读取数据
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
/**
* 永久获取request请求头并进行请求体参数处理
* @param request
* @return
*/
private byte[] getBodyString(HttpServletRequest request) {
try (BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
byte[] bytes= baos.toByteArray();
String body = new String(bytes);
log.info("------------------- body = " + body + "------------------------");
if (isNotBlank(body)) {
JSONObject jsonBody = JSONObject.parseObject(body);
if (null != jsonBody) {
String aesKey = request.getHeader("token");
String dataEncrypt = jsonBody.getString("data");
String data;
JSONObject json = null;
try {
log.info("------------------- dataEncrypt = " + dataEncrypt + "------------------------");
log.info("------------------- aesKey = " + aesKey + "------------------------");
data = RsaEncryptUtil.decryptionRsaAndAes(privateKey, dataEncrypt, aesKey);
// 如果数据不为空就编译
if (isNotBlank(data)) {
//如果参数为空前端传回undefined
if ("undefined".equalsIgnoreCase(data)) {
json = new JSONObject();
} else {
json = JSONObject.parseObject(data);
}
}
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
throw new MyException("解密失败,请检查加解密密钥");
}
if (json != null) {
body = json.toJSONString();
}
log.info("------------------- body = " + body + "------------------------");
return body.getBytes();
}
}
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
throw new MyException("解密失败,请检查加解密密钥");
}
return null;
}
/**
* 判断字符串是否为空
* @param string 字符串
* @return 是否为空
*/
private boolean isNotBlank(String string) {
return string != null && !"".equals(string);
}
}
最后只要将过滤器进行注册就可以使用啦
前端代码实现
准备工作:
- npm 下载 Crypto.js npm install crypto-js
- npm 下载 jsencrypt.min.js
import CryptoJS, {
enc } from "crypto-js";
import JSEncrypt from './jsencrypt.min.js'
export default {
// AES密钥(加密前)
aesKey : '',
// AES密钥(加密后)
aesKeyEncrypt: '',
/**
* rsa + aes加密
*@param word:需要加密的内容
*@returns {*} :返回加密的内容
*/
encryptFun:function(word) {
//初始化AES密钥
this.aesKey = this.get32RandomNum();
//加密AES密钥
var encrypt = new JSEncrypt();
console.log(publicKey);
encrypt.setPublicKey(publicKey);
this.aesKeyEncrypt = encrypt.encrypt(this.aesKey);
//AES加密密钥
var key = CryptoJS.enc.Utf8.parse(this.aesKey);
//AES加密内容
var srcs = CryptoJS.enc.Utf8.parse(word);
//AES加密
var encrypted =CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB, padding:CryptoJS.pad.Pkcs7 });
//返回
return encrypted.toString();
},
/**
* rsa + aes解密
*@param encodedText:需要解密的内容
*@returns {*} :返回解密的内容
*/
decryptFun:function(encodedText) {
//获取参数
var resultObj = JSON.parse(encodedText);
//获取加密后的aeskey 和内容
var aesKeyEncrypt = resultObj['aesKeyEncrypt'];
var encodedText = resultObj['encodedText'];
console.log("解密前密文 ===> " + encodedText);
//解密AES密钥
var decrypt = new JSEncrypt();
console.log(privateKey);
decrypt.setPrivateKey(privateKey);
var aesKey = decrypt.decrypt(aesKeyEncrypt);
console.log("解密后AES密钥 ===> " + aesKey);
//AES解密密钥
var key = CryptoJS.enc.Utf8.parse(aesKey);
//AES解密
var decrypted = CryptoJS.AES.decrypt(encodedText, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
//返回
console.log("解密后密文 ===> " + CryptoJS.enc.Utf8.stringify(decrypted).toString());
var result=CryptoJS.enc.Utf8.stringify(decrypted).toString();
return JSON.parse(result);
},
/**
* 加密参数
* @param data
* @returns {string}
*/
getEncryptData: function(data) {
return JSON.stringify({
"data":this.encryptFun(JSON.stringify(data))});
},
/**
* 获取32位随机码
* @returns {string}
*/
get32RandomNum: function(){
var chars = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
var nums="";
for(var i=0;i<32;i++){
var id = parseInt(Math.random()*61);
nums+=chars[id];
}
return nums;
},
/**
* 判断url是否在验证白名单中
* @param url
*/
verifyWhiteList: function(url) {
var whiteList = ["/login"];
for (var i = 0;i < whiteList.length;i++){
if(url.indexOf(whiteList[i]) != -1){
return true;
}
}
return false;
},
/**
* rsa加密
*/
rasEncrypt: function() {
var encrypt = new JSEncrypt();
encrypt.setPublicKey(this.publicKey);
// 这里输出加密后的字符串
console.log(encrypt.encrypt("你好asd1"));
},
/**
* aes加密
*@param word:需要加密的内容
*@returns {*} :返回加密的内容
*/
encrypt: function(word) {
varkey = CryptoJS.enc.Utf8.parse("abcdefgabcdefg12");
var srcs =CryptoJS.enc.Utf8.parse(word);
var encrypted =CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB, padding:CryptoJS.pad.Pkcs7 });
return encrypted.toString();
},
/**
* aes解密
* @param word
* @returns {*}
*/
decrypt: function(word) {
var key =CryptoJS.enc.Utf8.parse("abcdefgabcdefg12");
var decrypt =CryptoJS.AES.decrypt(word, key, {
mode: CryptoJS.mode.ECB, padding:CryptoJS.pad.Pkcs7 });
returnCryptoJS.enc.Utf8.stringify(decrypt).toString();
}
}
写在最后
还有个问题需要注意,那就是jre环境的security包需要修改两个jar包 local_policy.jar 和 US_export_policy.jar 以允许进行256位的密钥加解密。
踩了不少坑,终于搞定了,最后放张实现后的截图