SpringBoot 實現 RAS+AES 自動接口解密

一、講個事故

接口安全老生常談了

過年之前做了過一款飛機大戰的H5小游戲,里面無限模式-需要保存用戶的積分,因為使用的Body傳參,參數是可見的。

為了接口安全我,我和前端約定了傳遞參數是:用戶無限模式的積分+“我們約定的一個數字”+用戶id的和,在用Base64加密,請求到服務器我再解密,出用戶無限模式的積分;如下:

{"integral":?"MTExMTM0NzY5NQ==",
}

可是過年的時候,運營突然找我說無限模式積分排行榜分數不對:

圖片

圖片

這就很詭異了,第二名才一萬多分,第一名就40多萬分!!!!

一開始我以為是我解密有問題,反復看了好幾變,可就兩三行代碼不可能有問題的!!!

沒辦法我去翻了好久的日志,才發現這個用戶把我接口參數給改了。。。。

他把Base64接口參數改了

圖片

圖片

事已至此,我也不能怪用戶,誰讓我把人家想得太簡單,接口安全也沒到位

所以年后上班第一件是就是把接口加密的工作搞起來

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

二、RSA和AES基礎知識

1、非對稱加密和對稱加密

非對稱加密

非對稱加密算法是一種密鑰的保密方法。非對稱加密算法需要兩個密鑰:公開密鑰(publickey:簡稱公鑰)和私有密鑰(privatekey:簡稱私鑰)。

公鑰與私鑰是一對,如果用公鑰對數據進行加密,只有用對應的私鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。

對稱加密

加密秘鑰和解密秘鑰是一樣,當你的密鑰被別人知道后,就沒有秘密可言了

AES 是對稱加密算法,優點:加密速度快;缺點:如果秘鑰丟失,就容易解密密文,安全性相對比較差;關注公眾號:碼猿技術專欄,回復關鍵詞:1111 獲取阿里內部Java性能調優手冊!

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工作模式下最后一塊要在加密前進行填充,其它不用選擇填充模式;關注公眾號:碼猿技術專欄,回復關鍵詞:1111 獲取阿里內部Java性能調優手冊!

  • 填充模式: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%可用~~~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14091.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14091.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14091.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML靜態網頁成品作業(HTML+CSS)——魅族商城首頁網頁(1個頁面)

&#x1f389;不定期分享源碼&#xff0c;關注不丟失哦 文章目錄 一、作品介紹二、作品演示三、代碼目錄四、網站代碼HTML部分代碼 五、源碼獲取 一、作品介紹 &#x1f3f7;?本套采用HTMLCSS&#xff0c;未使用Javacsript代碼&#xff0c;共有1個頁面。 二、作品演示 三、代…

基于Python+OpenCV卷積神經網絡的字符識別

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 一、項目背景與意義 字符識別是計算機視覺和模式識別領域的一個重要應用&#xff0c;它在文檔數字化、車牌識別、驗…

gpt-4o考場安排

說明 &#xff1a;經過多次交互&#xff0c;前后花了幾個小時&#xff0c;總算完成了基本功能。如果做到按不同層次分配考場&#xff0c;一鍵出打印結果就完美了。如果不想看中間“艱苦”的過程&#xff0c;請直接跳到“最后結果”及“食用方法”。中間過程還省略了一部分交互&…

Android-多個tv_item_[i] 點擊事件簡寫

private TextView[] tvConstellations new TextView[12];//獲取當前id元素并在其點擊的時候修改其顏色 for (int i 0; i < 12; i) {int resId getResources().getIdentifier("tv_constellation_" (i1), "id", getPackageName());tvConstellations[i…

神經網絡與深度學習 課程復習總結

神經網絡的發展歷史 第一代神經網絡&#xff08;1958~1969&#xff09; MCP模型&#xff08;1943年&#xff09;&#xff1a;麥卡洛克和皮茨提出了第一個人工神經元模型&#xff0c;簡化為輸入信號線性加權、求和、非線性激活&#xff08;閾值法&#xff09;。感知器&#xf…

鴻蒙開發 組件之間的傳值

1.Prop&#xff1a;父組件傳遞給子組件&#xff0c;單向傳遞&#xff0c;子組件改變值&#xff0c;父組件UI不更新。 引入子組件 并賦值&#xff0c;子組件用Prop 接收 import headerView from ../../common/bean/BaseNavHeaderView headerView({titlestr:添加地址,isback…

go slice 擴容

擴容 slice 會遷移到新的內存位置&#xff0c;新底層數組的長度也會增加&#xff0c;這樣就可以放置新增的元素。同時&#xff0c;為了應對未來可能再次發生的 append 操作&#xff0c;新的底層數組的長度&#xff0c;也就是新 slice 的容量是留了一定的 buffer 的。否則&…

【C++】STL快速入門基礎

文章目錄 STL&#xff08;Standard Template Library&#xff09;1、一般介紹2、STL的六大組件2.1、STL容器2.2、STL迭代器2.3、相關容器的函數vectorpairstringqueuepriority_queuestackdequeset, map, multiset, multimapunordered_set, unordered_map, unordered_multiset, …

LabVIEW2022安裝教程指南【附安裝包】

文章目錄 前言一、安裝指南1、軟件包獲取 二、安裝步驟總結 前言 LabVIEW是一種程序開發環境&#xff0c;提供一種圖形化編程方法&#xff0c;可可視化應用程序的各個方面&#xff0c;包括硬件配置、測量數據和調試&#xff0c;同時可以通過FPGA數學和分析選板中的NI浮點庫鏈接…

有趣的css - 兩個圓形加載效果

大家好&#xff0c;我是 Just&#xff0c;這里是「設計師工作日常」&#xff0c;今天分享的是一款小清新的加載動畫&#xff0c;適用于 app 列表加載&#xff0c;頁面加載或者彈層內容延遲加載等場景。 最新文章通過公眾號「設計師工作日常」發布。 目錄 整體效果核心代碼html…

AWS安全性身份和合規性之Amazon Macie

Amazon Macie是一項數據安全和數據隱私服務&#xff0c;它利用機器學習&#xff08;ML&#xff09;和模式匹配來發現和保護敏感數據。可幫助客戶發現、分類和保護其敏感數據&#xff0c;以及監控其數據存儲庫的安全性。 應用場景&#xff1a; 敏感數據發現 一家金融服務公司…

20年交易老兵悟出的寶貴經驗,做到這10點或許你也能躺著賺錢

交易要靠親身體驗來真正獲得發展&#xff0c;在正確引導下&#xff0c;我們就不會把時間和精力浪費在彎路上。交易之技易學&#xff0c;實難在心態與思考。接下來&#xff0c;我將與您分享一位交易了20年的老兵所積累的10條珍貴經驗。 Nial Fuller,一個交易了接近20年的市場“老…

Git遠程控制

文章目錄 1. 創建倉庫1.1 Readme1.2 Issue1.3 Pull request 2. 遠程倉庫克隆3. 推送遠程倉庫4. 拉取遠程倉庫5. 配置Git.gitignore配置別名 使用GitHub可以&#xff0c;采用Gitee也行 1. 創建倉庫 1.1 Readme Readme文件相當于這個倉庫的說明書&#xff0c;gitee會初始化2兩份…

go mod模式下,import gitlab中的項目

背景 為了go項目能夠盡可能復用代碼&#xff0c;把一些公用的工具類&#xff0c;公用的方法等放到共用包里統一管理。把共用包放到gitlab的私有倉庫中。 遇到的問題 通過https方式&#xff0c;執行go get報了錯誤。 通過ssh方式&#xff0c;執行go get報了錯誤。 修改配置&am…

介紹一個免費的在線pdf轉word網站

Smallpdf.com - A Free Solution to all your PDF Problems 轉換效果不錯&#xff0c;比那些收費的軟件強多了&#xff0c;主要是免費的&#xff01;

面試八股之MySQL篇2——索引篇

&#x1f308;hello&#xff0c;你好鴨&#xff0c;我是Ethan&#xff0c;一名不斷學習的碼農&#xff0c;很高興你能來閱讀。 ??目前博客主要更新Java系列、項目案例、計算機必學四件套等。 &#x1f3c3;人生之義&#xff0c;在于追求&#xff0c;不在成敗&#xff0c;勤通…

Springboot階段項目---《書城項目》

一 項目介紹 本項目采用集成開發平臺IntelliJ IDEA開發了在線作業成績統計系統的設計與實現&#xff0c;實現了圖書商城系統的綜合功能和圖形界面的顯示&#xff0c;可以根據每個用戶登錄系統后&#xff0c;動態展示書城首頁圖書&#xff0c;實現了分類還有分頁查詢&#xff0c…

進程、線程——面經(一)

1、什么是進程&#xff08;Process&#xff09;&#xff0c;線程&#xff08;Thread&#xff09;&#xff0c;有什么區別&#xff1f; 進程&#xff08;Process&#xff09;&#xff1a; 定義&#xff1a; 進程是一個獨立的執行環境&#xff0c;它擁有獨立的內存空間&#xf…

Linux應用入門(二)

1. 輸入系統應用編程 1.1 輸入系統介紹 常見的輸入設備有鍵盤、鼠標、遙控桿、書寫板、觸摸屏等。用戶經過這些輸入設備與Linux系統進行數據交換。這些設備種類繁多&#xff0c;如何去統一它們的接口&#xff0c;Linux為了統一管理這些輸入設備實現了一套能兼容所有輸入設備的…

怎么壓縮pdf pdf在線壓縮 pdf文件壓縮大小

pdf文件無論在何種設備上打開&#xff0c;PDF文件都能保持其原始的布局和格式&#xff0c;這對于文檔共享和打印非常重要。PDF不僅支持文本&#xff0c;還能嵌入圖像、視頻、音頻以及動態鏈接等元素。PDF文件支持加密和密碼保護&#xff0c;可以限制訪問、編輯、復制或打印文檔…