闽公网安备 35020302035485号
{
"integral": "MTExMTM0NzY5NQ==",
}
可是过年的时候,运营突然找我说无限模式积分排行榜分数不对:
ENCRYPTION_NONE:不填充模式,是RSA加密和RSA解密使用较少的填充模式。
2.RSA/ECB/PKCS1Padding
/**
* 生成密钥对
* @param keyLength 密钥长度
* @return KeyPair
*/
public static KeyPair getKeyPair(int keyLength) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); //默认:RSA/None/PKCS1Padding
keyPairGenerator.initialize(keyLength);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("生成密钥对时遇到异常" + e.getMessage());
}
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(KeyPair keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
return rsaPublicKey.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(KeyPair keyPair) {
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
return rsaPrivateKey.getEncoded();
}
3、AES基础知识输出反馈模式(OFB)
填充模式:AES支持的填充模式为PKCS7和NONE不填充。其中PKCS7标准是主流加密算法都遵循的数据填充算法。AES标准规定的区块长度为固定值128Bit,对应的字节长度为16位,这明显和PKCS5标准规定使用的固定值8位不符,虽然有些框架特殊处理后可以通用PKCS5,但是从长远和兼容性考虑,推荐PKCS7。
密钥KEY:AES标准规定区块长度只有一个值,固定为128Bit,对应的字节为16位。AES算法规定密钥长度只有三个值,128Bit、192Bit、256Bit,对应的字节为16位、24位和32位,其中密钥KEY不能公开传输,用于加密解密数据。
初始化向量IV:该字段可以公开,用于将加密随机化。同样的明文被多次加密也会产生不同的密文,避免了较慢的重新产生密钥的过程,初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密。然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV,一般推荐初始化向量IV为16位的随机值。
客户端将AES加密后的密文和RSA加密后的密文,传递给服务器即可。
RequestDecryptionUtil

{
"key":"0t7FtCDKofbEVpSZS",
"keyVI":"0t7WESMofbEVpSZS",
"time":211213232323323
}
//转成JSON字符串
4、AES信息密钥信息,再使用RSA公钥加密,得到AES密钥的密文“sym”
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestRSA {
}
2、创建一个aop切片.将获取解密后的真实参数,封装到接口入参的类中
import com.alibaba.fastjson.JSONObject;
import app.activity.common.interceptor.RequestRSA;
import app.activity.util.RequestDecryptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @copyright 请求验证RSA & AES 统一验证切面
**/
@Aspect
@Component
@Order(2)
@Slf4j
public class RequestRSAAspect {
/**
* 1> 获取请求参数
* 2> 获取被请求接口的入参类型
* 3> 判断是否为get请求 是则跳过AES解密判断
* 4> 请求参数解密->封装到接口的入参
*/
@Pointcut("execution(public * app.activity.controller.*.*(..))")
public void requestRAS() {
}
@Around("requestRAS()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//=======AOP解密切面通知=======
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method methods = methodSignature.getMethod();
RequestRSA annotation = methods.getAnnotation(RequestRSA.class);
if (Objects.nonNull(annotation)){
//获取请求的body参数
Object data = getParameter(methods, joinPoint.getArgs());
String body = JSONObject.toJSONString(data);
//获取asy和sym的值
JSONObject jsonObject = JSONObject.parseObject(body);
String asy = jsonObject.get("asy").toString();
String sym = jsonObject.get("sym").toString();
//调用RequestDecryptionUtil方法解密,获取解密后的真实参数
JSONObject decryption = RequestDecryptionUtil.getRequestDecryption(sym, asy);
//获取接口入参的类
String typeName = joinPoint.getArgs()[0].getClass().getTypeName();
System.out.println("参数值类型:"+ typeName);
Class<?> aClass = joinPoint.getArgs()[0].getClass();
//将获取解密后的真实参数,封装到接口入参的类中
Object o = JSONObject.parseObject(decryption.toJSONString(), aClass);
Object[] as = {o};
return joinPoint.proceed(as);
}
return joinPoint.proceed();
}
/**
* 根据方法和传入的参数获取请求参数 获取的是接口的入参
*/
private Object getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//将RequestBody注解修饰的参数作为请求参数
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList.get(0);
} else {
return argList;
}
}
}
3、RequestDecryptionUtil 解密类{
"key":"0t7FtSMofbEVpSZS",
"keyVI":"0t7FtSMofbEVpSZS",
"time":211213232323323
}
2、获取当前时间戳,与time比较是否超过一分钟(6000毫秒),超过就抛出“Request timed out, please try again”异常import com.alibaba.fastjson.JSONObject;
import app.activity.common.rsa.RSADecodeData;
import app.common.exception.ServiceException;
import java.security.interfaces.RSAPrivateKey;
import java.util.Objects;
public class RequestDecryptionUtil {
private final static String publicKey = "RSA生成的公钥";
private final static String privateKey = "RSA生成的私钥";
private final static Integer timeout = 60000;
/**
*
* 堆代码 duidaima.com
* @param sym RSA 密文
* @param asy AES 密文
* @param clazz 接口入参类
* @return Object
*/
public static <T> Object getRequestDecryption(String sym, String asy, Class<T> clazz){
//验证密钥
try {
//解密RSA
RSAPrivateKey rsaPrivateKey = ActivityRSAUtil.getRSAPrivateKeyByString(privateKey);
String RSAJson = ActivityRSAUtil.privateDecrypt(sym, rsaPrivateKey);
RSADecodeData rsaDecodeData = JSONObject.parseObject(RSAJson, RSADecodeData.class);
boolean isTimeout = Objects.nonNull(rsaDecodeData) && Objects.nonNull(rsaDecodeData.getTime()) && System.currentTimeMillis() - rsaDecodeData.getTime() < timeout;
if (!isTimeout){
throw new ServiceException("Request timed out, please try again."); //请求超时
}
//解密AES
String AESJson = AES256Util.decode(rsaDecodeData.getKey(),asy,rsaDecodeData.getKeyVI());
System.out.println("AESJson: "+AESJson);
return JSONObject.parseObject(AESJson,clazz);
} catch (Exception e) {
throw new RuntimeException("RSA decryption Exception: " +e.getMessage());
}
}
public static JSONObject getRequestDecryption(String sym, String asy){
//验证密钥
try {
//解密RSA
RSAPrivateKey rsaPrivateKey = ActivityRSAUtil.getRSAPrivateKeyByString(privateKey);
String RSAJson = ActivityRSAUtil.privateDecrypt(sym, rsaPrivateKey);
RSADecodeData rsaDecodeData = JSONObject.parseObject(RSAJson, RSADecodeData.class);
boolean isTimeout = Objects.nonNull(rsaDecodeData) && Objects.nonNull(rsaDecodeData.getTime()) && System.currentTimeMillis() - rsaDecodeData.getTime() < timeout;
if (!isTimeout){
throw new ServiceException("Request timed out, please try again."); //请求超时
}
//解密AES
String AESJson = AES256Util.decode(rsaDecodeData.getKey(),asy,rsaDecodeData.getKeyVI());
System.out.println("AESJson: "+AESJson);
return JSONObject.parseObject(AESJson);
} catch (Exception e) {
throw new RuntimeException("RSA decryption Exception: " +e.getMessage());
}
}
}
4、ActivityRSAUtil 工具类import org.apache.commons.io.IOUtils;
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;
import java.util.Base64;
public class ActivityRSAUtil {
/**
* 字符集
*/
public static String CHARSET = "UTF-8";
/**
* 生成密钥对
* @param keyLength 密钥长度
* @return KeyPair
*/
public static KeyPair getKeyPair(int keyLength) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); //默认:RSA/None/PKCS1Padding
keyPairGenerator.initialize(keyLength);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("生成密钥对时遇到异常" + e.getMessage());
}
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(KeyPair keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
return rsaPublicKey.getEncoded();
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey(KeyPair keyPair) {
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
return rsaPrivateKey.getEncoded();
}
/**
* 公钥字符串转PublicKey实例
* @param publicKey 公钥字符串
* @return PublicKey
* @throws Exception e
*/
public static PublicKey getPublicKey(String publicKey) throws Exception {
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* 私钥字符串转PrivateKey实例
* @param privateKey 私钥字符串
* @return PrivateKey
* @throws Exception e
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
/**
* 获取公钥字符串
* @param keyPair KeyPair
* @return 公钥字符串
*/
public static String getPublicKeyString(KeyPair keyPair){
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
return new String(org.apache.commons.codec.binary.Base64.encodeBase64(publicKey.getEncoded()));
}
/**
* 获取私钥字符串
* @param keyPair KeyPair
* @return 私钥字符串
*/
public static String getPrivateKeyString(KeyPair keyPair){
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
return new String(org.apache.commons.codec.binary.Base64.encodeBase64((privateKey.getEncoded())));
}
/**
* 公钥加密
* @param data 明文
* @param publicKey 公钥
* @return 密文
*/
public static String publicEncrypt(String data, RSAPublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength());
return new String(org.apache.commons.codec.binary.Base64.encodeBase64(bytes));
} catch (Exception e) {
throw new RuntimeException("加密字符串[" + data + "]时遇到异常"+ e.getMessage());
}
}
/**
* 私钥解密
* @param data 密文
* @param privateKey 私钥
* @return 明文
*/
public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getDecoder().decode(data), privateKey.getModulus().bitLength()), CHARSET);
} catch (Exception e) {
throw new RuntimeException("privateKey解密字符串[" + data + "]时遇到异常"+ e.getMessage());
}
}
/**
* 私钥加密
* @param content 明文
* @param privateKey 私钥
* @return 密文
*/
public static String encryptByPrivateKey(String content, RSAPrivateKey privateKey){
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] bytes = rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE,content.getBytes(CHARSET), privateKey.getModulus().bitLength());
return new String(org.apache.commons.codec.binary.Base64.encodeBase64(bytes));
} catch (Exception e) {
throw new RuntimeException("privateKey加密字符串[" + content + "]时遇到异常" + e.getMessage());
}
}
/**
* 公钥解密
* @param content 密文
* @param publicKey 私钥
* @return 明文
*/
public static String decryByPublicKey(String content, RSAPublicKey publicKey){
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getDecoder().decode(content), publicKey.getModulus().bitLength()), CHARSET);
} catch (Exception e) {
throw new RuntimeException("publicKey解密字符串[" + content + "]时遇到异常" +e.getMessage());
}
}
public static RSAPublicKey getRSAPublicKeyByString(String publicKey){
try {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return (RSAPublicKey)keyFactory.generatePublic(keySpec);
} catch (Exception e) {
throw new RuntimeException("String转PublicKey出错" + e.getMessage());
}
}
public static RSAPrivateKey getRSAPrivateKeyByString(String privateKey){
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec);
} catch (Exception e) {
throw new RuntimeException("String转PrivateKey出错" + e.getMessage());
}
}
//rsa切割解码 , ENCRYPT_MODE,加密数据 ,DECRYPT_MODE,解密数据
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
int maxBlock = 0; //最大块
if (opmode == Cipher.DECRYPT_MODE) {
maxBlock = keySize / 8;
} else {
maxBlock = keySize / 8 - 11;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] buff;
int i = 0;
try {
while (datas.length > offSet) {
if (datas.length - offSet > maxBlock) {
//可以调用以下的doFinal()方法完成加密或解密数据:
buff = cipher.doFinal(datas, offSet, maxBlock);
} else {
buff = cipher.doFinal(datas, offSet, datas.length - offSet);
}
out.write(buff, 0, buff.length);
i++;
offSet = i * maxBlock;
}
} catch (Exception e) {
throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常: " + e.getMessage());
}
byte[] resultDatas = out.toByteArray();
IOUtils.closeQuietly(out);
return resultDatas;
}
}
5、AES256Util 工具类import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;
public class AES256Util {
private static final String AES = "AES";
/**
* 初始向量IV, 初始向量IV的长度规定为128位16个字节, 初始向量的来源为随机生成.
*/
/**
* 加密解密算法/加密模式/填充方式
*/
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
private static final Base64.Encoder base64Encoder = java.util.Base64.getEncoder();
private static final Base64.Decoder base64Decoder = java.util.Base64.getDecoder();
//通过在运行环境中设置以下属性启用AES-256支持
static {
Security.setProperty("crypto.policy", "unlimited");
}
/*
* 解决java不支持AES/CBC/PKCS7Padding模式解密
*/
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* AES加密
*/
public static String encode(String key, String content,String keyVI) {
try {
javax.crypto.SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(key.getBytes(), AES);
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKey, new javax.crypto.spec.IvParameterSpec(keyVI.getBytes()));
// 获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byteEncode = content.getBytes(java.nio.charset.StandardCharsets.UTF_8);
// 根据密码器的初始化方式加密
byte[] byteAES = cipher.doFinal(byteEncode);
// 将加密后的数据转换为字符串
return base64Encoder.encodeToString(byteAES);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* AES解密
*/
public static String decode(String key, String content,String keyVI) {
try {
javax.crypto.SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(key.getBytes(), AES);
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKey, new javax.crypto.spec.IvParameterSpec(keyVI.getBytes()));
// 将加密并编码后的内容解码成字节数组
byte[] byteContent = base64Decoder.decode(content);
// 解密
byte[] byteDecode = cipher.doFinal(byteContent);
return new String(byteDecode, java.nio.charset.StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* AES加密ECB模式PKCS7Padding填充方式
* @param str 字符串
* @param key 密钥
* @return 加密字符串
* @throws Exception 异常信息
*/
public static String aes256ECBPkcs7PaddingEncrypt(String str, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, AES));
byte[] doFinal = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
return new String(Base64.getEncoder().encode(doFinal));
}
/**
* AES解密ECB模式PKCS7Padding填充方式
* @param str 字符串
* @param key 密钥
* @return 解密字符串
* @throws Exception 异常信息
*/
public static String aes256ECBPkcs7PaddingDecrypt(String str, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, AES));
byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str));
return new String(doFinal);
}
}
亲测100%可用~~~