crypto.subtle.decrypt() 返回空 arraybuffer 是因参数或密钥不匹配:iv 长度错误(aes-gcm 要求 uint8array,推荐12字节)、key 未通过 importkey 转为 cryptokey 对象、密文未原样传入(如错误截取导致认证标签丢失),或 taglength 加解密不一致;必须严格按 derivekey+pbkdf2+salt+iterations≥100000 流程生成密钥,并确保 https/localhost 安全上下文。

纯前端用 SubtleCrypto 做加密,不是“输密码点一下就完事”,而是必须手动串联密钥派生、参数校验、字节视图转换这三步;漏掉任意一环,decrypt() 就会静默返回空 ArrayBuffer 或直接抛 DataError。
为什么 crypto.subtle.decrypt() 总是返回空 ArrayBuffer?
这不是浏览器 bug,而是参数或密钥没对上。最常见原因有三个:
-
iv长度不对(AES-GCM 要求Uint8Array,推荐 12 字节;传了 16 字节的 CBC iv 会失败) - 解密用的
key不是CryptoKey对象——比如直接把字符串"mykey"传进去,会报TypeError: The provided value is not of type '(ArrayBuffer or ArrayBufferView)' - 密文 ArrayBuffer 里混了 salt/iv/tag 但没按原样传入:比如把 base64 解码后手动截掉前 12 字节当 iv、剩下全当密文,而实际 AES-GCM 的认证标签(tag)在末尾,默认 16 字节;若
tagLength: 96,那 tag 就是 12 字节,截错就解不出
怎么安全地从密码生成 CryptoKey?
不能用 md5(password) 或 sha1(password) 直接当密钥——它们不是密钥派生函数,抗穷举能力极差。必须走 deriveKey() + PBKDF2 流程:
- 盐值(
salt)必须随机且唯一,每次加密都该重新生成并和密文一起存(比如拼在 base64 前面) -
iterations至少设为100000;低于这个数,短密码几秒就被暴力跑出 - 派生出的
key必须用importKey("raw", ...)转成可解密的CryptoKey,不能跳过这步直接传给decrypt() - 示例关键片段:
const keyMaterial = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), { name: "PBKDF2" }, false, ["deriveKey"]); const key = await crypto.subtle.deriveKey( { name: "PBKDF2", salt, iterations: 100000, hash: "SHA-256" }, keyMaterial, { name: "AES-GCM", length: 256 }, false, ["decrypt"] );
AES-GCM 加密后怎么组织数据才能安全解密?
别自己拼字符串。浏览器加密输出是单个 ArrayBuffer,结构为「密文|认证标签」,但规范不保证 tag 一定在末尾——所以最稳妥方式是:把整个 ArrayBuffer 和 iv、salt 分开序列化存储(例如用 JSON 存 {"iv": "...", "salt": "...", "ciphertextWithTag": "..."}),解密时再还原成对应视图:
-
iv和salt都得是Uint8Array,不能是 base64 字符串;解码后要用new Uint8Array(atob(...).split("").map(c => c.charCodeAt(0)))或更稳的Uint8Array.from(atob(...), c => c.charCodeAt(0)) - 加密时显式指定
tagLength: 128,解密时也必须传同样的值,否则可能抛DataError - 如果密文来自服务端,确认它没被 UTF-8 自动转义过——
ArrayBuffer被当字符串处理一次就会损坏
最容易被忽略的是上下文限制:SubtleCrypto 的多数方法只在 HTTPS 或 localhost 下可用;开发时用 http://127.0.0.1 可以,但 http://localhost 在某些旧版 Chrome 里会被禁用。别等上线才发现 decrypt 根本不触发。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











