在 JavaScript 中轻松实现 AES 加密与解密:从原理到实战
在当今的 Web 开发中,数据安全至关重要,而 AES(Advanced Encryption Standard,高级加密标准)作为目前最流行的对称加密算法之一,被广泛应用于接口数据传输、本地数据存储等场景。本文将带大家深入了解 AES 加密的核心概念,并通过纯 JavaScript 代码案例,详细讲解加密、解密的实现过程,同时清晰剖析每个参数的作用,让你看完就能直接上手使用。
一、AES 加密的核心概念铺垫
在开始代码实现前,我们需要先明确几个 AES 加密的关键概念,这是理解后续参数和代码的基础:
-
对称加密:AES 属于对称加密算法,意味着加密和解密使用同一把密钥(Key),因此密钥的安全性直接决定了数据的安全性。
-
分组密码:AES 是分组密码,会将明文按照固定长度(称为 “分组大小”)分成若干组进行加密,AES 的分组大小固定为128 位(16 字节)。
-
密钥长度:AES 支持三种密钥长度:128 位、192 位、256 位,对应的算法通常称为 AES-128、AES-192、AES-256。密钥长度越长,安全性越高,但加密速度会略有下降,日常开发中 AES-128 已能满足大部分场景需求。
-
模式(Mode):由于 AES 是分组加密,需要通过 “模式” 定义多组数据的加密方式,常用的模式有CBC(Cipher Block Chaining,密码分组链接模式)、GCM(Galois/Counter Mode,伽罗瓦 / 计数器模式)等。其中 CBC 模式需要配合 “初始化向量(IV)” 使用,而 GCM 模式自带完整性校验,安全性更高。
-
填充(Padding):当明文长度不是分组大小(16 字节)的整数倍时,需要通过 “填充” 方式补全到分组大小。常用的填充方式有PKCS#7(在 JavaScript 中常用)、PKCS#5 等。
二、JavaScript 实现 AES 的依赖:CryptoJS
在浏览器环境中,原生 JavaScript 并没有直接提供 AES 加密的 API,因此我们通常使用第三方库 ——CryptoJS(一个轻量级的加密库,支持 AES、MD5、SHA 等多种加密算法)。
1. 引入 CryptoJS
可以通过 CDN 直接引入,也可以下载源码本地引入:
<!-- 通过CDN引入CryptoJS(包含所有加密算法) --><script src="https://cdn.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js"></script><!-- 若只需要AES模块,可引入单独的AES文件(体积更小) --><script src="https://cdn.jsdelivr.net/npm/crypto-js@4.2.0/aes.min.js"></script><script src="https://cdn.jsdelivr.net/npm/crypto-js@4.2.0/pad-pkcs7.min.js"></script> <!-- 用于PKCS#7填充 --><script src="https://cdn.jsdelivr.net/npm/crypto-js@4.2.0/mode-cbc.min.js"></script> <!-- 用于CBC模式 -->
三、AES 加密:参数详解与代码实现
AES 加密的核心是构建加密配置,并传入明文、密钥、初始化向量(IV)等参数。下面以最常用的 “AES-128-CBC-PKCS#7” 组合为例,讲解加密过程。
1. 加密所需核心参数
参数名 | 作用 | 要求与说明 |
---|---|---|
plaintext | 待加密的明文数据 | 可以是字符串(如接口参数、用户信息)、JSON 对象(需先转为字符串) |
key | 加密密钥(对称密钥) | 需与解密密钥完全一致;若使用 AES-128,密钥长度需为 16 字节(128 位) |
iv | 初始化向量(仅 CBC、CFB 等模式需要) | 长度固定为 16 字节(与 AES 分组大小一致);建议每次加密生成随机 IV,增强安全性 |
mode | 加密模式 | 常用CryptoJS.mode.CBC 、CryptoJS.mode.GCM 等 |
padding | 填充方式 | 常用CryptoJS.pad.PKCS7 (当明文长度不是 16 的倍数时自动补全) |
outputType | 加密结果的输出格式 | 可选Base64 (常用,结果简洁)、Hex (十六进制字符串) |
2. 加密代码实现(AES-128-CBC)
下面的代码包含 “生成随机 IV”“明文加密”“结果格式化” 三个核心步骤,并添加了详细注释:
/*** AES加密函数(AES-128-CBC-PKCS#7,输出Base64格式)* @param {string} plaintext - 待加密的明文(字符串)* @param {string} key - 加密密钥(16字节,若长度不足会自动补全,建议手动控制为16字节)* @returns {object} - 包含加密结果(ciphertext)和初始化向量(iv)的对象*/function aesEncrypt(plaintext, key) {// 1. 生成随机IV(16字节,CBC模式必须)// CryptoJS.lib.WordArray.random(16) 生成16字节的随机数,再转为Base64格式方便存储/传输const iv = CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Base64);// 2. 将密钥和IV转为CryptoJS可识别的格式(WordArray)// CryptoJS.enc.Utf8.parse(key):将UTF-8编码的密钥转为WordArrayconst keyWordArray = CryptoJS.enc.Utf8.parse(key);const ivWordArray = CryptoJS.enc.Base64.parse(iv); // IV是Base64格式,需用Base64解析// 3. 执行AES加密// 参数1:明文(需转为WordArray);参数2:密钥;参数3:加密配置(模式、IV、填充)const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), // 明文转为WordArraykeyWordArray,{mode: CryptoJS.mode.CBC, // 加密模式:CBCiv: ivWordArray, // 初始化向量padding: CryptoJS.pad.PKCS7 // 填充方式:PKCS#7});// 4. 加密结果转为Base64格式(encrypted.toString() 默认就是Base64)const ciphertext = encrypted.toString();// 返回加密结果和IV(解密时需要IV)return { ciphertext, iv };}// ------------------- 加密示例 -------------------const originalData = '{"username":"admin","password":"123456"}'; // 待加密的JSON字符串const secretKey = 'mySecretKey123456'; // 16字节的密钥(AES-128)// 执行加密const encryptResult = aesEncrypt(originalData, secretKey);console.log('加密结果:', encryptResult.ciphertext);console.log('使用的IV:', encryptResult.iv);// 输出示例:// 加密结果: U2FsdGVkX1+...(Base64字符串)// 使用的IV: abc123def456...(Base64字符串)
四、AES 解密:参数详解与代码实现
解密过程是加密的逆操作,需要使用与加密相同的密钥、IV、模式、填充方式,否则无法正确解密。
1. 解密所需核心参数
参数名 | 作用 | 要求与说明 |
---|---|---|
ciphertext | 待解密的密文(加密后的结果) | 需与加密输出格式一致(如 Base64,若加密用 Hex 则解密也用 Hex) |
key | 解密密钥 | 必须与加密密钥完全一致 |
iv | 初始化向量 | 必须与加密时使用的 IV 完全一致(CBC 模式必需) |
mode /padding | 解密模式 / 填充方式 | 必须与加密时的配置完全一致(否则解密会失败或出现乱码) |
2. 解密代码实现(AES-128-CBC)
解密代码与加密代码结构对称,核心是 “解析密文和 IV”“执行解密”“还原明文”:
/*** AES解密函数(AES-128-CBC-PKCS#7,输入Base64格式密文)* @param {string} ciphertext - 待解密的密文(Base64格式)* @param {string} key - 解密密钥(与加密密钥一致,16字节)* @param {string} iv - 加密时使用的IV(Base64格式)* @returns {string} - 解密后的明文(字符串)*/function aesDecrypt(ciphertext, key, iv) {// 1. 将密钥、IV、密文转为CryptoJS可识别的格式const keyWordArray = CryptoJS.enc.Utf8.parse(key);const ivWordArray = CryptoJS.enc.Base64.parse(iv); // IV是Base64格式,需解析const ciphertextWordArray = CryptoJS.enc.Base64.parse(ciphertext); // 密文是Base64格式,需解析// 2. 执行AES解密const decrypted = CryptoJS.AES.decrypt({ ciphertext: ciphertextWordArray }, // 密文需包装为 { ciphertext: WordArray } 格式keyWordArray,{mode: CryptoJS.mode.CBC, // 与加密模式一致iv: ivWordArray, // 与加密IV一致padding: CryptoJS.pad.PKCS7 // 与加密填充方式一致});// 3. 解密结果转为UTF-8字符串(还原明文)const plaintext = decrypted.toString(CryptoJS.enc.Utf8);return plaintext;}// ------------------- 解密示例 -------------------// 使用加密时的结果(密文和IV)进行解密const decryptResult = aesDecrypt(encryptResult.ciphertext, // 加密后的密文secretKey, // 与加密一致的密钥encryptResult.iv // 与加密一致的IV);console.log('解密结果:', decryptResult);// 输出示例:{"username":"admin","password":"123456"}(与原始明文一致)// 若原始数据是JSON,可进一步解析为JSON对象const decryptedData = JSON.parse(decryptResult);console.log('解密后的JSON对象:', decryptedData);// 输出示例:{ username: 'admin', password: '123456' }
五、常见问题与注意事项
-
密钥长度不匹配怎么办?
若密钥长度不是 16/24/32 字节(对应 AES-128/192/256),CryptoJS 会自动用
0x00
补全或截断,但这会降低安全性。建议手动控制密钥长度,例如通过 SHA-256 哈希将任意长度的字符串转为 32 字节(256 位)密钥:
// 将任意长度的密钥转为32字节(AES-256)const rawKey = 'myLongerSecretKey123'; // 任意长度const key = CryptoJS.SHA256(rawKey).toString().slice(0, 32); // SHA-256哈希后取前32字节
-
IV 需要保密吗?
不需要!IV 的作用是防止相同明文加密后得到相同密文,因此 IV 可以随密文一起传输(如放在接口返回的 JSON 中),但必须保证每次加密使用不同的 IV(建议随机生成)。
-
GCM 模式与 CBC 模式的区别?
GCM 模式支持 “加密 + 完整性校验”,可以检测密文是否被篡改,安全性更高,且不需要单独的填充方式(自带处理)。若需使用 GCM 模式,只需将
mode
改为CryptoJS.mode.GCM
,并在加密结果中获取authTag
(解密时需传入):
// GCM模式加密(示例片段)const encrypted = CryptoJS.AES.encrypt(plaintextWordArray, keyWordArray, {mode: CryptoJS.mode.GCM,iv: ivWordArray});const authTag = encrypted.getAuthTag().toString(CryptoJS.enc.Base64); // 获取认证标签// GCM模式解密(示例片段)const decrypted = CryptoJS.AES.decrypt({ ciphertext: ciphertextWordArray },keyWordArray,{mode: CryptoJS.mode.GCM,iv: ivWordArray,authTag: CryptoJS.enc.Base64.parse(authTag) // 传入认证标签});
-
浏览器环境与 Node.js 环境的差异?
本文代码适用于浏览器环境(依赖 CryptoJS);若在 Node.js 环境中,可使用原生的
crypto
模块(无需第三方库),核心参数(密钥、IV、模式、填充)的逻辑一致,只是 API 写法不同。
六、总结
本文通过 CryptoJS 库,详细讲解了 JavaScript 中 AES 加密、解密的实现过程,核心要点可总结为:
-
参数一致性:加密和解密的
密钥、IV、模式、填充方式
必须完全一致,否则无法正确解密; -
密钥安全性:密钥需手动控制长度(16/24/32 字节),且避免硬编码在前端代码中(建议通过后端接口动态获取);
-
IV 随机性:每次加密生成随机 IV,避免相同明文加密后得到相同密文;
-
模式选择:日常场景用 CBC 模式足够,对安全性要求高的场景(如支付、敏感数据)建议用 GCM 模式。
掌握这些知识后,你可以轻松将 AES 加密应用到实际项目中,例如接口请求参数加密、本地 Storage 数据加密等,为你的 Web 应用增加一层安全保障。