1. 程式人生 > 實用技巧 >JAVA實現AES加密、解密

JAVA實現AES加密、解密

一、什麼是AES?

高階加密標準(英語:Advanced Encryption Standard,縮寫:AES),是一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣為全世界所使用。

那麼為什麼原來的DES會被取代呢,,原因就在於其使用56位金鑰,比較容易被破解。而AES可以使用128、192、和256位金鑰,並且用128位分組加密和解密資料,相對來說安全很多。完善的加密演算法在理論上是無法破解的,除非使用窮盡法。使用窮盡法破解金鑰長度在128位以上的加密資料是不現實的,僅存在理論上的可能性。統計顯示,即使使用目前世界上運算速度最快的計算機,窮盡128位金鑰也要花上幾十億年的時間,更不用說去破解採用256位金鑰長度的AES演算法了。

目前世界上還有組織在研究如何攻破AES這堵堅厚的牆,但是因為破解時間太長,AES得到保障,但是所用的時間不斷縮小。隨著計算機計算速度的增快,新演算法的出現,AES遭到的攻擊只會越來越猛烈,不會停止的。

AES現在廣泛用於金融財務、線上交易、無線通訊、數字儲存等領域,經受了最嚴格的考驗,但說不定哪天就會步DES的後塵。

二、AES加密方式簡析

* AES加密是對稱加密 128 192 256 分別表示金鑰的長度

* AES的加密方式會將明文拆分成不同的塊進行加密,例如一個256 位的資料用128的金鑰加密,則分成

明文1(128位) 明文2(128位)

加密

密文1(128位) 密文2(128位)

填充:

  如果明文不是128位(16位元組)的則需要填充,即在明文某個地方補充到16個位元組整數倍的長度,加解密時需要採用同樣的填充方式,否則無法解密成功,以下是幾種填充方式

** NoPadding
不進行填充,但是這裡要求明文必須要是16個位元組的整數倍,這個可以使用者本身自己去實現填充,除了該種模式以外的其他填充模式,如果已經是16個位元組的資料的話,會再填充一個16位元組的資料

** PKCS5Padding(預設)
在明文的末尾進行填充,填充的資料是當前和16個位元組相差的數量,例如:
未填充明文
1,2,3,4,5,6,7,8,9,10,11

填充明文(缺少五個滿足16個位元組)

1,2,3,4,5,6,7,8,9,10,11,5,5,5,5,5

 由於使用PKCS7Padding/PKCS5Padding填充時,最後一個位元組肯定為填充資料的長度,所以在解密後可以準確刪除填充的資料

** ISO10126Padding
在明文的末尾進行填充,當前和16個位元組相差的數量填寫在最後,其餘位元組填充隨機數,例如:
未填充明文
1,2,3,4,5,6,7,8,9,10,11

填充明文(缺少五個滿足16個位元組)
1,2,3,4,5,6,7,8,9,10,11,c,b,4,1,5

模式

  模式是需要制定AES對明文進行加密時使用的模式(這裡並不涉及具體的加密方法,只是加密步驟上的不同模式,在加解密時同樣需要相同的模式,否則無法成功),一共提供了五種模式,模式的基本原理是近似的,但是細節上會有一些變化,如下:

** ECB模式(預設)電碼本模式 Electronic Codebook Book

這個模式是預設的,就只是根據金鑰的位數,將資料分成不同的塊進行加密,加密完成後,再將加密後的資料拼接起來,過程如下:

明文(64位元組) 金鑰(16位元組)

明文1(16位元組) 明文2(16位元組) 明文3(16位元組) 明文4(16位元組)

密文1(16位元組) 密文2(16位元組) 密文3(16位元組) 密文4(16位元組)

密文(64位元組)

優點:簡單、速度快、可並行
缺點:如果明文塊相同,則生成的密文塊也相同,這樣會導致安全性降低


** CBC模式 密碼分組連結模式 Cipher Block Chaining

為了解決ECB模式的密文塊相同的缺點,CBC的模式引入了一個初始向量概念,該向量必須是一個與金鑰長度相等的資料,在第一次加密前,會使用初始化向量與第一塊資料做異或運算,生成的新資料再進行加密,加密第二塊之前,會拿第一塊的密文資料與第二塊明文進行異或運算後再進行加密,以此類推,解密時也是在解密後,進行異或運算,生成最終的明文。過程如下:

明文(63位元組) 金鑰 (16位元組) 初始向量iv(16位元組)

明文1(16位元組) 明文2(16位元組) 明文3(16位元組) 明文4+一個0(16位元組)

異或 +初始向量 +密文1 +密文2 +密文3

密文1(16位元組) 密文2(16位元組) 密文3(16位元組) 密文4(16位元組)

密文(64位元組)

這裡需要注意如下幾點:
1.向量必須是一個與金鑰長度相等的資料
2.由於在加密前和解密後都會做異或運算,因此我們的明文可以不用補全,不是16個位元組的倍數也可以,CBC中會自動用0補全進行異或運算
3.在解密時是解密後才會再做異或運算,保證資料解密成功
4.由於自動進行了補全,所以解密出的資料也會在後面補全0,因此獲取到資料時,需要將末尾的0去除,或者根據源資料長度來擷取解密後的資料

優點:每次加密金鑰不同,加強了安全性

CBC的方式解決了EBC的缺點,但是也有其缺點:
1.加密無法並行運算,但是解密可以並行,必須在前一個塊加密完成後,才能加密後塊,並且也需要填充0在後面,所以並不適合流資料(不適合的原因可能是,需要滿足128位的資料之後才能進行加密,這樣後面才不會有0的補全)
2.如果前一個數據加密錯誤,那麼後續的資料都是錯的了
3.兩端需要同時約定初始向量iv


** CFB模式: 密碼反饋模式 Cipher FeedBack
這個模式只使用了加密方法,原理是用到了一個數值異或運算之後再進行一次異或運算,值不改變的原理。並且在加密的時候,如果資料並不滿足一個金鑰的位元組,那麼只做儲存,待滿足一個金鑰的位元組後再進行加密 過程如下:

加密:

明文(260個位元組) iv(128個位元組)

明文1(128個位元組) 明文2(128個位元組) 明文3(4個位元組)

(iv+key)異或 明文1 (密文1+key)異或 明文1 (密文1+key)異或明文3

密文1(128個位元組) 密文2(128個位元組) 密文3(4個位元組)


解密:

密文(260個位元組) iv(128個位元組)金鑰(128位元組)

密文1(128個位元組) 密文2(128個位元組) 密文3(4個位元組)

(iv+key)異或密文1 (密文1+key)異或密文2 (密文1+key)異或密文3

明文1 (128個位元組) 明文2 (128個位元組) 明文3(4個位元組)

這裡需要注意如下幾點:
1.加解密時會返回一個num,這個num表示還需要幾個數字,才會使用上一個密文加密,否則一直使用上上一個
2.加解密時也需要傳入字串的長度
3.由於解密時使用的都是密文來進行解密,並沒有使用上一次解密的明文,因此解密也可以並行
4.由於CFB模式並不需要補全,或者一個完整的128位元組才能加解密,綜合第三點,所以適合流資料的傳輸。
5.CFB模式不止有CFB128(即與金鑰長度一致),還有CFB1 和CFB8 即加解密1或8位後,再呼叫一次加密器生成新的值,這樣可以使加密更安全,但是就會處理更多的運算,CFB1的運算時間是CFB8的八倍 CFB128的128倍
6.使用CFB128或者CFB8的時候傳入的length單位是位元組,CFB1是length的單位是位。
7.使用CFB1和CFB8的時候,num值會始終為0

優點:解密可同步,可以傳入非16位元組倍數的資料,適合流資料

CFB模式當然也有一個缺點,解密的時候可以並行解密,但是加密的時候並不可以並行加密。並且也需要選擇iv


** OFB模式: 輸出反饋模式 Output FeedBack
該模式與CFB類似,但是是將iv或者上一個iv加密後的資料加密,生成的key與明文做異或運算,解密時採用的是同樣的方法,利用了異或運算的對稱性來進行加解密,除了這一點,其餘與CFB一致

加密/解密:

CFB:
(iv+key)異或 明文1 (密文1+key)異或 明文1 (密文1+key)異或明文3

OFB
(iv+key)異或明文1 ((iv+key)+key)異或明文1 (((iv+key)+key)+key)異或明文3

優點:與CFB一樣,方便傳輸流資料

缺點:由於依賴上一次的加密結果,所以並不能並行處理,特性是解密步驟完全一致,因此使用方法上不會有區別。


** CTR模式: 計算器模式 Counter
OFB不能並行的原因就在於需要上一次的iv進行加密後的結果,因此在CTR中我們將(iv+key)+key替換成了(iv+1)+key,這樣我們就不需要依賴上一次的加密結果了。對比如下:

OFB
(iv+key)異或明文1 ((iv+key)+key)異或明文1 (((iv+key)+key)+key)異或明文3

CTR
(iv+key)異或明文1 ((iv+1)+key)異或明文1 (((iv+1)+1)+key)異或明文3

優點:由於加解密可以並行,因此CTR模式的加解密速度也很快

缺點:iv+1的獲取比較負責,需要獲取瞬時iv

三、提供兩個示例

1、java mysql 通用aes加密演算法

通用的aes加密,使用場景,插入資料時,使用java進行加密資料,查詢時,通過sql進行解密,不用取出再遍歷解密

注:to_base64只適用mysql5.6之後的,之前的沒有這個函式,不適用,可以使用HEX,UNHEX ,當然java要用對應的方法解密

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
 
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
 
/**
 * java使用AES加密解密 AES-128-ECB加密
 * 與mysql資料庫aes加密演算法通用
 * 資料庫aes加密解密
 * -- 加密
 *    SELECT to_base64(AES_ENCRYPT('www.gowhere.so','jkl;POIU1234++=='));
 *    -- 解密
 *    SELECT AES_DECRYPT(from_base64('Oa1NPBSarXrPH8wqSRhh3g=='),'jkl;POIU1234++==');
 * @author 836508
 *
 */
public class MyAESUtil {
 
    // 加密
    public static String Encrypt(String sSrc, String sKey) throws Exception {
        if (sKey == null) {
            System.out.print("Key為空null");
            return null;
        }
        // 判斷Key是否為16位
        if (sKey.length() != 16) {
            System.out.print("Key長度不是16位");
            return null;
        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"演算法/模式/補碼方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
        return new BASE64Encoder().encode(encrypted);//此處使用BASE64做轉碼功能,同時能起到2次加密的作用。
    }
 
    // 解密
    public static String Decrypt(String sSrc, String sKey) throws Exception {
        try {
            // 判斷Key是否正確
            if (sKey == null) {
                System.out.print("Key為空null");
                return null;
            }
            // 判斷Key是否為16位
            if (sKey.length() != 16) {
                System.out.print("Key長度不是16位");
                return null;
            }
            byte[] raw = sKey.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);//先用base64解密
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original,"utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }
 
    public static void main(String[] args) throws Exception {
        /*
         * 此處使用AES-128-ECB加密模式,key需要為16位。
         */
        String cKey = "jkl;POIU1234++==";
        // 需要加密的字串
        String cSrc = "www.gowhere.so";
        System.out.println(cSrc);
        // 加密
        String enString = MyAESUtil.Encrypt(cSrc, cKey);
        System.out.println("加密後的字串是:" + enString);
 
        // 解密
        String DeString = MyAESUtil.Decrypt(enString, cKey);
        System.out.println("解密後的字串是:" + DeString);
    }
}

2、javaAES-128-CBC加密模式

package com.zhongzhi.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
 * @Classname ZzSecurityHelper
 * @Description TODO
 * @Date 2019/6/24 16:50
 * @Created by whd
 */
public class ZzSecurityHelper {
    /*
     * 加密用的Key 可以用26個字母和數字組成 使用AES-128-CBC加密模式,key需要為16位。
     */
    private static final String key="hj7x89H$yuBI0456";
    private static final String iv ="NIfb&95GUY86Gfgh";
    /**
     * @author miracle.qu
     * @Description AES演算法加密明文
     * @param data 明文
     * @param key 金鑰,長度16
     * @param iv 偏移量,長度16
     * @return 密文
     */
    public static String encryptAES(String data) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;

            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());  // CBC模式,需要一個向量iv,可增加加密演算法的強度

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return ZzSecurityHelper.encode(encrypted).trim(); // BASE64做轉碼。

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @author miracle.qu
     * @Description AES演算法解密密文
     * @param data 密文
     * @param key 金鑰,長度16
     * @param iv 偏移量,長度16
     * @return 明文
     */
    public static String decryptAES(String data) throws Exception {
        try
        {
            byte[] encrypted1 = ZzSecurityHelper.decode(data);//先用base64解密

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString.trim();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 編碼
     * @param byteArray
     * @return
     */
    public static String encode(byte[] byteArray) {
        return new String(new Base64().encode(byteArray));
    }

    /**
     * 解碼
     * @param base64EncodedString
     * @return
     */
    public static byte[] decode(String base64EncodedString) {
        return new Base64().decode(base64EncodedString);
    }
}

參考文章:

https://blog.csdn.net/DamonREN/article/details/87601165

https://blog.csdn.net/xy371661665/article/details/86423762

https://blog.csdn.net/TurboAnho/article/details/98611138

https://my.oschina.net/u/3784129/blog/3066112