目录
[前端代码示例(Web Crypto API)](#前端代码示例(Web Crypto API))
[Web Crypto讲解](#Web Crypto讲解)
[Web Crypto 核心特性](#Web Crypto 核心特性)
[Web Crypto 核心对象与方法](#Web Crypto 核心对象与方法)
[Web Crypto 典型使用场景](#Web Crypto 典型使用场景)
[1. 计算文件哈希(如校验文件完整性)](#1. 计算文件哈希(如校验文件完整性))
[2. AES-GCM 加密数据](#2. AES-GCM 加密数据)
[3. 非对称加密(RSA-OAEP)](#3. 非对称加密(RSA-OAEP))
[Web Crypto 密钥管理](#Web Crypto 密钥管理)
[1. 导出/导入密钥(JWK 格式)](#1. 导出/导入密钥(JWK 格式))
[2. 密钥派生(PBKDF2)](#2. 密钥派生(PBKDF2))
[Web Crypto 安全注意事项](#Web Crypto 安全注意事项)
[Web Crypto 与其他库的对比](#Web Crypto 与其他库的对比)
[Web Crypto 浏览器兼容性](#Web Crypto 浏览器兼容性)
[Web Crypto 总结](#Web Crypto 总结)
[对称加密 vs 非对称加密对比表](#对称加密 vs 非对称加密对比表)
前端信封加解密(Envelope Encryption)是一种结合对称加密和非对称加密(或分层密钥管理)的技术,主要用于解决密钥管理 和大规模数据加密的安全性问题。
信封加解密中的核心术语
术语 | 定义 | 作用 | 技术细节 | 典型应用场景 |
---|---|---|---|---|
信封加密 (Envelope Encryption) | 一种分层加密技术,通过两层密钥(DEK + KEK)保护数据。 | 平衡性能与安全性:用对称加密处理大数据,用非对称加密保护密钥。 | 组合 AES(对称) + RSA/ECC(非对称) | 云存储加密、前端敏感数据传输、端到端加密。 |
DEK (Data Encryption Key) | 用于直接加密数据的临时对称密钥。 | 加密明文数据,保证数据机密性。 | 算法:AES-GCM、AES-CBC 长度:128/256 位 生命周期:单次使用或短期有效。 | 加密用户上传的文件、数据库字段加密。 |
KEK (Key Encryption Key) | 用于加密 DEK 的主密钥(对称或非对称)。 | 保护 DEK,避免直接暴露主密钥。 | 对称:AES-256 非对称:RSA-2048/ECC-384 生命周期:长期存储,需严格保护。 | AWS KMS、阿里云 KMS 中的主密钥管理。 |
IV (Initialization Vector) | 初始化向量,配合对称加密算法使用。 | 确保相同明文加密后生成不同密文,防止模式攻击。 | 长度:AES-GCM 为 12 字节 生成方式:随机值(无需保密) | AES-GCM/AES-CBC 加密时生成随机 IV。 |
KMS (Key Management Service) | 密钥管理服务,用于生成、存储和管理 KEK。 | 集中管理密钥,提供安全的密钥存储和访问控制。 | 支持硬件安全模块(HSM)、密钥轮换、审计日志。 | 云服务(如 AWS KMS、Google Cloud KMS)中的密钥托管。 |
RSA-OAEP | 一种非对称加密算法(Optimal Asymmetric Encryption Padding)。 | 安全加密 DEK,防止选择密文攻击。 | 填充模式:OAEP 哈希算法:SHA-256 密钥长度:2048 位以上。 | 加密 DEK 并传输给后端。 |
AES-GCM | 对称加密算法(Galois/Counter Mode)。 | 高效加密数据,同时提供完整性和认证。 | 密钥长度:128/256 位 附加认证数据(AAD):可选 输出:密文 + 认证标签(Authentication Tag)。 | 加密用户敏感信息(如身份证号、银行卡号)。 |
密钥派生 (Key Derivation) | 从主密钥或密码派生子密钥的过程。 | 减少密钥存储数量,增强密钥复用安全性。 | 算法:PBKDF2、HKDF、Scrypt 参数:盐值(Salt)、迭代次数。 | 从用户密码派生加密密钥。 |
密钥轮换 (Key Rotation) | 定期更换密钥的策略。 | 降低密钥泄露风险,满足合规要求。 | 自动轮换周期(如 90 天) 历史密钥保留策略。 | 云服务中的 KEK 自动轮换。 |
密文 (Ciphertext) | 加密后的数据。 | 保护明文数据,防止未授权访问。 | 格式:Base64 或二进制 包含元数据:IV、认证标签(如 AES-GCM)。 | 存储在数据库或传输中的加密结果。 |
明文 (Plaintext) | 未加密的原始数据。 | 加密前的可读数据。 | 文本、二进制文件等。 | 用户输入的密码、身份证号等敏感信息。 |
密钥包装 (Key Wrapping) | 用 KEK 加密 DEK 的过程。 | 安全传输/存储 DEK。 | API:crypto.subtle.wrapKey() 格式:RAW/PKCS8/JWK。 |
前端用 RSA 公钥加密 DEK 后传输给后端。 |
密钥解包 (Key Unwrapping) | 用 KEK 解密 DEK 的过程。 | 恢复 DEK 以解密数据。 | API:crypto.subtle.unwrapKey() 需指定目标算法和用途。 |
后端用 RSA 私钥解密 DEK 后解密数据。 |
端到端加密 (E2EE) | 数据仅在发送方和接收方解密,中间节点无法访问明文。 | 保护通信隐私,防止中间人窃听。 | 结合信封加密与密钥协商协议(如 Diffie-Hellman)。 | 即时通讯(如 WhatsApp)、安全文件传输。 |
密钥生命周期管理 | 密钥从生成到销毁的全过程管理。 | 确保密钥安全性和合规性。 | 阶段:生成 -> 激活 -> 轮换 -> 归档 -> 销毁。 | 企业级 KMS 中的密钥策略配置。 |
关键概念关联图
javascript
+-----------------+
| KEK | (主密钥,长期存储)
+--------+--------+
|
| 加密/解密
v
+-----------------+
| DEK | (临时密钥,加密数据)
+--------+--------+
|
| 加密/解密
v
+-----------------+
| Plaintext | <--> | Ciphertext |
+-----------------+ +------------------+
信封加解密核心原理
信封加密的核心思想是 "用密钥加密密钥",分为两层加密:
-
数据加密密钥(DEK, Data Encryption Key)
-
使用对称加密算法(如AES)加密实际数据,速度快,适合大数据量。
-
DEK是临时生成的随机密钥,每次加密时动态创建。
-
-
密钥加密密钥(KEK, Key Encryption Key)
-
使用非对称加密算法(如RSA)或另一个对称密钥加密DEK。
-
KEK是长期存储的主密钥,需严格保护(如存储在硬件安全模块HSM或KMS中)。
-
最终加密结果称为"信封",包含:
-
密文(数据加密后的结果)
-
加密后的DEK(用KEK加密后的DEK)
典型应用场景
-
云端数据加密
如AWS KMS、阿里云KMS,用户通过KEK管理DEK,避免主密钥频繁暴露。
-
前端敏感数据加密
前端加密用户数据后传输到后端,后端仅存储加密后的DEK和密文。
-
端到端加密(E2EE)
结合信封加密和密钥协商协议(如Diffie-Hellman),实现通信双方的安全数据传输。
具体实现步骤
以前端加密数据为例,典型流程如下:
步骤1:生成DEK(数据加密密钥)
javascript
// 使用Web Crypto API生成随机AES密钥
const dek = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true, // 可导出(若需要)
["encrypt", "decrypt"]
);
步骤2:用DEK加密数据
javascript
const data = new TextEncoder().encode("敏感数据");
const iv = crypto.getRandomValues(new Uint8Array(12)); // 初始化向量
const encryptedData = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
dek,
data
);
步骤3:用KEK加密DEK
假设KEK是一个预先配置的公钥(非对称加密):
javascript
// 假设kekPublicKey是后端提供的RSA公钥
const encryptedDEK = await crypto.subtle.wrapKey(
"raw", // 导出格式
dek,
kekPublicKey,
{ name: "RSA-OAEP" }
);
步骤4:组装"信封"
将加密后的数据和加密后的DEK组合发送到后端:
javascript
{
"encryptedData": "Base64密文",
"encryptedDEK": "Base64加密后的DEK",
"iv": "Base64初始化向量"
}
步骤5:解密流程
后端用KEK私钥解密DEK,再用DEK解密数据。
信封加解密全流程示例图 
前端代码示例(Web Crypto API)
1.效果预览(仅展示,实际开发请看安全提示哦!)
2.前端展示信封加解密全代码
html
<!DOCTYPE html>
<html>
<head>
<title>信封加密完整示例</title>
<meta charset="UTF-8">
</head>
<body>
<h2>信封加密演示</h2>
<div style="max-width: 800px; margin: 20px auto;">
<textarea id="plaintext" rows="4" cols="50" placeholder="输入要加密的文本..."></textarea>
<div style="margin: 10px 0;">
<button onclick="encryptData()">加密</button>
<button onclick="decryptData()">解密</button>
</div>
<div>
<h4>加密结果:</h4>
<pre id="encryptedResult"></pre>
<h4>解密结果:</h4>
<pre id="decryptedResult"></pre>
</div>
</div>
<script>
// 存储加密信封的全局对象
let encryptionEnvelope = null;
// 修复点1:正确配置密钥用途
async function generateRSAKeys() {
/*
生成RSA密钥对:
- 公钥用途:wrapKey(加密DEK)
- 私钥用途:unwrapKey(解密DEK)
*/
return await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
hash: "SHA-256"
},
true, // 密钥可导出(用于演示)
["wrapKey", "unwrapKey"] // 关键修复:明确密钥用途
);
}
// 加密函数
async function encryptData() {
try {
const plaintext = document.getElementById('plaintext').value;
if (!plaintext) return alert("请输入要加密的内容");
// 生成RSA密钥对(KEK)
const { publicKey, privateKey } = await generateRSAKeys();
// 生成AES-GCM密钥(DEK)
const dek = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true, // 必须为true才能导出
["encrypt", "decrypt"] // DEK用途
);
// 使用DEK加密数据
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedData = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
dek,
new TextEncoder().encode(plaintext)
);
// 修复点2:使用正确的wrapKey方法
const wrappedDek = await crypto.subtle.wrapKey(
"raw", // 导出格式
dek, // 要加密的DEK
publicKey, // 使用RSA公钥加密
{ name: "RSA-OAEP" }
);
// 存储信封数据(仅用于演示)
encryptionEnvelope = {
encryptedData,
wrappedDek,
iv,
privateKey // 警告:实际生产环境私钥不能存储在前端!
};
// 显示加密结果
document.getElementById('encryptedResult').textContent =
`加密数据:${arrayBufferToBase64(encryptedData)}\n` +
`加密后的DEK:${arrayBufferToBase64(wrappedDek)}\n` +
`IV:${arrayBufferToBase64(iv)}`;
} catch (error) {
handleError("加密失败", error);
}
}
// 解密函数
async function decryptData() {
if (!encryptionEnvelope) return alert("请先加密数据");
try {
// 修复点3:正确的unwrapKey参数
const unwrappedDek = await crypto.subtle.unwrapKey(
"raw", // 导入格式
encryptionEnvelope.wrappedDek,
encryptionEnvelope.privateKey,
{
name: "RSA-OAEP",
hash: "SHA-256" // 必须与生成时一致
},
{
name: "AES-GCM", // 必须匹配原始算法
length: 256 // 必须指定长度
},
true, // 密钥可导出
["decrypt"] // 明确解密用途
);
// 使用DEK解密数据
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv: encryptionEnvelope.iv },
unwrappedDek,
encryptionEnvelope.encryptedData
);
document.getElementById('decryptedResult').textContent =
new TextDecoder().decode(decrypted);
} catch (error) {
handleError("解密失败", error);
}
}
// 工具函数:ArrayBuffer转Base64
function arrayBufferToBase64(buffer) {
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}
// 错误处理
function handleError(context, error) {
console.error(`${context}:`, error);
alert(`${context}:${error.message}`);
}
</script>
<!-- 安全警告 -->
<div style="color: #d32f2f; margin-top: 20px; padding: 15px; background: #ffebee;">
<strong>安全提示:</strong>
<ul>
<li>此示例仅用于论坛学习交流目的,实际生产环境中:
<ul>
<li>RSA私钥必须存储在后端安全系统(如HSM/KMS)</li>
<li>必须通过HTTPS协议传输加密数据</li>
<li>应定期轮换主密钥(KEK)</li>
</ul>
</li>
<li>前端加密不能替代传输层安全(必须使用HTTPS)</li>
</ul>
</div>
</body>
</html>
信封加解密安全注意事项
-
密钥管理
KEK必须严格保护,推荐使用HSM或KMS服务,避免硬编码在前端。
-
算法选择
使用AES-GCM(对称)和RSA-OAEP(非对称)等现代算法,避免ECB模式。
-
初始化向量(IV)
IV需随机生成且无需保密,但同一DEK不能重复使用IV。
-
前端局限性
纯前端加密无法完全抵御中间人攻击,需配合HTTPS、证书绑定(Certificate Pinning)等。
与KMS服务集成
云服务(如AWS KMS)的信封加密流程:
-
前端请求KMS生成DEK。
-
KMS返回明文DEK和加密后的DEK。
-
前端用明文DEK加密数据,丢弃明文DEK。
-
存储加密后的DEK和密文,解密时需调用KMS解密DEK。
Web Crypto讲解
Web Crypto API 是浏览器原生提供的密码学操作接口,用于在客户端(如浏览器)执行加密、解密、签名、哈希等安全操作。它是 W3C 标准的一部分,旨在提供高性能、安全且标准化的密码学功能,取代传统的 JavaScript 加密库(如 CryptoJS)
Web Crypto核心特性
-
标准化与安全性
-
由 W3C 定义,浏览器原生实现,避免第三方库潜在的安全漏洞。
-
运行在浏览器安全沙箱中,密钥材料无法被 JavaScript 直接访问(需通过
CryptoKey
对象管理)。
-
-
支持主流算法
-
对称加密:AES(CBC、CTR、GCM 等模式)。
-
非对称加密:RSA(OAEP、PKCS1-v1.5)、ECC(ECDH、ECDSA)。
-
哈希算法:SHA-1、SHA-256、SHA-384、SHA-512。
-
密钥派生:PBKDF2、HKDF。
-
签名与验证:HMAC、RSA-PSS、ECDSA。
-
-
异步操作
- 所有方法均返回
Promise
,适合处理耗时操作。
- 所有方法均返回
-
安全上下文限制
- 仅能在 HTTPS 或 localhost 等安全上下文中使用。
Web Crypto核心对象与方法
Web Crypto API 的主入口是 crypto.subtle
对象(SubtleCrypto
接口),提供以下核心方法:
方法 | 用途 |
---|---|
generateKey() |
生成密钥(对称或非对称)。 |
encrypt() / decrypt() |
加密/解密数据。 |
sign() / verify() |
生成签名/验证签名。 |
digest() |
计算数据哈希值(如 SHA-256)。 |
deriveKey() |
从基础密钥派生出新密钥(如 PBKDF2)。 |
wrapKey() / unwrapKey() |
包装(加密)/解包(解密)密钥。 |
importKey() / exportKey() |
导入/导出密钥(如从 JWK 格式转换)。 |
Web Crypto典型使用场景
1. 计算文件哈希(如校验文件完整性)
javascript
async function calculateFileHash(file) {
const buffer = await file.arrayBuffer();
const hash = await crypto.subtle.digest("SHA-256", buffer);
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
// 使用示例
const fileInput = document.querySelector("input[type=file]");
fileInput.addEventListener("change", async (e) => {
const file = e.target.files[0];
const hash = await calculateFileHash(file);
console.log("SHA-256:", hash);
});
2. AES-GCM 加密数据
javascript
async function encryptData(plainText, key) {
const encoded = new TextEncoder().encode(plainText);
const iv = crypto.getRandomValues(new Uint8Array(12)); // 初始化向量
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
encoded
);
return { encrypted, iv };
}
// 生成 AES 密钥
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true, // 是否可导出
["encrypt", "decrypt"]
);
3. 非对称加密(RSA-OAEP)
javascript
// 生成 RSA 密钥对
const { publicKey, privateKey } = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
// 加密数据
const encrypted = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
publicKey,
new TextEncoder().encode("Secret Message")
);
// 解密数据
const decrypted = await crypto.subtle.decrypt(
{ name: "RSA-OAEP" },
privateKey,
encrypted
);
console.log(new TextDecoder().decode(decrypted)); // "Secret Message"
Web Crypto密钥管理
1. 导出/导入密钥(JWK 格式)
javascript
// 导出公钥为 JWK 格式
const jwkPublicKey = await crypto.subtle.exportKey("jwk", publicKey);
// 导入 JWK 格式密钥
const importedKey = await crypto.subtle.importKey(
"jwk",
jwkPublicKey,
{ name: "RSA-OAEP", hash: "SHA-256" },
true,
["encrypt"]
);
2. 密钥派生(PBKDF2)
javascript
const password = "user-password";
const salt = crypto.getRandomValues(new Uint8Array(16));
// 从密码派生 AES 密钥
const baseKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
{ name: "PBKDF2" },
false,
["deriveKey"]
);
const derivedKey = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
baseKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
Web Crypto安全注意事项
-
密钥保护
- 避免硬编码密钥,优先使用密钥管理系统(如 KMS)或安全存储。
-
算法选择
- 使用现代算法(如 AES-GCM、RSA-OAEP、SHA-256),避免已弃用算法(如 MD5、SHA-1)。
-
初始化向量(IV)
- IV 必须唯一且随机,同一密钥下不可重复使用。
-
错误处理
- 加密操作可能抛出异常(如密钥不匹配),需用
try/catch
捕获。
- 加密操作可能抛出异常(如密钥不匹配),需用
Web Crypto与其他库的对比
特性 | Web Crypto API | CryptoJS |
---|---|---|
实现方式 | 浏览器原生支持 | 第三方 JavaScript 库 |
性能 | 更高(底层优化) | 较低(纯 JS 实现) |
安全性 | 密钥不可直接访问 | 密钥可能暴露给 JS 代码 |
算法支持 | 支持现代算法(如 AES-GCM) | 支持传统算法(如 AES-CBC) |
使用场景 | 需安全上下文的 Web 应用 | 旧浏览器或非安全环境 |
Web Crypto浏览器兼容性
-
现代浏览器:Chrome 37+、Firefox 34+、Edge 79+、Safari 11+ 均支持。
-
Polyfill:不推荐(无法实现真正的安全性),优先要求用户升级浏览器。
Web Crypto总结
Web Crypto API 是浏览器端安全操作的黄金标准,适合以下场景:
-
客户端加密敏感数据(如密码、个人信息)。
-
文件哈希校验、数字签名。
-
端到端加密(E2EE)应用的开发。
-
与后端配合实现信封加密(Envelope Encryption)。
通过合理使用 Web Crypto API,开发者可以在不依赖后端的情况下,提升前端应用的安全性,同时避免传统 JS 加密库的潜在风险。
对称加密和非对称加密的详细对比解析
对称加密 vs 非对称加密对比表
特性 | 对称加密 | 非对称加密 |
---|---|---|
定义 | 使用相同密钥进行加密和解密 | 使用公钥加密、私钥解密(或私钥签名、公钥验证) |
密钥数量 | 单一密钥 | 一对密钥(公钥 + 私钥) |
典型算法 | AES、DES、3DES、ChaCha20 | RSA、ECC(椭圆曲线)、ElGamal、DSA |
速度 | ⚡ 快(适合大数据量) | 🐢 慢(比对称加密慢100-1000倍) |
密钥管理 | 🔑 密钥分发困难(需安全通道传输) | 🔒 公钥可公开,私钥保密(无需传输私钥) |
安全性基础 | 密钥的保密性 | 数学难题(如大数分解、椭圆曲线离散对数) |
主要用途 | 数据加密 | 密钥交换、数字签名、身份认证 |
对称加密详解
优点
-
高性能:加密/解密速度快(AES可达数GB/s)
-
资源消耗低:适合IoT设备、移动端等资源受限场景
-
算法成熟:AES-GCM等通过NIST认证,安全性高
缺点
-
密钥分发难题:需要安全通道传输密钥
-
密钥管理复杂:每对通信需要独立密钥(N²增长)
-
无身份验证:需配合其他机制实现完整性和认证
使用场景
-
数据库字段加密(如信用卡号)
-
HTTPS连接的数据加密阶段
-
文件加密(ZIP/RAR压缩包密码)
-
磁盘加密(BitLocker、LUKS)
非对称加密详解
优点
-
安全密钥交换:通过公钥加密实现安全通信(如RSA-KEM)
-
数字签名:可验证数据来源和完整性(如SSL证书)
-
身份认证:私钥持有者身份可被验证(如SSH登录)
缺点
-
性能瓶颈:不适合加密大数据(通常只用于密钥/小数据)
-
密钥长度长:同等安全强度下密钥比对称加密长(RSA-2048 ≈ AES-128)
-
实现复杂:易因参数误用导致漏洞(如选择错误的填充模式)
使用场景
-
SSL/TLS握手中的密钥协商
-
数字签名(代码签名、文档签名)
-
加密货币钱包地址生成
-
PGP电子邮件加密
-
SSH免密登录认证
核心算法对比
算法 | 类型 | 密钥长度 | 典型应用场景 |
---|---|---|---|
AES-256-GCM | 对称加密 | 256位 | 加密数据库内容、HTTPS数据传输 |
ChaCha20-Poly1305 | 对称加密 | 256位 | 移动端加密(替代AES)、QUIC协议 |
RSA-2048-OAEP | 非对称加密 | 2048位(等效112位安全) | 加密对称密钥、数字证书签发 |
ECC-secp256k1 | 非对称加密 | 256位(等效3072位RSA) | 比特币地址生成、物联网设备认证 |
EdDSA (Ed25519) | 非对称签名 | 256位 | SSH密钥对、区块链交易签名 |
混合加密系统(最佳实践)
实际应用中常结合两者优势:
密钥交换阶段 :用非对称加密安全传输对称密钥
**数据加密阶段:**用对称加密处理实际数据(如HTTPS中的AES)
**数字签名:**用非对称加密验证数据来源(如SSL证书链)
策略选择
需求 | 推荐方案 |
---|---|
加密大量数据 | AES-256-GCM + HKDF密钥派生 |
安全传输密钥 | RSA-3072-OAEP 或 ECDH + X25519 |
数字签名 | ECDSA with SHA-384 或 EdDSA |
资源受限设备 | ChaCha20-Poly1305 或 AES-128-CTR |
长期数据存储 | AES-256 + 定期密钥轮换策略 |