详细讲解在JavaScript环境下如何使用CryptoJS库实现标准的AES加密与解密。包含环境引入、WordArray编码转换、CBC模式与IV配置、跨语言对接规范以及前端安全边界分析。
JavaScript怎么使用CryptoJS实现AES加密
时间:2026-11-02
浏览:325
文章目录
在 JavaScript 中使用 CryptoJS 实现 AES 加密,核心在于理解密钥格式解析、工作模式(如 CBC)、初始向量(IV)以及密文编码输出。初学者在对接 Java、Go 或 Python 服务端时,最容易出现“加密报错”或“解密出来全是乱码”的问题,其根源往往不在于 AES 算法本身,而在于未将字符串显式转换为 CryptoJS 内部标准的 WordArray,或者前后端的填充(Padding)与工作模式(Mode)未严格对齐。
一、引入 CryptoJS 依赖
在编写业务逻辑前,需要根据运行时环境(Node.js 或 Web 浏览器)将 CryptoJS 引入工程中。
1. Node.js / 打包工具环境(Webpack, Vite 等)
在工程根目录安装官方 npm 包:
npm install crypto-js
随后在 JavaScript 代码中按需或全量导入:
// CommonJS 规范
const CryptoJS = require("crypto-js");
// 或 ES Module 规范
import CryptoJS from "crypto-js";
2. 原生 HTML / 浏览器直引环境
如果在没有前端工程化构建工具的页面中使用,可以通过 CDN 脚本标签引入:
<script src="https://cdn.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js"></script>
引入后,全局对象 window.CryptoJS 即可直接调用。
二、AES 加密工作机制与数据流向
AES(Advanced Encryption Standard,高级加密标准)属于对称分组密码体制,数据块大小固定为 128 位(16 字节)。密钥长度支持 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。
在实际工程中,最常用且安全性受认可的分组模式是 CBC(Cipher Block Chaining,密文分组链接模式)。在 CBC 模式下,每一个明文块在被加密前,都会先与前一个密文块进行异或运算;而第一个明文块则与指定的“初始向量(IV)”进行异或。这意味着相同的明文即使多次加密,只要 IV 随机,产生的密文就完全不同,有效抵御模式分析攻击。
三、使用 CryptoJS 实现 AES 加密的操作步骤
在实际编码中,实现一套标准、规范的 AES 加密解密流程,需要按部就班完成 4 个关键操作环节:
四、标准代码示例:AES-CBC 加密与解密
下面是一个经过严格验证的生产级别完整实现代码。示例包含明文加密为 Base64 字符串、以及从 Base64 密文还原明文的全部逻辑:
const CryptoJS = require("crypto-js");
/**
* AES-CBC 模式加密
* @param {string} plainText 待加密明文
* @param {string} keyStr 16位(AES-128)或32位(AES-256)密钥字符串
* @param {string} ivStr 16位初始向量字符串
* @returns {string} Base64 编码密文
*/
function encryptAES_CBC(plainText, keyStr, ivStr) {
// 1. 将字符串转为 CryptoJS 内部的 WordArray
const key = CryptoJS.enc.Utf8.parse(keyStr);
const iv = CryptoJS.enc.Utf8.parse(ivStr);
const srcs = CryptoJS.enc.Utf8.parse(plainText);
// 2. 执行 AES 加密
const encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 3. 返回标准的 Base64 密文字符串
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}
/**
* AES-CBC 模式解密
* @param {string} cipherTextBase64 Base64 编码密文
* @param {string} keyStr 密钥字符串
* @param {string} ivStr 初始向量字符串
* @returns {string} 解密后的明文字符串
*/
function decryptAES_CBC(cipherTextBase64, keyStr, ivStr) {
// 1. 解析 key 与 iv
const key = CryptoJS.enc.Utf8.parse(keyStr);
const iv = CryptoJS.enc.Utf8.parse(ivStr);
// 2. 解密过程可直接接收 Base64 字符串,也可先 parse 为 WordArray
const decrypted = CryptoJS.AES.decrypt(cipherTextBase64, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 3. 将解密出来的 WordArray 转为 UTF-8 文本
return decrypted.toString(CryptoJS.enc.Utf8);
}
// ==================== 运行验证 ====================
// 选用 16 字节(128位)的密钥与向量
const secretKey = "1234567890123456"; // 16 字符 = 16 字节
const secretIV = "abcdef0123456789"; // 16 字符 = 16 字节
const originalText = "Hello CryptoJS! 中文测试与安全通信。";
console.log("原始明文:", originalText);
// 加密
const cipherResult = encryptAES_CBC(originalText, secretKey, secretIV);
console.log("加密输出 (Base64):", cipherResult);
// 解密
const plainResult = decryptAES_CBC(cipherResult, secretKey, secretIV);
console.log("解密还原:", plainResult);
// 验证一致性
console.log("还原校验:", originalText === plainResult ? "成功匹配" : "解密失败");
原始明文: Hello CryptoJS! 中文测试与安全通信。
加密输出 (Base64): R3vXm7q5G1YfI8u...(实际输出因密钥与明文而定)
解密还原: Hello CryptoJS! 中文测试与安全通信。
还原校验: 成功匹配
五、工作模式对比:为什么推荐 CBC 而非 ECB
CryptoJS 同时支持多种工作模式,如 CryptoJS.mode.CBC、CryptoJS.mode.ECB、CryptoJS.mode.CFB、CryptoJS.mode.CTR、CryptoJS.mode.OFB。但在实际业务中,最常引起争议和误用的是 ECB 与 CBC。
在 ECB 模式下,明文被简单切成 16 字节小段单独加密。如果明文中有两段相同的内容(例如重复的空格或固定的 JSON 结构),其密文结果也是完全相同的。攻击者无需破解密钥,仅通过观察密文段的重复频率或对密文段进行重新拼接排序,就能伪造合法报文。因此在现代系统开发中,应坚决选用 CBC 模式或认证加密模式(如 GCM)。
六、跨语言对接的常见三大陷阱
前后端跨语言对接(例如 JavaScript 前端加密,Java/Spring Boot 或 Python 后端解密)时,超过 90% 的失败是由以下细节引起的:
陷阱 1:字符串未转 WordArray 导致隐式口令派生(OpenSSL 格式)
如果直接传入字符串而非 WordArray:
// 错误用法:传入普通字符串
CryptoJS.AES.encrypt("hello", "my-secret-key");
此时 CryptoJS 会认为传入的不是“原始密钥”,而是一个“口令(Passphrase)”。它会在内部生成一个随机的 8 字节 Salt(盐值),并通过 PBKDF/OpenSSL 规则计算出真正的密钥,最终导出的密文字符串会以 Salted__(Base64 表现为 U2FsdGVkX1...)开头。如果后端直接拿 my-secret-key 当作真实 Key 去解密,必定报“Bad Padding”或解密出乱码。
解决办法:必须始终使用 CryptoJS.enc.Utf8.parse(keyStr) 将 Key 与 IV 转成 WordArray。
陷阱 2:密钥与 IV 长度不足或超标
AES 算法严格要求:
- AES-128:密钥长度必须是 16 字节(128 位)。
- AES-192:密钥长度必须是 24 字节(192 位)。
- AES-256:密钥长度必须是 32 字节(256 位)。
- CBC 模式下的 IV:长度必须固定为 16 字节(128 位)。
如果传入了 15 个字符或包含中文多字节字符(一个汉字占 3 字节 UTF-8),字节总数未能精确匹配,将导致不可预期的截断或填充行为。在编写代码时应显式确认 key.sigBytes 达到对应字节数。
陷阱 3:密文编码格式不一致(Base64 与 Hex 混淆)
CryptoJS.AES.encrypt() 返回的是一个 CipherParams 对象。初学者常常直接调用 encrypted.toString()。如果加密时提供了 WordArray 格式的 Key 和 IV,encrypted.toString() 默认会输出 Base64 编码;但部分后端系统(如使用某些 C/C++ 或旧版 PHP 驱动)可能要求十六进制 Hex(如 e4a1b2...)。必须与后端接口文档核对清楚:
// 输出 Base64(最常见)
const base64Cipher = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
// 输出 Hex(十六进制)
const hexCipher = encrypted.ciphertext.toString(CryptoJS.enc.Hex);
七、生产落地自查与安全边界认知
为了确保系统不仅能“跑通加解密”,还能真正具备工程安全性,建议在项目上线前依照下表进行安全自查:
最后必须清醒认识到前端加密的威胁模型与局限性:在浏览器环境中,任何打包在 bundle.js 里的密钥或混淆算法,对具有调试能力的逆向者而言都是透明的。因此,前端 AES 加密通常用于提高爬虫和自动化脚本的数据提取门槛,或者与非对称加密(RSA/SM2)结合实现混合加密体系(例如使用 RSA 公钥加密临时的 AES Key,再用 AES 加密大批量业务报文)。全链路的信道安全,仍应依托底层的 TLS/HTTPS 协议来保障。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。