SpringBoot 实现 RAS+AES 自动接口解密

接口安全老生常谈了

目前常用的加密方式就对称性加密和非对称性加密,加密解密的操作的肯定是大家知道的,最重要的使用什么加密解密方式,制定什么样的加密策略;考虑到我技术水平和接口的速度,采用的是RAS非对称加密和AES对称加密一起用!!!!

二、RSA和AES基础知识

1、非对称加密和对称加密

非对称加密

非对称加密算法是一种密钥的保密方法。非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。

公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

对称加密

加密秘钥和解密秘钥是一样,当你的密钥被别人知道后,就没有秘密可言了

AES 是对称加密算法,优点:加密速度快;缺点:如果秘钥丢失,就容易解密密文,安全性相对比较差

RSA 是非对称加密算法 , 优点:安全 ;缺点:加密速度慢

2、RSA基础知识

RSA——非对称加密,会产生公钥和私钥,公钥在客户端,私钥在服务端。公钥用于加密,私钥用于解密。

大概的流程:

客户端向服务器发送消息:客户端用公钥加密信息,发送给服务端,服务端再用私钥机密

服务器向客户端发送消息:服务端用私钥加密信息,发送给客户端,客户端再用公钥机密

当然中间要保障密钥的安全,还有很多为了保障数据安全的操作,比如数字签名,证书签名等等,在这我们就先不说了;

RSA加密解密算法支持三种填充模式,

分别是ENCRYPTION_OAEPENCRYPTION_PKCS1ENCRYPTION_NONERSA填充是为了和公钥等长。

  • ENCRYPTION_OAEP:最优非对称加密填充,是RSA加密和RSA解密最新最安全的推荐填充模式。

  • ENCRYPTION_PKCS1:随机填充数据模式,每次加密的结果都不一样,是RSA加密和RSA解密使用最为广泛的填充模式。

  • ENCRYPTION_NONE:不填充模式,是RSA加密和RSA解密使用较少的填充模式。

RSA 常用的加密填充模式

  • RSA/None/PKCS1Padding

  • RSA/ECB/PKCS1Padding

知识点:

Java 默认的 RSA 实现是 RSA/None/PKCS1Padding

在创建RSA秘钥对时,长度最好选择 2048的整数倍,长度为1024在已经不很安全了

一般由服务器创建秘钥对,私钥保存在服务器,公钥下发至客户端

DER是RSA密钥的二进制格式,PEM是DER转码为Base64的字符格式,由于DER是二进制格式,不便于阅读和理解。一般而言,密钥都是通过PEM的格式进行存储的

/*** 生成密钥对* @param keyLength  密钥长度* @return KeyPair*/
public static KeyPair getKeyPair(int keyLength) {try {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");   //默认:RSA/None/PKCS1PaddingkeyPairGenerator.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基础知识

AES 简介

AES加密解密算法是一种可逆的对称加密算法,这类算法在加密和AES解密时使用相同的密钥,或是使用两个可以简单地相互推算的密钥,一般用于服务端对服务端之间对数据进行加密解密。它是一种为了替代原先DES、3DES而建立的高级加密标准(Advanced Encryption Standard)。

作为可逆且对称的块加密,AES加密算法的速度比公钥加密等加密算法快很多,在很多场合都需要AES对称加密,但是要求加密端和解密端双方都使用相同的密钥是AES算法的主要缺点之一。

AES加密解密

AES加密需要:明文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充) AES解密需要:密文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)

AES的算法模式一般为 AES/CBC/PKCS5Padding 或 AES/CBC/PKCS7Padding

AES常见的工作模式:

  • 电码本模式(ECB)

  • 密码分组链接模式(CBC)

  • 计算器模式(CTR)

  • 密码反馈模式(CFB)

  • 输出反馈模式(OFB)

除了ECB无须设置初始化向量IV而不安全之外,其它AES工作模式都必须设置向量IV,其中GCM工作模式较为特殊。

AES填充模式

块密码只能对确定长度的数据块进行处理,而消息的长度通常是可变的,因此需要选择填充模式。

  • 填充区别:在ECB、CBC工作模式下最后一块要在加密前进行填充,其它不用选择填充模式;

  • 填充模式:AES支持的填充模式为PKCS7和NONE不填充。其中PKCS7标准是主流加密算法都遵循的数据填充算法。AES标准规定的区块长度为固定值128Bit,对应的字节长度为16位,这明显和PKCS5标准规定使用的固定值8位不符,虽然有些框架特殊处理后可以通用PKCS5,但是从长远和兼容性考虑,推荐PKCS7。

AES密钥KEY和初始化向量IV

初始化向量IV可以有效提升安全性,但是在实际的使用场景中,它不能像密钥KEY那样直接保存在配置文件或固定写死在代码中,一般正确的处理方式为:在加密端将IV设置为一个16位的随机值,然后和加密文本一起返给解密端即可。

  • 密钥KEY:AES标准规定区块长度只有一个值,固定为128Bit,对应的字节为16位。AES算法规定密钥长度只有三个值,128Bit、192Bit、256Bit,对应的字节为16位、24位和32位,其中密钥KEY不能公开传输,用于加密解密数据;

  • 初始化向量IV:该字段可以公开,用于将加密随机化。同样的明文被多次加密也会产生不同的密文,避免了较慢的重新产生密钥的过程,初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密。然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV,一般推荐初始化向量IV为16位的随机值。

三、加密策略

RAS、AES加密解密的操作都是一样,如果有效的结合到一起才能达到更好的加密效果很重要;

上面说到:

AES 是对称加密算法,优点:加密速度快;缺点:如果秘钥丢失,就容易解密密文,安全性相对比较差

RSA 是非对称加密算法, 优点:安全 ;缺点:加密速度慢

1、主要思路:

那么我们就结合2个加密算法的优点来操作:

  • 因为接口传递的参数有多有少,当接口传递的参数过多时,使用RSA加密会导致加密速度慢,所以我们使用AES加密加密接口参数

  • 因为AES的密钥key和偏移量VI都是固定的所以可以使用RSA加密

  • 客户端将AES加密后的密文和RSA加密后的密文,传递给服务器即可。

2、涉及工具类:

util包下:

  • ActivityRSAUtil

  • AES256Util

  • RequestDecryptionUtil

3、加密策略

图片

4、交互方式

前端:

1、客户端随机生成2个16为的AES密钥和AES偏移量

2、使用AES加密算法加密真实传递参数,得到参数密文“asy”

3、将AES密钥、AES偏移量和当前时间戳,格式如下:

  • key:密钥

  • keyVI:偏移量

  • time:请求时间,用户判断是否重复请求

{"key":"0t7FtCDKofbEVpSZS","keyVI":"0t7WESMofbEVpSZS","time":211213232323323
}
//转成JSON字符串

4、AES信息密钥信息,再使用RSA公钥加密,得到AES密钥的密文“sym”

5、将“sym”和“asy”作为body参数,调用接口

图片

后端:

1、在接口接收参数中,多增加2个字段接收加密后的“sym”和“asy” (名字可以自己定,能接收到就行)

2、使用RequestDecryptionUtil.getRequestDecryption()方法解密,返回解密后的真实传递参数

四、服务器自动解密

因为不是每个接口都需求加密解密,我们可以自定义一个注解,将需要解密的接口上加一个这个注解,

1、自定义解密注解:@RequestRSA
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切片
  1. AOP判断controller接收到请求是否带有@RequestRSA注解

  2. 如果带有注解,通过ProceedingJoinPointgetArgs()方法获取请求的body参数,

  3. 将body参数,传为JSONObject类,获取到"asy"和"sym"属性,再调用RequestDecryptionUtil解密获取接口传递的真实参数

  4. 获取接口入参的类

  5. 将获取解密后的真实参数,封装到接口入参的类中

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;/*** @module* @author: qingxu.liu* @date: 2023-02-08 16:41* @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 解密类

1、使用privateKey私钥对”sym“解密获取到客户端加密的AES密钥,偏移量、时间等信息

{"key":"0t7FtSMofbEVpSZS","keyVI":"0t7FtSMofbEVpSZS","time":211213232323323
}

2、获取当前时间戳,与time比较是否超过一分钟(6000毫秒),超过就抛出“Request timed out, please try again”异常

3、没有超时,将获取的到AES密钥和偏移量,再对“asy”解密获取接口传递的真实参数

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;/*** @module* @author: qingxu.liu* @date: 2023-02-09 17:43* @copyright**/
public class RequestDecryptionUtil {private final static String publicKey = "RSA生成的公钥";private final static String privateKey = "RSA生成的私钥";private final static Integer timeout = 60000;/**** @param sym RSA 密文* @param asy AES 密文* @param clazz 接口入参类* @return Object*/public static <T> Object getRequestDecryption(String sym, String asy, Class<T> clazz){//验证密钥try {//解密RSARSAPrivateKey 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."); //请求超时}//解密AESString 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 {//解密RSARSAPrivateKey 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."); //请求超时}//解密AESString 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;/*** @module* @author: qingxu.liu* @date: 2023-02-07 16:54* @copyright**/
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/PKCS1PaddingkeyPairGenerator.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;/*** @module* @author: qingxu.liu* @date: 2023-02-07 16:14* @copyright**/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%可用~~~

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3029432.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux增加硬盘分区并挂载(各个云平台操作)

第一部分&#xff0c;增加硬盘 1.购买硬盘并选择云服务器 输入lsblk 命令后即可看到刚刚添加的硬盘了 vdb就是新添加的硬盘名称了 第二部分 对硬盘进行分区处理 然后对新建磁盘进行分区 输入命令fdisk /dev/vdb 输入lsblk -f 命令查看刚刚建好的分区(看到多余的sdc不用在意…

解决mybatis的配置文件没代码提示的问题

1.将org.apache.ibatis.builder.xml包里的两个dtd文件复制出来&#xff0c;jar包里复制 2.复制dtd的url地址&#xff1a; http://mybatis.org/dtd/mybatis-3-mapper.dtd 一样的做法&#xff01; 3.关闭两个配置文件&#xff0c;重新打开&#xff0c;就可以有代码提示了&…

Vue3:项目创建

Vue 3 相对于 Vue 2 带来了许多改进和优点&#xff0c;这些改进主要是为了提高性能、开发体验和可维护性。但是对于创建项目&#xff0c;Vue3也可以采用跟Vue2相同的方式。 使用CLI创建 1. 安装Vue CLI 首先&#xff0c;确保你已经安装了Node.js&#xff08;建议使用LTS版本…

Python学习笔记------json

json简介 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据 JSON本质上是一个带有特定格式的字符串 主要功能&#xff1a;json就是一种在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互 为了让不同的语言能够相互通…

[C++核心编程-06]----C++类和对象之对象模型和this指针

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

Day 43 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

最后一块石头重量Ⅱ 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&#xff1a; 如果 x y&#xff0c;那么两…

drawio 网页版二次开发(2):开始修改代码

目录 一 说明 二 打开开发环境 1. 代码调整 2. 修改访问链接 3. 注意 三 部分功能的代码汇总 1. 保存功能 2. 菜单栏折叠按钮功能 3. 顶部菜单栏 4.在顶部菜单栏中的【文件】菜单中新增选项 &#xff08;1&#xff09; 方法一&#xff1a;单独增加 &#xff08;…

中北大学软件学院javaweb实验二JSP应用开发实验报告

实验时间 2024年4月 25 日17时至 22 时 学时数 4 1.实验名称 实验2&#xff1a;JSP应用开发(2学时) 2.实验目的 &#xff08;1&#xff09;学会编写和运行简单的JSP页面&#xff0c;理解其运行原理&#xff1b; &#xff08;2&#xff09;学会使用JSP的声明、表达式、脚…

Python数据科学 | ​Python 离群点检测算法 -- GMM

本文来源公众号“Python数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;​Python 离群点检测算法 -- GMM 星星在天空中聚集或分散&#xff0c;呈现出自然的分布。在统计学中&#xff0c;K-均值法是一种著名的聚类技术&a…

Aapache Tomcat AJP 文件包含漏洞(CVE-2020-1938)

1 漏洞描述 CVE-2020-1938 是 Apache Tomcat 中的一个严重安全漏洞&#xff0c;该漏洞涉及到 Tomcat 的 AJP&#xff08;Apache JServ Protocol&#xff09;连接器。由于 AJP 协议在处理请求时存在缺陷&#xff0c;攻击者可以利用此漏洞读取服务器上的任意文件&#xff0c;甚至…

keil5软件安装教程(MDKv5.39)

keil5软件安装分为三部分&#xff1a; 目录 1.安装mdk 2.激活mdk 3.安装STM32芯片包 1.安装mdk 安装包链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1PZoGhzI5Y19ROv7xe9QJKA?pwdgt3s 提取码&#xff1a;gt3s 1、下载keil5的压缩包并解压&#xff0c;鼠…

通俗的理解网关的概念的用途(三):你的数据包是如何到达下一层的

其实&#xff0c;这一章我写不好&#xff0c;因为这其中会涉及到一些计算和一些广播等概念&#xff0c;本人不善于此项。在此略述&#xff0c;可以参考。 每台设备的不同连接在获得有效的IP地址后&#xff0c;会根据IP地址的规则和掩码的规则&#xff0c;在操作系统和交换机&a…

【C++】继承 — 继承的引入、赋值切片详细讲解

前言 我们知道C语言是一门面向对象编程的语言&#xff0c;而面向对象编程有三大特性&#xff0c;它们分别是&#xff1a; 封装继承多态 目录 1. 继承的概念及定义1.1继承的概念1.2继承的定义格式1.3 继承的使用 2 基类和派生类对象赋值转换3 继承中的作用域3.1 派生类对象的存…

基于springboot+vue+Mysql的体质测试数据分析及可视化设计

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Windows11提示升级,我要点确定吗?

Windows 11自2021年10月推出以来&#xff0c;一直保持着缓慢而稳定的增长&#xff0c;相对来说在游戏PC上的影响力较大&#xff0c; 而Windows 10也即将于2025年10月结束支持。 截至2024年&#xff0c;Windows 11的主要版本包括24H2、 23H2、22H2和21H2。 24H2&#xff1a;20…

5月白银现货最新行情走势

美联储5月的议息会议举行在即&#xff0c;但从联邦公开市场委员会&#xff08;FOMC&#xff09;近期透露的信息来看&#xff0c;降息似乎并没有迫切性。——美联储理事鲍曼认为通胀存在"上行风险"&#xff0c;明尼阿波利斯联邦储备银行行长卡什卡利提出了今年不降息的…

数学:人工智能领域的基石与灵魂

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到了我们生活的方方面面&#xff0c;从智能家居、智能医疗到自动驾驶、智能客服&#xff0c;AI无处不在。然而&#xff0c;当我们赞叹于AI的神奇时&#xff0c;却往往忽视了其背后的推动力——数学…

5月游戏市场迎来新的体验,网易两款游戏重磅出炉

易采游戏网5月9日消息&#xff0c;随着科技的飞速发展&#xff0c;手机游戏已经成为人们休闲娱乐的重要方式。在这个领域&#xff0c;网易作为国内领先的游戏开发商&#xff0c;一直致力于为玩家带来高品质的游戏体验。近日&#xff0c;网易携手国际大厂Square Enix&#xff0c…

列表、字典推导式介绍和用法|lambda的介绍和用法

列表、字典推导式介绍和用法|lambda的介绍和用法 列表推导式示例应用与传统写法代码行数直观比较 字典推导式示例应用 lambda示例应用 列表推导式、字典推导式、lambda使用简洁语法进行代码的编写 列表推导式 用于快速创建新的列表&#xff0c;通过对现有列表进行迭代和筛选。…

技术速递|Python in Visual Studio Code 2024年5月发布

排版&#xff1a;Alan Wang 我们很高兴地宣布 Visual Studio Code 的 Python 和 Jupyter 扩展将于 2024 年 5 月发布&#xff01; 此版本包括以下公告&#xff1a; “Implement all inherited abstract classes”代码操作新的自动缩进设置Debugpy 从 Python 扩展中删除&#…