SpringBoot如何实现RAS+AES自动接口解密
本篇内容介绍了“SpringBoot如何实现RAS+AES自动接口解密”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
一、讲个事故
接口安全老生常谈了
过年之前做了过一款飞机大战的H5小游戏,里面无限模式-需要保存用户的积分,因为使用的Body传参,参数是可见的,为了接口安全我,我和前端约定了传递参数是:用户无限模式的积分+“我们约定的一个数字”+用户id的和,在用Base64
加密,请求到服务器我再解密,出用户无限模式的积分;如下:
{"integral":"MTExMTM0NzY5NQ==",}
可是过年的时候,运营突然找我说无限模式积分排行榜分数不对:
这就很诡异了,第二名才一万多分,第一名就40多万分!!!!
一开始我以为是我解密有问题,反复看了好几变,可就两三行代码不可能有问题的!!!
没办法我去翻了好久的日志,才发现这个用户把我接口参数给改了。。。。
他把Base64
接口参数改了
事已至此,我也不能怪用户,谁让我把人家想得太简单,接口安全也没到位
所以年后上班第一件是就是把接口加密的工作搞起来
目前常用的加密方式就对称性加密和非对称性加密,加密解密的操作的肯定是大家知道的,最重要的使用什么加密解密方式,制定什么样的加密策略;考虑到我技术水平和接口的速度,采用的是RAS非对称加密和AES对称加密一起用!!!!
二、RSA和AES基础知识
1、非对称加密和对称加密
非对称加密
非对称加密算法是一种密钥的保密方法。 非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。 公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。 因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
对称加密
加密秘钥和解密秘钥是一样,当你的密钥被别人知道后,就没有秘密可言了
AES 是对称加密算法,优点:加密速度快;缺点:如果秘钥丢失,就容易解密密文,安全性相对比较差
RSA 是非对称加密算法 , 优点:安全 ;缺点:加密速度慢
2、RSA基础知识
RSA
——非对称加密,会产生公钥和私钥,公钥在客户端,私钥在服务端。公钥用于加密,私钥用于解密。
大概的流程:
客户端向服务器发送消息: 客户端用公钥加密信息,发送给服务端,服务端再用私钥机密
服务器向客户端发送消息:服务端用私钥加密信息,发送给客户端,客户端再用公钥机密
当然中间要保障密钥的安全,还有很多为了保障数据安全的操作,比如数字签名,证书签名等等,在这我们就先不说了;
RSA加密解密算法支持三种填充模式,
分别是ENCRYPTION_OAEP
、ENCRYPTION_PKCS1
、ENCRYPTION_NONE
,RSA填充是为了和公钥等长。
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的格式进行存储的
/***生成密钥对*@paramkeyLength密钥长度*@returnKeyPair*/publicstaticKeyPairgetKeyPair(intkeyLength){try{KeyPairGeneratorkeyPairGenerator=KeyPairGenerator.getInstance("RSA");//默认:RSA/None/PKCS1PaddingkeyPairGenerator.initialize(keyLength);returnkeyPairGenerator.generateKeyPair();}catch(NoSuchAlgorithmExceptione){thrownewRuntimeException("生成密钥对时遇到异常"+e.getMessage());}}/***获取公钥*/publicstaticbyte[]getPublicKey(KeyPairkeyPair){RSAPublicKeyrsaPublicKey=(RSAPublicKey)keyPair.getPublic();returnrsaPublicKey.getEncoded();}/***获取私钥*/publicstaticbyte[]getPrivateKey(KeyPairkeyPair){RSAPrivateKeyrsaPrivateKey=(RSAPrivateKey)keyPair.getPrivate();returnrsaPrivateKey.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个加密算法的优点来操作:
1、因为接口传递的参数有多有少,当接口传递的参数过多时,使用RSA加密会导致加密速度慢,所以我们使用AES加密加密接口参数
2、因为AES的密钥key和偏移量VI都是固定的所以可以使用RSA加密
3、客户端将AES加密后的密文和RSA加密后的密文,传递给服务器即可。
2、涉及工具类:
util包下:
ActivityRSAUtilAES256UtilRequestDecryptionUtil
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
importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceRequestRSA{}
2、创建一个aop切片
1、AOP判断controller接收到请求是否带有@RequestRSA
注解
2、如果带有注解,通过ProceedingJoinPoint类getArgs()方法获取请求的body参数,
3、将body参数,传为JSONObject类,获取到"asy"和"sym"属性,再调用RequestDecryptionUtil解密获取接口传递的真实参数
4、获取接口入参的类
5、将获取解密后的真实参数,封装到接口入参的类中
import .alibaba.fastjson.JSONObject;importapp.activitymon.interceptor.RequestRSA;importapp.activity.util.RequestDecryptionUtil;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.core.annotation.Order;importorg.springframework.stereotypeponent;importorg.springframework.web.bind.annotation.RequestBody;importjava.lang.reflect.Method;importjava.lang.reflect.Parameter;importjava.util.ArrayList;importjava.util.List;importjava.util.Objects;/***@module*@author:qingxu.liu*@date:2023-02-0816:41*@copyright请求验证RSA&AES统一验证切面**/@Aspectponent@Order(2)@Slf4jpublicclassRequestRSAAspect{/***1>获取请求参数*2>获取被请求接口的入参类型*3>判断是否为get请求是则跳过AES解密判断*4>请求参数解密->封装到接口的入参*/@Pointcut("execution(public*app.activity.controller.*.*(..))")publicvoidrequestRAS(){}@Around("requestRAS()")publicObjectdoAround(ProceedingJoinPointjoinPoint)throwsThrowable{//=======AOP解密切面通知=======MethodSignaturemethodSignature=(MethodSignature)joinPoint.getSignature();Methodmethods=methodSignature.getMethod();RequestRSAannotation=methods.getAnnotation(RequestRSA.class);if(Objects.nonNull(annotation)){//获取请求的body参数Objectdata=getParameter(methods,joinPoint.getArgs());Stringbody=JSONObject.toJSONString(data);//获取asy和sym的值JSONObjectjsonObject=JSONObject.parseObject(body);Stringasy=jsonObject.get("asy").toString();Stringsym=jsonObject.get("sym").toString();//调用RequestDecryptionUtil方法解密,获取解密后的真实参数JSONObjectdecryption=RequestDecryptionUtil.getRequestDecryption(sym,asy);//获取接口入参的类StringtypeName=joinPoint.getArgs()[0].getClass().getTypeName();System.out.println("参数值类型:"+typeName);Class<?>aClass=joinPoint.getArgs()[0].getClass();//将获取解密后的真实参数,封装到接口入参的类中Objecto=JSONObject.parseObject(decryption.toJSONString(),aClass);Object[]as={o};returnjoinPoint.proceed(as);}returnjoinPoint.proceed();}/***根据方法和传入的参数获取请求参数获取的是接口的入参*/privateObjectgetParameter(Methodmethod,Object[]args){List<Object>argList=newArrayList<>();Parameter[]parameters=method.getParameters();for(inti=0;i<parameters.length;i++){//将RequestBody注解修饰的参数作为请求参数RequestBodyrequestBody=parameters[i].getAnnotation(RequestBody.class);if(requestBody!=null){argList.add(args[i]);}}if(argList.size()==0){returnnull;}elseif(argList.size()==1){returnargList.get(0);}else{returnargList;}}}
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 .alibaba.fastjson.JSONObject;importapp.activitymon.rsa.RSADecodeData;importappmon.exception.ServiceException;importjava.security.interfaces.RSAPrivateKey;importjava.util.Objects;/***@module*@author:qingxu.liu*@date:2023-02-0917:43*@copyright**/publicclassRequestDecryptionUtil{privatefinalstaticStringpublicKey="RSA生成的公钥";privatefinalstaticStringprivateKey="RSA生成的私钥";privatefinalstaticIntegertimeout=60000;/****@paramsymRSA密文*@paramasyAES密文*@paramclazz接口入参类*@returnObject*/publicstatic<T>ObjectgetRequestDecryption(Stringsym,Stringasy,Class<T>clazz){//验证密钥try{//解密RSARSAPrivateKeyrsaPrivateKey=ActivityRSAUtil.getRSAPrivateKeyByString(privateKey);StringRSAJson=ActivityRSAUtil.privateDecrypt(sym,rsaPrivateKey);RSADecodeDatarsaDecodeData=JSONObject.parseObject(RSAJson,RSADecodeData.class);booleanisTimeout=Objects.nonNull(rsaDecodeData)&&Objects.nonNull(rsaDecodeData.getTime())&&System.currentTimeMillis()-rsaDecodeData.getTime()<timeout;if(!isTimeout){thrownewServiceException("Requesttimedout,pleasetryagain.");//请求超时}//解密AESStringAESJson=AES256Util.decode(rsaDecodeData.getKey(),asy,rsaDecodeData.getKeyVI());System.out.println("AESJson:"+AESJson);returnJSONObject.parseObject(AESJson,clazz);}catch(Exceptione){thrownewRuntimeException("RSAdecryptionException:"+e.getMessage());}}publicstaticJSONObjectgetRequestDecryption(Stringsym,Stringasy){//验证密钥try{//解密RSARSAPrivateKeyrsaPrivateKey=ActivityRSAUtil.getRSAPrivateKeyByString(privateKey);StringRSAJson=ActivityRSAUtil.privateDecrypt(sym,rsaPrivateKey);RSADecodeDatarsaDecodeData=JSONObject.parseObject(RSAJson,RSADecodeData.class);booleanisTimeout=Objects.nonNull(rsaDecodeData)&&Objects.nonNull(rsaDecodeData.getTime())&&System.currentTimeMillis()-rsaDecodeData.getTime()<timeout;if(!isTimeout){thrownewServiceException("Requesttimedout,pleasetryagain.");//请求超时}//解密AESStringAESJson=AES256Util.decode(rsaDecodeData.getKey(),asy,rsaDecodeData.getKeyVI());System.out.println("AESJson:"+AESJson);returnJSONObject.parseObject(AESJson);}catch(Exceptione){thrownewRuntimeException("RSAdecryptionException:"+e.getMessage());}}}
4、ActivityRSAUtil 工具类
importorg.apachemons.io.IOUtils;importjavax.crypto.Cipher;importjava.io.ByteArrayOutputStream;importjava.security.*;importjava.security.interfaces.RSAPrivateKey;importjava.security.interfaces.RSAPublicKey;importjava.security.spec.PKCS8EncodedKeySpec;importjava.security.spec.X509EncodedKeySpec;importjava.util.Base64;/***@module*@author:qingxu.liu*@date:2023-02-0716:54*@copyright**/publicclassActivityRSAUtil{/***字符集*/publicstaticStringCHARSET="UTF-8";/***生成密钥对*@paramkeyLength密钥长度*@returnKeyPair*/publicstaticKeyPairgetKeyPair(intkeyLength){try{KeyPairGeneratorkeyPairGenerator=KeyPairGenerator.getInstance("RSA");//默认:RSA/None/PKCS1PaddingkeyPairGenerator.initialize(keyLength);returnkeyPairGenerator.generateKeyPair();}catch(NoSuchAlgorithmExceptione){thrownewRuntimeException("生成密钥对时遇到异常"+e.getMessage());}}/***获取公钥*/publicstaticbyte[]getPublicKey(KeyPairkeyPair){RSAPublicKeyrsaPublicKey=(RSAPublicKey)keyPair.getPublic();returnrsaPublicKey.getEncoded();}/***获取私钥*/publicstaticbyte[]getPrivateKey(KeyPairkeyPair){RSAPrivateKeyrsaPrivateKey=(RSAPrivateKey)keyPair.getPrivate();returnrsaPrivateKey.getEncoded();}/***公钥字符串转PublicKey实例*@parampublicKey公钥字符串*@returnPublicKey*@throwsExceptione*/publicstaticPublicKeygetPublicKey(StringpublicKey)throwsException{byte[]publicKeyBytes=Base64.getDecoder().decode(publicKey.getBytes());X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(publicKeyBytes);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePublic(keySpec);}/***私钥字符串转PrivateKey实例*@paramprivateKey私钥字符串*@returnPrivateKey*@throwsExceptione*/publicstaticPrivateKeygetPrivateKey(StringprivateKey)throwsException{byte[]privateKeyBytes=Base64.getDecoder().decode(privateKey.getBytes());PKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(privateKeyBytes);KeyFactorykeyFactory=KeyFactory.getInstance("RSA");returnkeyFactory.generatePrivate(keySpec);}/***获取公钥字符串*@paramkeyPairKeyPair*@return公钥字符串*/publicstaticStringgetPublicKeyString(KeyPairkeyPair){RSAPublicKeypublicKey=(RSAPublicKey)keyPair.getPublic();//得到公钥returnnewString(org.apachemons.codec.binary.Base64.encodeBase64(publicKey.getEncoded()));}/***获取私钥字符串*@paramkeyPairKeyPair*@return私钥字符串*/publicstaticStringgetPrivateKeyString(KeyPairkeyPair){RSAPrivateKeyprivateKey=(RSAPrivateKey)keyPair.getPrivate();//得到私钥returnnewString(org.apachemons.codec.binary.Base64.encodeBase64((privateKey.getEncoded())));}/***公钥加密*@paramdata明文*@parampublicKey公钥*@return密文*/publicstaticStringpublicEncrypt(Stringdata,RSAPublicKeypublicKey){try{Ciphercipher=Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE,publicKey);byte[]bytes=rsaSplitCodec(cipher,Cipher.ENCRYPT_MODE,data.getBytes(CHARSET),publicKey.getModulus().bitLength());returnnewString(org.apachemons.codec.binary.Base64.encodeBase64(bytes));}catch(Exceptione){thrownewRuntimeException("加密字符串["+data+"]时遇到异常"+e.getMessage());}}/***私钥解密*@paramdata密文*@paramprivateKey私钥*@return明文*/publicstaticStringprivateDecrypt(Stringdata,RSAPrivateKeyprivateKey){try{Ciphercipher=Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE,privateKey);returnnewString(rsaSplitCodec(cipher,Cipher.DECRYPT_MODE,Base64.getDecoder().decode(data),privateKey.getModulus().bitLength()),CHARSET);}catch(Exceptione){thrownewRuntimeException("privateKey解密字符串["+data+"]时遇到异常"+e.getMessage());}}/***私钥加密*@paramcontent明文*@paramprivateKey私钥*@return密文*/publicstaticStringencryptByPrivateKey(Stringcontent,RSAPrivateKeyprivateKey){try{Ciphercipher=Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE,privateKey);byte[]bytes=rsaSplitCodec(cipher,Cipher.ENCRYPT_MODE,content.getBytes(CHARSET),privateKey.getModulus().bitLength());returnnewString(org.apachemons.codec.binary.Base64.encodeBase64(bytes));}catch(Exceptione){thrownewRuntimeException("privateKey加密字符串["+content+"]时遇到异常"+e.getMessage());}}/***公钥解密*@paramcontent密文*@parampublicKey私钥*@return明文*/publicstaticStringdecryByPublicKey(Stringcontent,RSAPublicKeypublicKey){try{Ciphercipher=Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE,publicKey);returnnewString(rsaSplitCodec(cipher,Cipher.DECRYPT_MODE,Base64.getDecoder().decode(content),publicKey.getModulus().bitLength()),CHARSET);}catch(Exceptione){thrownewRuntimeException("publicKey解密字符串["+content+"]时遇到异常"+e.getMessage());}}publicstaticRSAPublicKeygetRSAPublicKeyByString(StringpublicKey){try{X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(Base64.getDecoder().decode(publicKey));KeyFactorykeyFactory=KeyFactory.getInstance("RSA");return(RSAPublicKey)keyFactory.generatePublic(keySpec);}catch(Exceptione){thrownewRuntimeException("String转PublicKey出错"+e.getMessage());}}publicstaticRSAPrivateKeygetRSAPrivateKeyByString(StringprivateKey){try{PKCS8EncodedKeySpecpkcs8EncodedKeySpec=newPKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));KeyFactorykeyFactory=KeyFactory.getInstance("RSA");return(RSAPrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec);}catch(Exceptione){thrownewRuntimeException("String转PrivateKey出错"+e.getMessage());}}//rsa切割解码,ENCRYPT_MODE,加密数据,DECRYPT_MODE,解密数据privatestaticbyte[]rsaSplitCodec(Ciphercipher,intopmode,byte[]datas,intkeySize){intmaxBlock=0;//最大块if(opmode==Cipher.DECRYPT_MODE){maxBlock=keySize/8;}else{maxBlock=keySize/8-11;}ByteArrayOutputStreamout=newByteArrayOutputStream();intoffSet=0;byte[]buff;inti=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(Exceptione){thrownewRuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常:"+e.getMessage());}byte[]resultDatas=out.toByteArray();IOUtils.closeQuietly(out);returnresultDatas;}}
5、AES256Util 工具类
importorg.bouncycastle.jce.provider.BouncyCastleProvider;importjavax.crypto.Cipher;importjavax.crypto.spec.SecretKeySpec;importjava.nio.charset.StandardCharsets;importjava.security.Security;importjava.util.Base64;/***@module*@author:qingxu.liu*@date:2023-02-0716:14*@copyright**/publicclassAES256Util{privatestaticfinalStringAES="AES";/***初始向量IV,初始向量IV的长度规定为128位16个字节,初始向量的来源为随机生成.*//***加密解密算法/加密模式/填充方式*/privatestaticfinalStringCIPHER_ALGORITHM="AES/CBC/PKCS7Padding";privatestaticfinalBase64.Encoderbase64Encoder=java.util.Base64.getEncoder();privatestaticfinalBase64.Decoderbase64Decoder=java.util.Base64.getDecoder();//通过在运行环境中设置以下属性启用AES-256支持static{Security.setProperty("crypto.policy","unlimited");}/**解决java不支持AES/CBC/PKCS7Padding模式解密*/static{Security.addProvider(newBouncyCastleProvider());}/***AES加密*/publicstaticStringencode(Stringkey,Stringcontent,StringkeyVI){try{javax.crypto.SecretKeysecretKey=newjavax.crypto.spec.SecretKeySpec(key.getBytes(),AES);javax.crypto.Ciphercipher=javax.crypto.Cipher.getInstance(CIPHER_ALGORITHM);cipher.init(javax.crypto.Cipher.ENCRYPT_MODE,secretKey,newjavax.crypto.spec.IvParameterSpec(keyVI.getBytes()));//获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码byte[]byteEncode=content.getBytes(java.nio.charset.StandardCharsets.UTF_8);//根据密码器的初始化方式加密byte[]byteAES=cipher.doFinal(byteEncode);//将加密后的数据转换为字符串returnbase64Encoder.encodeToString(byteAES);}catch(Exceptione){e.printStackTrace();}returnnull;}/***AES解密*/publicstaticStringdecode(Stringkey,Stringcontent,StringkeyVI){try{javax.crypto.SecretKeysecretKey=newjavax.crypto.spec.SecretKeySpec(key.getBytes(),AES);javax.crypto.Ciphercipher=javax.crypto.Cipher.getInstance(CIPHER_ALGORITHM);cipher.init(javax.crypto.Cipher.DECRYPT_MODE,secretKey,newjavax.crypto.spec.IvParameterSpec(keyVI.getBytes()));//将加密并编码后的内容解码成字节数组byte[]byteContent=base64Decoder.decode(content);//解密byte[]byteDecode=cipher.doFinal(byteContent);returnnewString(byteDecode,java.nio.charset.StandardCharsets.UTF_8);}catch(Exceptione){e.printStackTrace();}returnnull;}/***AES加密ECB模式PKCS7Padding填充方式*@paramstr字符串*@paramkey密钥*@return加密字符串*@throwsException异常信息*/publicstaticStringaes256ECBPkcs7PaddingEncrypt(Stringstr,Stringkey)throwsException{Ciphercipher=Cipher.getInstance("AES/ECB/PKCS7Padding");byte[]keyBytes=key.getBytes(StandardCharsets.UTF_8);cipher.init(Cipher.ENCRYPT_MODE,newSecretKeySpec(keyBytes,AES));byte[]doFinal=cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));returnnewString(Base64.getEncoder().encode(doFinal));}/***AES解密ECB模式PKCS7Padding填充方式*@paramstr字符串*@paramkey密钥*@return解密字符串*@throwsException异常信息*/publicstaticStringaes256ECBPkcs7PaddingDecrypt(Stringstr,Stringkey)throwsException{Ciphercipher=Cipher.getInstance("AES/ECB/PKCS7Padding");byte[]keyBytes=key.getBytes(StandardCharsets.UTF_8);cipher.init(Cipher.DECRYPT_MODE,newSecretKeySpec(keyBytes,AES));byte[]doFinal=cipher.doFinal(Base64.getDecoder().decode(str));returnnewString(doFinal);}}
“SpringBoot如何实现RAS+AES自动接口解密”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注主机评测网网站,小编将为大家输出更多高质量的实用文章!
上一篇:Android无需读写权限通过临时授权读写用户文件怎么实现