相關系列文章
- 常用加密算法之 SM4 簡介及應用
- 常用加密算法之 RSA 簡介及應用
引言
AES(Advanced Encryption Standard,高級加密標準)
是一種??廣泛使用的對稱分組加密算法??,它使用相同的密鑰進行加密和解密操作,以其高安全性、高效率和支持多種密鑰長度的特點,成為保護數據安全的基石技術之一。
AES的設計基于替代-置換網絡(SPN)結構,能夠有效抵抗多種密碼攻擊,保障數據在傳輸和存儲過程中的機密性與完整性。
一、AES算法核心原理
AES是一種??分組密碼算法??,它將明文數據分成固定長度的塊進行處理,每個塊的大小為128位(16字節)。其加密過程通過多輪循環的替換和置換操作實現。
1. 密鑰與輪數
AES支持三種不同長度的密鑰,每種密鑰對應不同的加密輪數:
? AES-128??:128位密鑰,10輪加密
??? AES-192??:192位密鑰,12輪加密
??? AES-256??:256位密鑰,14輪加密
密鑰長度越長,安全性越高,但計算開銷也相應增加。
2. 加密過程
AES的加密過程包含四個核心操作,每輪都會執行這些操作(最后一輪略有不同):
-
?字節替換(SubBytes)??:通過一個稱為S盒(Substitution box)的非線性替換表,將輸入數據的每個字節替換為另一個字節,增加算法的非線性特性,增強安全性。
-
??行移位(ShiftRows)??:將數據矩陣的每一行進行循環左移操作:第一行不變,第二行左移1字節,第三行左移2字節,第四行左移3字節。這打破了列的獨立性,增強了擴散效果。
-
??列混淆(MixColumns)??:將每列視為有限域GF(2^8)上的多項式,與一個固定多項式進行模乘運算,使每個輸出字節依賴于輸入列中的所有字節,實現列內的擴散(??最后一輪省略此步驟??)。
-
??輪密鑰加(AddRoundKey)??:將當前數據狀態與當前輪的輪密鑰進行簡單的按位異或(XOR)操作。??輪密鑰由初始密鑰通過密鑰擴展算法生成??,每一輪使用的輪密鑰都不同。
3. 密鑰擴展
密鑰擴展算法將初始密鑰擴展成多個輪密鑰(子密鑰)。擴展過程使用非線性變換和循環移位操作,確保每輪加密使用不同的密鑰,極大增強了安全性。
4. 解密過程
AES的解密過程是加密過程的逆操作,使用相同的密鑰,但以相反的順序執行逆操作(InvSubBytes、InvShiftRows、InvMixColumns)和應用輪密鑰。
二、AES的工作模式與填充
1. 常見工作模式
AES有多種工作模式,以適應不同的應用場景:
? ?ECB(電子密碼本)??:最簡單的工作模式,但安全性較低,相同的明文會生成相同的密文。
? ??CBC(密碼塊鏈接)??:引入初始化向量(IV),增強安全性,是廣泛使用的模式之一。
? ??CTR(計數器模式)??:將塊密碼轉換為流密碼,適用于流加密場景。
? ??GCM(伽羅瓦/計數器模式)??:提供加密和認證功能,適用于需要保證數據完整性和機密性的場景。
工作模式 | 全稱 | 特點 | 適用場景 |
---|---|---|---|
ECB | Electronic Codebook | 簡單,并行計算,相同明文生成相同密文,安全性較低 | 簡單數據加密,不推薦用于加密大量重復模式的數據 |
CBC | Cipher Block Chaining | 引入初始化向量(IV),鏈接模式,安全性高于ECB | 文件加密,SSL/TLS |
CTR | Counter | 將分組密碼轉換為流密碼,并行加密,需唯一計數器 | 實時流媒體加密,高速網絡通信 |
GCM | Galois/Counter Mode | 提供加密和認證功能,高效并行處理 | 需要同時保證機密性和完整性的場景,如SSH |
2. 填充模式
當數據長度不是128位(16字節)的整數倍時,需要進行填充。常見的填充模式包括:
? ??PKCS5/PKCS7 Padding??:最常用的填充方案。如果需要填充n個字節,則每個填充字節的值都是n。
? ??Zero Padding??:用0x00字節填充不足的部分。需注意無法區分填充值和實際數據,可能需額外記錄數據長度。
? ??No Padding??:要求明文長度必須是塊大小的整數倍。
三、SpringBoot 集成 AES 加密算法實戰
下文 以 Java AES/CBC/PKCS5Padding
為例,實現 加密解密
PKCS5Padding 和 PKCS7Padding 填充方式一致
1. Maven 依賴
首先,在項目的 pom.xml文件中添加以下依賴:
<dependencies><!-- Bouncy Castle Provider 用于支持 PKCS7Padding --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk18on</artifactId><version>1.72</version> <!-- 請檢查最新版本 --></dependency><!-- Apache Commons Lang 用于字符串操作 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version> <!-- 請檢查最新版本 --></dependency>
</dependencies>
2. java 工具類AESUtil .java
/*** AES/CBC/PKCS5Padding 加密解密工具類 (JDK 8 原生支持)* 注意:密鑰和IV需要妥善管理,不建議硬編碼在代碼中* PKCS5Padding 和 PKCS7Padding 填充方式一致*/
public class AESUtil {// 加密算法、模式、填充方式private static final String ALGORITHM = "AES";private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; // 使用 JDK 原生支持的 PKCS5Padding// 默認密鑰和初始化向量(IV),建議從外部配置讀取private static final String DEFAULT_KEY = "abcdefghigklmnop"; // 16字節 密鑰(必須為16、24或32字節)private static final String DEFAULT_IV = "abcdefghigklmnop"; // 16字節 初始化向量(必須為16字節)/*** AES/CBC/PKCS5Padding 加密* @param plaintext 待加密的明文* @return Base64編碼的加密字符串*/public static String encrypt(String plaintext) {return encrypt(plaintext, DEFAULT_KEY, DEFAULT_IV);}/*** AES/CBC/PKCS5Padding 加密* @param plaintext 待加密的明文* @param keyStr 密鑰(必須為16、24或32字節)* @param ivStr 初始化向量(必須為16字節)* @return Base64編碼的加密字符串*/public static String encrypt(String plaintext, String keyStr, String ivStr) {if (plaintext == null || plaintext.isEmpty()) {
// throw new IllegalArgumentException("Plaintext cannot be null or empty");return "";}try {// 將密鑰和IV轉換為字節數組并封裝SecretKeySpec keySpec = new SecretKeySpec(keyStr.getBytes(StandardCharsets.UTF_8), ALGORITHM);IvParameterSpec ivSpec = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8));// 獲取并初始化Cipher對象Cipher cipher = Cipher.getInstance(TRANSFORMATION); // 使用 JDK 原生支持的 PKCS5Paddingcipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);// 執行加密操作byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));// 返回Base64編碼的字符串return Base64.getEncoder().encodeToString(encryptedBytes);} catch (Exception e) {throw new RuntimeException("Encryption failed", e);}}/*** AES/CBC/PKCS5Padding 解密* @param ciphertext Base64編碼的密文* @return 解密后的明文*/public static String decrypt(String ciphertext) {return decrypt(ciphertext, DEFAULT_KEY, DEFAULT_IV);}/*** AES/CBC/PKCS5Padding 解密* @param ciphertext Base64編碼的密文* @param keyStr 密鑰(必須與加密時使用的密鑰一致)* @param ivStr 初始化向量(必須與加密時使用的IV一致)* @return 解密后的明文*/public static String decrypt(String ciphertext, String keyStr, String ivStr) {if (ciphertext == null || ciphertext.isEmpty()) {return "";
// throw new IllegalArgumentException("Ciphertext cannot be null or empty");}try {// 將密鑰和IV轉換為字節數組并封裝SecretKeySpec keySpec = new SecretKeySpec(keyStr.getBytes(StandardCharsets.UTF_8), ALGORITHM);IvParameterSpec ivSpec = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8));// 獲取并初始化Cipher對象Cipher cipher = Cipher.getInstance(TRANSFORMATION); // 使用 JDK 原生支持的 PKCS5Paddingcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);// 先將Base64編碼的字符串解碼byte[] encryptedBytes = Base64.getDecoder().decode(ciphertext);// 執行解密操作byte[] decryptedBytes = cipher.doFinal(encryptedBytes);// 返回解密后的字符串return new String(decryptedBytes, StandardCharsets.UTF_8);} catch (Exception e) {throw new RuntimeException("Decryption failed", e);}}/*** 測試示例*/public static void main(String[] args) {String originalText = "hello AES CBC";// 加密String encryptedText = encrypt(originalText);System.out.println("加密后 (Base64): " + encryptedText);// 解密String decryptedText = decrypt(encryptedText);System.out.println("解密后: " + decryptedText);// 驗證System.out.println("解密是否成功: " + originalText.equals(decryptedText));}
}
四、前端 Vue 中使用 AES-CBC-Pkcs7 加密解密
1. 安裝 CryptoJS 庫
首先,使用 npm 或 yarn 安裝 crypto-js
:
npm install crypto-js
# 或
yarn add crypto-js
2. 創建工具文件 AESUtils.js
創建一個單獨的 JavaScript 文件(如 cryptoUtils.js)來存放加密解密函數,方便復用:
import CryptoJS from 'crypto-js';
// const CryptoJS = require('crypto-js'); //引用AES源碼js
const key = CryptoJS.enc.Utf8.parse("abcdefghigklmnop"); //密鑰
const iv = CryptoJS.enc.Utf8.parse("abcdefghigklmnop"); //密鑰偏移量// 加密方法 - AES-CBC-Pkcs7
export function EncryptCBCPkcs7(word) {const encrypted = CryptoJS.AES.encrypt(word, key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return encrypted.toString();
}// 解密方法 - AES-CBC-Pkcs7
export function DecryptCBCPkcs7(word) {const decrypt = CryptoJS.AES.decrypt(word, key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return decrypt.toString(CryptoJS.enc.Utf8);
}
3. 測試使用
// 引入js
import { EncryptCBCPkcs7,DecryptCBCPkcs7 } from '@/utils/AESUtils';
//編寫測試方法
function test(){try {const encrypted = EncryptCBCPkcs7('Hello, World!');console.log('加密結果:', encrypted);const decrypted = DecryptCBCPkcs7(encrypted);console.log('解密結果:', decrypted);} catch (error) {console.error('錯誤:', error);}
}