中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2023-03-21 15:21:50 來源:億速云 閱讀:140 作者:iii 欄目:開發技術

本篇內容介紹了“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_OAEPENCRYPTION_PKCS1ENCRYPTION_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的格式進行存儲的

        /**
         * 生成密鑰對
         * @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基礎知識

    AES 簡介 AES加密解密算法是一種可逆的對稱加密算法,這類算法在加密和AES解密時使用相同的密鑰,或是使用兩個可以簡單地相互推算的密鑰,一般用于服務端對服務端之間對數據進行加密解密。它是一種為了替代原先DES、3DES而建立的高級加密標準(Advanced Encryption Standard)。作為可逆且對稱的塊加密,AES加密算法的速度比公鑰加密等加密算法快很多,在很多場合都需要AES對稱加密,但是要求加密端和解密端雙方都使用相同的密鑰是AES算法的主要缺點之一。

    AES加密解密

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

    AES的算法模式一般為 AES/CBC/PKCS5PaddingAES/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、加密策略

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

    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參數,調用接口

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

    后端:

    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、如果帶有注解,通過ProceedingJoinPoint類getArgs()方法獲取請求的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 {
                //解密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;
    
    /**
     * @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/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;
    
    /**
     * @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);
        }
    }

    “SpringBoot如何實現RAS+AES自動接口解密”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    沙湾县| 民勤县| 奉节县| 柯坪县| 米林县| 宜章县| 同江市| 滦南县| 贵州省| 永春县| 定日县| 邯郸市| 宝山区| 诏安县| 合山市| 山阳县| 海原县| 牙克石市| 溧阳市| 开封县| 济南市| 罗定市| 北流市| 肇州县| 红桥区| 营山县| 隆回县| 定兴县| 灌阳县| 玉山县| 高邑县| 阳新县| 平泉县| 大田县| 宽城| 高淳县| 射洪县| 成安县| 古田县| 南川市| 措勤县|