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

如何用JavaScript Crypto API为多公钥加密数据?RSA-OAEP可行吗?

多公钥加密的可行方案(替代RSA-OAEP直接多密钥加密)

好问题!先直接给结论:Web Crypto API的RSA-OAEP确实无法直接用多个公钥加密同一份数据——你写的那段代码里把ArrayOfPublicKeys传给encrypt方法会直接报错,因为这个API只接受单个CryptoKey对象作为密钥参数,不支持数组。

不过我们可以用「对称加密+多公钥加密对称密钥」的经典方案来实现需求,这也是业界处理多接收方加密的标准做法,具体逻辑如下:

核心思路

  1. 生成一个随机的对称加密密钥(比如AES-GCM,效率远高于RSA)
  2. 用这个对称密钥加密原始数据(只需要加密一次)
  3. 遍历每个接收方的公钥,用RSA-OAEP分别加密这个对称密钥(每个公钥对应一份加密后的对称密钥)
  4. 把「加密后的原始数据」+「所有公钥加密后的对称密钥集合」一起发给接收方
  5. 每个接收方用自己的私钥解密对应的对称密钥,再用该密钥解密原始数据

完整测试代码

async function encryptForMultipleRecipients(publicKeys, rawData) {
  // 1. 生成随机AES-GCM对称密钥
  const symmetricKey = await window.crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true, // 可导出(后续需要用公钥加密它)
    ["encrypt", "decrypt"]
  );

  // 2. 用对称密钥加密原始数据
  const iv = window.crypto.getRandomValues(new Uint8Array(12)); // AES-GCM标准IV长度
  const encryptedData = await window.crypto.subtle.encrypt(
    { name: "AES-GCM", iv: iv },
    symmetricKey,
    rawData
  );

  // 3. 导出对称密钥为原始二进制,方便用RSA加密
  const exportedSymmetricKey = await window.crypto.subtle.exportKey(
    "raw",
    symmetricKey
  );

  // 4. 用每个公钥分别加密对称密钥
  const encryptedSymmetricKeys = [];
  for (const pubKey of publicKeys) {
    const encryptedKey = await window.crypto.subtle.encrypt(
      { name: "RSA-OAEP" },
      pubKey,
      exportedSymmetricKey
    );
    encryptedSymmetricKeys.push(encryptedKey);
  }

  // 返回所有需要传输的数据:IV、加密后的原始数据、每个公钥对应的加密对称密钥
  return {
    iv: iv,
    encryptedData: encryptedData,
    encryptedSymmetricKeys: encryptedSymmetricKeys
  };
}

async function decryptWithPrivateKey(privateKey, encryptedPackage, keyIndex) {
  // 1. 用私钥解密对应的对称密钥
  const encryptedSymmetricKey = encryptedPackage.encryptedSymmetricKeys[keyIndex];
  const exportedSymmetricKey = await window.crypto.subtle.decrypt(
    { name: "RSA-OAEP" },
    privateKey,
    encryptedSymmetricKey
  );

  // 2. 导入对称密钥
  const symmetricKey = await window.crypto.subtle.importKey(
    "raw",
    exportedSymmetricKey,
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );

  // 3. 解密原始数据
  const decryptedData = await window.crypto.subtle.decrypt(
    { name: "AES-GCM", iv: encryptedPackage.iv },
    symmetricKey,
    encryptedPackage.encryptedData
  );

  return decryptedData;
}

// ---------------------- 测试示例 ----------------------
(async () => {
  // 生成两个测试用的RSA密钥对
  const keyPair1 = await window.crypto.subtle.generateKey(
    { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" },
    true,
    ["encrypt", "decrypt"]
  );
  const keyPair2 = await window.crypto.subtle.generateKey(
    { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" },
    true,
    ["encrypt", "decrypt"]
  );

  // 原始数据(转为ArrayBuffer)
  const rawData = new TextEncoder().encode("Hello, multiple recipients!");

  // 用两个公钥加密数据
  const encryptedPackage = await encryptForMultipleRecipients(
    [keyPair1.publicKey, keyPair2.publicKey],
    rawData
  );

  // 测试第一个私钥解密
  const decrypted1 = await decryptWithPrivateKey(keyPair1.privateKey, encryptedPackage, 0);
  console.log("解密结果1:", new TextDecoder().decode(decrypted1));

  // 测试第二个私钥解密
  const decrypted2 = await decryptWithPrivateKey(keyPair2.privateKey, encryptedPackage, 1);
  console.log("解密结果2:", new TextDecoder().decode(decrypted2));
})();

为什么RSA-OAEP不支持多公钥直接加密?

RSA-OAEP的加密流程是基于单个公钥的数学运算:它会用公钥的模数和指数对数据(或密钥)进行加密,每个公钥的模数/指数都不同,无法同时适配多个公钥进行一次加密操作。Web Crypto API的设计也严格遵循了这一数学特性,所以只接受单个密钥参数。

内容的提问来源于stack exchange,提问作者Dr.Jack Millan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:51:12