Java 加密擴展(JCE)框架 之 Cipher 加密與解密

目錄

常用加密算法對比

Cipher 密碼加密概述

Cipher 常量與常用方法

Hello World 快速入門

RSA 非對稱加密詳解


常用加密算法對比

常用加密算法對比
加密算法 描述
SHA  安全散列算法。單向加密、數據不可逆(即不可恢復)。
BASE64

1、BASE64 有自己的編碼表,可編碼,也可解碼;解碼後能得到源內容。

2、BASE64 編碼的長度是不固定的,隨着源字符串的增加而增加。

3、在某些不方便使用中文的地方,可以通過 BASE64 編碼成可見字符,需要中文時再解碼回來即可,比如 url 地址,或者 Cookie.

MD5

信息摘要是安全的單向哈希函數,對大小的信息輸出固定長度的哈希值,單向加密、數據不可逆。

摘要好比指紋,每個人都是唯一的,相同的源數據,摘要也一樣,不同的數據,摘要則不一樣。摘要只是源數據的局部,所以想要解碼回去恢復整個源數據是不行的,因爲是不完整的。

DES

Data Encrytion Standard(數據加密標準) , 特點:1. 對稱加密  2. 同一個 SECRET_KEY(密鑰)

AES

Advanced Encrytion Standard(高級加密標準),特點:1. 對稱加密  2. 一個 SECRET_KEY(密鑰)擴展成多個子 SK,輪加密

RSA

1. 非對稱加密,即:PK(PUBLIC_KEY 公鑰) 與 SK( SECRET_KEY 密鑰) 不是同一個

2. PK 加密時,必須用 SK 解密、反之  SK 加密時,必須用 PK 解密,

3. PK 決定 SK,但是 PK 很難算出 SK(數學原理:兩個大質數相乘,積很難因式分解)

4. 速度慢,適合對少量數據加密

Cipher 密碼加密概述

1、javax.crypto.Cipher 類提供加密和解密的功能,它構成了 Java Cryptography Extension (JCE) —— Java 加密擴展框架的核心。這些都是 Java JDK 原生的 API,不是第三方的。

2、Cipher 的 getInstance(String transformation) 方法可以獲取實例,參數 transformation 表示轉換名稱,包括:加密算法/反饋模式/填充方案加密算法是必選項,反饋模式與填充方案可以不寫使用默認值。如:

Cipher cipher = Cipher.getInstance("AES");
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");

3、Cipher  支持 4 種加密算法:AES、DES、DESede、RSA。

Cipher.getInstance 實例化時轉換名稱參數可選值如下
AES/CBC/NoPadding
AES/CBC/PKCS5Padding
AES/ECB/NoPadding
AES/ECB/PKCS5Padding

1、加密算法有 4 種:AES、DES、DESede、RSA

2、反饋模式有兩種:CBC(有向量模式)、ECB(無向量模式)

3、填充方案有 5 種:NoPadding 、PKCS5Padding、PKCS1Padding 、OAEPWithSHA-1AndMGF1Padding、OAEPWithSHA-256AndMGF1Padding

 

4、NoPadding 填充方案: 加密內容不足8位時用0補足8位, Cipher類不提供補位功能,需自己實現代碼給加密內容添加0, 如{65,65,65,0,0,0,0,0}

5、PKCS5Padding 填充方案: 加密內容不足8位用餘位數補足8位, 如{65,65,65,5,5,5,5,5}或{97,97,97,97,97,97,2,2}.

DES/CBC/NoPadding
DES/CBC/PKCS5Padding
DES/ECB/NoPadding
DES/ECB/PKCS5Padding

DESede/CBC/NoPadding
DESede/CBC/PKCS5Padding
DESede/ECB/NoPadding
DESede/ECB/PKCS5Padding

RSA/ECB/PKCS1Padding
RSA/ECB/OAEPWithSHA-1AndMGF1Padding
RSA/ECB/OAEPWithSHA-256AndMGF1Padding

Cipher 常量與常用方法

javax.crypto.Cipher 常量字段
字段類型 字段名稱 字段值 描述
static int ENCRYPT_MODE 1 常數用於將密碼初始化爲加密模式。
static int DECRYPT_MODE 2 常數用於將密碼初始化爲解密模式。
static int WRAP_MODE 3 常數用於將密碼初始化爲密鑰包裝模式
static int UNWRAP_MODE 4 常數用於將密碼初始化爲密鑰解包模式。
static int PUBLIC_KEY 1 用於指示待解除密鑰的常數是“公鑰”。
static int PRIVATE_KEY 2 用於指示要解除密鑰的常數是“私鑰”。
static int SECRET_KEY 3 用於指示要解開的密鑰的常數是“密鑰”。
javax.crypto.Cipher 常用 API
方法 描述
Cipher getInstance(String transformation)
Cipher getInstance(String transformation,Provider provider)
Cipher getInstance(String transformation,String provider)

實例化 Cipher 對象。

1、transformation 轉換名稱,格式:加密算法/反饋模式/填充方案

2、provider 供應商

init(int opmode,Key key)
init(int opmode,Key key,AlgorithmParameters params)
init(int opmode,Key key,AlgorithmParameterSpec params)
init(int opmode,Key key,AlgorithmParameters params,SecureRandom random)
init(int opmode,Key key,AlgorithmParameterSpec params,SecureRandom random)
init(int var1, Key var2, SecureRandom var3)
init(int opmode,Certificate certificate)
init(int opmode,Certificate certificate,SecureRandom random)

初始化 Cipher

1、opmode 對應上面常量:解密、加密、解包、模式、包裝。

2、key :密匙,可以使用 SecretKeySpec、KeyGenerator 和 KeyPairGenerator 創建。其中
SecretKeySpec 和 KeyGenerator 支持 AES,DES,DESede 三種加密算法創建密匙,KeyPairGenerator支持 RSA 加密算法創建密匙。
3、params :使用 CBC 模式時必須傳入該參數.

update(byte[] input)
update(byte[] input,int off,int len)
輸入給定的字節數組完成更新

update(byte[] input,int off,int len,byte[] output)
update(byte[] input,int off,int len,byte[] output,int outputoffset)

將更新結果輸出到參數中
update(ByteBuffer input,ByteBuffer output) 使用緩衝的方式更新

byte[] wrap(Key key)

包裝密鑰
Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)

解包裝

wrappedKeyType 對應常量 PRIVATE_KEY、PUBLIC_KEY、SECRET_KEY

 byte[] getIV() 返回緩衝區中的初始化向量

int getMaxAllowedKeyLength(String transformation)

返回指定轉換的最大密鑰長度

int getBlockSize()

返回塊大小(以字節爲單位),如果底層算法不是塊密碼,則爲 0.

分組加密中,每一組都有固定的長度,稱爲塊.

int getOutputSize(int inputLen) 獲取輸出緩衝區字節長度
 String getAlgorithm() 返回此 Cipher 對象的算法名稱。
byte[] doFinal() 完成多部分加密或解密操作,這取決於該密碼如何初始化。
返回的 byte 數組,如果直接使用 new String(b) 封裝成字符串,則會出現亂碼。

Hello World 快速入門

源碼中都有詳細說明,這裏重點強調幾點;

1、使用 CBC 有向量模式時,cipher.init 必須傳入 {@link AlgorithmParameterSpec}-算法參數規範。如果使用的是 ECB-無向量模式,那麼 cipher.init 則加解密都不需要傳  {@link AlgorithmParameterSpec} 參數.

2、生成密鑰 SecretKey 與 算法參數規範 AlgorithmParameterSpec 的 key ,AES加密算法時必須是 16 個字節,DES 時必須是 8 字節.

3、加/解密都是調用 Cipher.doFinal(byte[] content) 方法,具體取決於 cipher.init 時 使用的是 Cipher.ENCRYPT_MODE 加密模式,還是 Cipher.DECRYPT_MODE 解密模式.

4、下面演示的是 AES 加密算法,DES 加密算法使用也是同理,區別在於參數:secret_key、vector_key : AES 時必須是 16 個字節,DES 時必須是 8 字節.

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.AlgorithmParameterSpec;
/**
 * Cipher 密碼工具類
 *
 * @author wangmaoxiong
 * @version 1.0
 * @date 2020/5/17 13:09
 */
public class CipherUtils {
    /**
     * AES/CBC/PKCS5Padding 加密
     *
     * @param content    :待加密的內容.
     * @param secret_key :用於生成密鑰的 key,自定義即可,加密與解密必須使用同一個,如果不一致,則拋出異常
     * @param vector_key 用於生成算法參數規範的 key,自定義即可,加密與解密必須使用同一個,如果不一致,解密的內容可能會造成與源內容不一致.
     *                   <p>
     *                   1、secret_key、vector_key: AES 時必須是 16 個字節,DES 時必須是 8 字節.
     *                   2、secret_key、vector_key 值不建議使用中文,如果是中文,注意一個漢字是3個字節。
     *                   </p>
     * @return 返回 Cipher 加密後的數據,對加密後的字節數組用 Base64 進行編碼轉成了可視字符串,如 7giH2bqIMH3kDMIg8gq0nY
     * @throws Exception
     */
    public static String encrypt(String content, String secret_key, String vector_key) throws Exception {
        //實例化 Cipher 對象。使用:AES-高級加密標準算法、CBC-有向量模式、PKCS5Padding-填充方案:(加密內容不足8位時用餘位數補足8位)
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //使用 SecretKeySpec(byte[] key, String algorithm) 創建密鑰. 算法要與 Cipher.getInstance 保持一致.
        SecretKey secretKey = new SecretKeySpec(secret_key.getBytes(), "AES");
        /**
         * init(int opMode,Key key,AlgorithmParameterSpec params):初始化 Cipher,
         * 1、Cipher.ENCRYPT_MODE 表示加密模式
         * 2、key 表示加密密鑰
         * 3、params 表示算法參數規範,使用 CBC 有向量模式時,必須傳入,如果是 ECB-無向量模式,那麼可以不傳
         * 4、所有參數規範都必須實現 {@link AlgorithmParameterSpec} 接口.
         */
        IvParameterSpec parameterSpec = new IvParameterSpec(vector_key.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
        /**
         * byte[] doFinal(byte[] content):對 content 完成加密操作,如果 cipher.init 初始化時使用的解密模式,則此時是解密操作.
         * 返回的是加密後的字節數組,如果直接 new String(byte[] bytes) 是會亂碼的,可以藉助 BASE64 轉爲可視字符串,或者轉成 16 進制字符
         */
        byte[] encrypted = cipher.doFinal(content.getBytes());
        //BASE64Encoder.encode:BASE64 對字節數組內容進行編碼,轉爲可視字符串,這樣方便存儲和轉換.
        String base64Encode = new BASE64Encoder().encode(encrypted);
        return base64Encode;
    }

    /**
     * AES/CBC/PKCS5Padding 解密
     *
     * @param base64Encode :待解密的內容,因爲加密時使用了 Base64 進行了編碼,所以這裏傳入的也是 Base64 編碼後的可視化字符串
     * @param secret_key   :用於生成密鑰的 key,自定義即可,加密與解密必須使用同一個,如果不一致,則拋出異常
     * @param vector_key   用於生成算法參數規範的 key,自定義即可,加密與解密必須使用同一個,如果不一致,解密的內容可能會造成與源內容不一致.
     *                     <p>
     *                     1、secret_key、vector_key:AES 時必須是 16 個字節,DES 時必須是 8 字節.
     *                     2、secret_key、vector_key 值不建議使用中文,如果是中文,注意一個漢字是3個字節。
     *                     </p>
     * @return
     * @throws Exception
     */
    public static String decrypt(String base64Encode, String secret_key, String vector_key) throws Exception {
        //實例化 Cipher 對象。加密算法/反饋模式/填充方案,解密與加密需要保持一致.
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //創建密鑰。算法也要與實例化 Cipher 一致.
        SecretKey secretKey = new SecretKeySpec(secret_key.getBytes(), "AES");
        //有向量模式(CBC)需要傳入 AlgorithmParameterSpec 算法參數規範參數.
        IvParameterSpec parameterSpec = new IvParameterSpec(vector_key.getBytes());
        //初始化 cipher。使用解密模式.
        cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
        //將 Base64 編碼的內容解碼成字節數組(因爲加密的時候,對密文使用了 Base64編碼,所以這裏需要先解碼)
        byte[] content = new BASE64Decoder().decodeBuffer(base64Encode);
        //執行解密操作。返回解密後的字節數組,此時可以使用 String(byte bytes[]) 轉成源字符串.
        byte[] decrypted = cipher.doFinal(content);
        return new String(decrypted);
    }

    public static void main(String[] args) throws Exception {
        String content = "紅網時刻5月17日訊(記者 汪衡 通訊員 顏雨彬 汪丹)“場面壯觀,氣勢磅礴,簡直就是一部抗洪搶險水域救援大片!”抗洪搶險水域救援實戰訓練剛一結束,現場圍觀的羣衆紛紛發出了感嘆。5月16日,湖南省消防救援總隊在長沙市望城區千龍湖舉辦舟艇操作員培訓,長沙、株洲、湘潭等40餘名消防指戰員參加,開展了一場抗洪搶險水域救援實戰訓練。" +
                "夏以來,隨着湖南防汛救援任務的日趨繁重。爲提高全省消防救援隊伍水域救援能力,充分發揮應急救援“主力軍”和“國家隊”的作用,湖南省消防救援總隊組織了這次爲期35天的培訓。依託長沙、岳陽、常德3支省級水域救援隊設立了3個培訓點,全省653名學員分17期參加培訓。此次培訓採用課堂授課、實地講解、實操訓練等多種形式,培訓內容貼近實戰,主要包括OSO駕駛、快速出入庫、翻船自救、故障排除、200米游泳、受限控艇等項目。省消防救援總隊相關負責人介紹,爲了加快適應“全災種、大應急”的職能定位,全面做好“防大汛、搶大險、救大災”的準備工作,湖南消防救援隊伍未雨綢繆,組建了1支省級抗洪搶險救援隊、13支市級抗洪搶險救援隊,有水域救援任務的消防救援站均成立了1支站級抗洪搶險救援隊,總人數2300餘人。此外,省消防救援總隊還把重型工程機械救援大隊、航空救援大隊作爲搶險救援機動力量納入調度體系,充分發揮消防救援隊伍裝備技術優勢,最大限度地挽救生命,減少災害損失。";
        //生成密鑰的 key 與 生成算法參數規範的 key 加解密必須一致,它就像是一把鑰匙或者口令,不能忘記.
        //AES 加密算法時必須是 16 個字節,DES 時必須是 8 字節.
        String slatKey = "wYDJty8o8HE6YjJS";
        String vectorKey = "SC35fdGrQozSMT5a";
        String encrypt = encrypt(content, slatKey, vectorKey);
        System.out.println("源內容:\n" + content);
        System.out.println("加密後:\n" + encrypt);
        System.out.println("解密後:\n" + decrypt(encrypt, slatKey, vectorKey));
    }
}

在線源碼:src/main/java/com/wmx/thymeleafapp/utils/CipherUtils.java

RSA 非對稱加密詳解

1. 非對稱加密,需要使用 PK(PUBLIC_KEY 公鑰) 與 SK( SECRET_KEY 密鑰),PK 加密時,必須用 SK 解密、反之  SK 加密時,必須用 PK 解密。

2. 安全性最高,但是速度慢,適合對少量數據加密。

3、源碼中有詳細解釋:

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
 * RSA 非對稱加密工具類。
 * 1、公鑰(PUBLIC_KEY)、私鑰:PRIVATE_KEY 必須分開使用,比如公鑰加密時,必須是私鑰解密,反之私鑰加密時,必須是公鑰解密
 *
 * @author wangmaoxiong
 * @version 1.0
 * @date 2020/5/18 15:41
 */
public class CipherRsaUtils {

    /**
     * CIPHER_TRANSFORMS : cipher 實例化時的 加密算法/反饋模式/填充方案。ECB 表示無向量模式
     * ALGORITHM: 創建密鑰時使用的算法
     * KEY_PAIR_LENGTH: 祕鑰對長度。數值越大,能加密的內容就越大。
     * <p>
     * 如 KEY_PAIR_LENGTH 爲 1024 時加密數據的長度不能超過 117 字節
     * 如 KEY_PAIR_LENGTH 爲 2048 時加密數據的長度不能超過 245 字節
     * 依次類推
     * </p>
     */
    private static final String CIPHER_TRANSFORMS = "RSA/ECB/PKCS1Padding";
    private static final int KEY_PAIR_LENGTH = 1024;

    /**
     * 生成 RSA 密鑰對:公鑰(PUBLIC_KEY)、私鑰:PRIVATE_KEY
     *
     * @param secureRandomSeed :SecureRandom 隨機數生成器的種子,只要種子相同,則生成的公鑰、私鑰就是同一對.
     *                         <p>randomSeed 長度可以自定義,加/解密必須是同一個.</p>
     * @return
     */
    public static KeyPair generateRsaKeyPair(String secureRandomSeed) {
        //KeyPair 是密鑰對(公鑰和私鑰)的簡單持有者。加密、解密都需要使用.
        KeyPair keyPair = null;
        try {
            //獲取生成 RSA 加密算法的公鑰/私鑰對 KeyPairGenerator 對象
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //獲取實現指定算法(SHA1PRNG)的隨機數生成器(RNG)對象.
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            //重新設定此隨機對象的種子.
            secureRandom.setSeed(secureRandomSeed.getBytes());
            /**
             * initialize(int keySize, SecureRandom random):使用給定的隨機源(random)初始化特定密鑰大小的密鑰對生成器。
             * keySize: 健的大小值,這是一個特定於算法的度量。值越大,能加密的內容就越多,否則會拋異常:javax.crypto.IllegalBlockSizeException: Data must not be longer than xxx bytes
             * 如 keySize 爲 2048 時加密數據的長度不能超過 245 字節。
             */
            keyPairGenerator.initialize(KEY_PAIR_LENGTH, secureRandom);
            //genKeyPair(): 生成密鑰對
            keyPair = keyPairGenerator.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return keyPair;
    }

    /**
     * 使用私鑰(PrivateKey)對數據進行加密 或 解密
     *
     * @param content          :待加/解密的數據。如果待解密的數據,則是 Base64 編碼後的可視字符串.
     * @param model            :1 表示加密模式(Cipher.ENCRYPT_MODE),2 表示解密模式(Cipher.DECRYPT_MODE)
     * @param secureRandomSeed :SecureRandom 隨機數生成器的種子,只要種子相同,則生成的公鑰、私鑰就是同一對.
     *                         加解密必須是同一個.
     * @return :返回加/解密後的數據,如果是加密,則將字節數組使用 Base64 轉爲可視字符串.
     */
    public static String cipherByPrivateKey(String content, int model, String secureRandomSeed) {
        String result = "";
        try {
            //獲取 RSA 密鑰對
            KeyPair keyPair = generateRsaKeyPair(secureRandomSeed);
            /**getPrivate():獲取密鑰對的私鑰。
             * getEncoded(): 返回已編碼的密鑰,如果密鑰不支持編碼,則返回 null*/
            byte[] privateEncoded = keyPair.getPrivate().getEncoded();
            /**PKCS8EncodedKeySpec(byte[] encodedKey): 使用給定的編碼密鑰創建新的 PKCS8EncodedKeySpec
             * PKCS8EncodedKeySpec 表示 ASN.1 號私鑰的編碼*/
            EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateEncoded);
            //創建 KeyFactory 對象,用於轉換指定算法(RSA)的公鑰/私鑰。
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            //從提供的密鑰規範生成私鑰對象
            PrivateKey keyPrivate = keyFactory.generatePrivate(encodedKeySpec);
            //實例化 Cipher 對象。
            result = encryptionDecryption(content, model, keyPrivate);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 使用公鑰(PublicKey)對數據進行加密 或 解解
     *
     * @param content          :待加解密的數據
     * @param model            :1 表示加密模式(Cipher.ENCRYPT_MODE),2 表示解密模式(Cipher.DECRYPT_MODE)
     * @param secureRandomSeed :SecureRandom 隨機數生成器的種子,只要種子相同,則生成的公鑰、私鑰就是同一對.
     *                         加解密必須是同一個.
     * @return :返回加密 或者 解密後的數據
     */
    public static String cipherByPublicKey(String content, int model, String secureRandomSeed) {
        String result = "";
        try {
            // 得到公鑰
            KeyPair keyPair = generateRsaKeyPair(secureRandomSeed);
            EncodedKeySpec keySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey keyPublic = keyFactory.generatePublic(keySpec);
            // 數據加/解密
            result = encryptionDecryption(content, model, keyPublic);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 加密或解密
     *
     * @param content :待加解密的數據
     * @param model   :1 表示加密模式(Cipher.ENCRYPT_MODE),2 表示解密模式(Cipher.DECRYPT_MODE)
     * @param key     :公鑰(PUBLIC_KEY)或 私鑰(PRIVATE_KEY)的 key
     * @return
     */
    private static String encryptionDecryption(String content, int model, Key key) {
        String result = "";
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMS);
            //init(int opMode, Key key):初始化 Cipher.
            cipher.init(model, key);
            //如果是解密模式,則需要使用 Base64 進行解碼.
            byte[] contentBytes = content.getBytes();
            if (model == Cipher.DECRYPT_MODE) {
                contentBytes = new BASE64Decoder().decodeBuffer(content);
            }
            /**執行加密 或 解密操作。如果 contentBytes 內容過長,則 doFinal 可能會拋出異常.
             *javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes :數據不能超過xxx字節
             * 此時需要調大 KEY_PAIR_LENGTH 的值*/
            byte[] finalBytes = cipher.doFinal(contentBytes);
            //如果是加密,則將加密後的字節數組使用 Base64 轉成可視化的字符串。否則是解密時,直接 new String 字符串.
            if (model == Cipher.ENCRYPT_MODE) {
                result = new BASE64Encoder().encode(finalBytes);
            } else if (model == Cipher.DECRYPT_MODE) {
                result = new String(finalBytes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static void main(String[] args) {
        String content = "紅網時刻5月17日訊(記者 汪衡 通訊員 顏雨彬 汪丹)“場面壯觀,氣勢磅礴.";
        String secureRandomSeed = "TPCz0lDvTHybGMsHTJi3mJ7Pt48llJmRHb";

        System.out.println("被加密數據字節大小:" + content.getBytes().length + " 字節," + content.length() + " 個字符");
        System.out.println("源內容:\n" + content);
        try {
            //公鑰、私鑰必須分開使用,公鑰解密時,必須是私鑰解密,反之亦然.
            String encrypted = CipherRsaUtils.cipherByPublicKey(content, 1, secureRandomSeed);
            String decrypted = CipherRsaUtils.cipherByPrivateKey(encrypted, 2, secureRandomSeed);
            System.out.println("加密後:\n" + encrypted);
            System.out.println("解密後:\n" + decrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

在線源碼:/src/main/java/com/wmx/thymeleafapp/utils/CipherRsaUtils.java

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章