You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用RSA-OAEP密钥对调用SubtleCrypto解密时出现DOMException报错

问题原因

解密失败的核心原因是密文的二进制数据和UTF-8字符串的转换过程发生了不可逆损坏:

  • subtle.encrypt接口返回的加密结果是ArrayBuffer类型的原始二进制数据,其中包含大量不符合UTF-8编码规则的字节序列
  • 直接用TextDecoder('utf-8').decode()转换该二进制数据时,解码器会自动将非法字节替换为U+FFFD替换字符,原始密文数据被永久修改
  • 后续用TextEncoder.encode转回二进制时,得到的已经不是加密输出的原始密文,不符合RSA-OAEP解密的输入要求,因此抛出DOMException异常。该问题和哈希算法选型无关,切换SHA-1无法解决。
解决方案

密文属于非文本二进制数据,不能直接用UTF-8编解码转换,需要选择无损的二进制转可打印字符串的编码方式存储,比如Base64或十六进制编码。以下是修正后的可运行代码:

let encoder = new TextEncoder();
let decoder = new TextDecoder('utf-8');

// 工具函数:ArrayBuffer转Base64字符串
function arrayBufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary);
}

// 工具函数:Base64字符串转ArrayBuffer
function base64ToArrayBuffer(base64) {
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

// 生成密钥对
let keyPair = await window.crypto.subtle.generateKey(
    {
        name: "RSA-OAEP",
        modulusLength: 4096,
        publicExponent: new Uint8Array([1, 0, 1]),
        hash: "SHA-256"
    },
    true,
    ["encrypt", "decrypt"]
);

let publicKey = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
let privateKey = await crypto.subtle.exportKey('jwk', keyPair.privateKey);

// 加密字符串
let encodedSecret = encoder.encode("MYSECRETVALUE");
let pubcryptokey = await window.crypto.subtle.importKey(
    'jwk',
    publicKey,
    {
        name: "RSA-OAEP",
        hash: "SHA-256"
    },
    false,
    ["encrypt"]
);
let encrypted = await window.crypto.subtle.encrypt(
    {
        name: "RSA-OAEP"
    },
    pubcryptokey,
    encodedSecret
);
// 密文转Base64后再存储,避免UTF-8转换损坏
let ct = arrayBufferToBase64(encrypted);

// 解密字符串
// 先将Base64密文转回原始二进制数据
let encodedCiphertext = base64ToArrayBuffer(ct);
let privcryptokey = await window.crypto.subtle.importKey(
    'jwk',
    privateKey,
    {
        name: "RSA-OAEP",
        hash: "SHA-256"
    },
    false,
    ["decrypt"]
);
console.log("Before decrypt");
let decrypted = await window.crypto.subtle.decrypt(
    {
        name: "RSA-OAEP"
    },
    privcryptokey,
    encodedCiphertext
);
console.log("After decrypt");
let pt = decoder.decode(decrypted);

console.log(pt);

如果不使用Base64,也可以选择将二进制密文转换为十六进制字符串存储,核心原则是保证密文二进制数据在转换、存储过程中不会被修改。导出的JWK格式密钥本身是标准JSON字符串,可直接存储不需要额外编码。

内容的提问来源于stack exchange,提问作者IanMckay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 16:45:03