Web Crypto API AES加密后解密失败,密文看起来不对劲?

シ熙苒 阅读 56

我用Web Crypto API写了个AES加密函数,但解密时总返回错误。明明密钥和参数都一致,加密后的密文看起来全是乱码,是不是哪里没处理对?

代码是这样写的:


async function encrypt(text) {
  const encoder = new TextEncoder();
  const key = await crypto.subtle.generateKey(
    { name: "AES-CBC", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
  const iv = crypto.getRandomValues(new Uint8Array(16));
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-CBC", iv },
    key,
    encoder.encode(text)
  );
  return btoa(String.fromCharCode.apply(null, new Uint8Array(encrypted)));
}

测试时发现解密函数能拿到密钥,但解密结果全是乱码。尝试过把加密后的ArrayBuffer转成Base64,会不会是编码方式的问题?或者初始化向量没传对?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
UI雨萱
UI雨萱 Lv1
先检查一下你的编码转换逻辑,问题就出在那行 btoa(String.fromCharCode.apply(null, new Uint8Array(encrypted))) 上。

String.fromCharCode 对二进制数据处理会出问题,特别是当字节值超过 127 时会被错误解码,导致密文损坏。你加密出来的是 ArrayBuffer,得老老实实用正确的方式转 Base64。

另外你还漏了个大坑:密钥和 IV 没保存!每次加密都生成新密钥和随机 IV,解密时怎么对得上?你得把用到的 IV 和 key(或导出的 key material)一起存下来或者传过去。

下面是改好的加密函数:

async function encrypt(text) {
const encoder = new TextEncoder();
const key = await crypto.subtle.generateKey(
{ name: "AES-CBC", length: 256 },
true,
["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(16));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-CBC", iv: iv },
key,
encoder.encode(text)
);

// 正确的ArrayBuffer转Base64
const uint8Array = new Uint8Array(encrypted);
let binary = '';
for (let i = 0; i < uint8Array.length; i++) {
binary += String.fromCharCode(uint8Array[i]);
}
const ciphertext = btoa(binary);

// 导出密钥以便后续解密(实际项目建议用更安全方式管理)
const exportedKey = await crypto.subtle.exportKey('raw', key);
const keyData = Array.from(new Uint8Array(exportedKey));
const keyBase64 = btoa(String.fromCharCode.apply(null, keyData));

// 把密文、IV、密钥一起返回
return {
ciphertext,
iv: Array.from(iv),
key: keyBase64
};
}


解密的时候记得把 IV 转回 Uint8Array,密钥用 importKey 还原。不然还是解不出来。

说白了就是两个点:别乱搞字符编码,Binary数据要原样转;加解密三要素——算法、密钥、IV 必须完全一致。你现在至少丢了两个。
点赞 2
2026-02-11 12:25
长孙杰森
问题在于你生成的密钥和IV是随机的,加密和解密时用的不是同一个。你需要把生成的密钥和IV保存下来,在解密时传入相同的值。

另外,你的加密结果转Base64的方式没问题,但建议用更现代的方式处理ArrayBuffer。

async function encrypt(text, key, iv) {
const encoder = new TextEncoder();
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-CBC", iv },
key,
encoder.encode(text)
);
return btoa(String.fromCharCode.apply(null, new Uint8Array(encrypted)));
}

// 使用时先生成key和iv,然后传给encrypt和decrypt
(async () => {
const key = await crypto.subtle.generateKey({ name: "AES-CBC", length: 256 }, true, ["encrypt", "decrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(16));

const encrypted = await encrypt('Hello World', key, iv);
console.log(encrypted);
})();
点赞 7
2026-02-02 14:01