You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Web Crypto API无法导入OpenSSL生成的密钥,求示例解决方法

解决Web Crypto API导入OpenSSL生成的RSA密钥报错问题

你遇到的问题核心是密钥格式不匹配:OpenSSL生成的是带标记的PEM格式密钥,而你直接用raw格式去导入,Web Crypto API根本不认这种格式。下面给你详细的解决步骤和可运行的示例代码:

1. 先搞懂格式差异

  • OpenSSL生成的公钥是PEM格式(开头是-----BEGIN PUBLIC KEY-----,结尾是-----END PUBLIC KEY-----),本质是Base64编码后的**SPKI(SubjectPublicKeyInfo)**格式
  • Web Crypto API导入RSA公钥必须用spki格式,私钥则用pkcs8格式,raw格式只适用于对称密钥或特定的非对称密钥原始字节,完全不适合PEM密钥

2. 公钥导入的完整代码

首先写个工具函数,把PEM字符串转换成Web Crypto能识别的ArrayBuffer:

function pemToArrayBuffer(pem) {
  // 去掉PEM的头尾标记和多余换行
  const cleanB64 = pem.replace(/-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----|\n/g, '');
  // Base64解码成二进制,再转成ArrayBuffer
  const binaryStr = atob(cleanB64);
  const byteArray = new Uint8Array(binaryStr.length);
  for (let i = 0; i < binaryStr.length; i++) {
    byteArray[i] = binaryStr.charCodeAt(i);
  }
  return byteArray.buffer;
}

然后用这个函数处理你的公钥,再导入:

const algConfig = { name: "RSA-OAEP", hash: {name: "SHA-256"} };
// 假设myPublicKey是你持有的PEM格式公钥字符串
const publicKeyBuffer = pemToArrayBuffer(myPublicKey);

window.crypto.subtle.importKey(
  'spki', // 这里必须用spki格式,替换之前的raw
  publicKeyBuffer,
  algConfig,
  false,
  ['encrypt'] // 公钥只赋予加密权限
)
.then(publicKey => {
  console.log('公钥导入成功!', publicKey);
  // 这里可以开始执行加密操作了
})
.catch(err => {
  console.error('导入公钥失败:', err);
});

3. 用户私钥的导入方法

如果用户传入的是OpenSSL生成的PEM私钥(比如mykey.pem),处理逻辑类似,只是格式换成pkcs8

function privatePemToArrayBuffer(pem) {
  const cleanB64 = pem.replace(/-----BEGIN RSA PRIVATE KEY-----|-----END RSA PRIVATE KEY-----|\n/g, '');
  const binaryStr = atob(cleanB64);
  const byteArray = new Uint8Array(binaryStr.length);
  for (let i = 0; i < binaryStr.length; i++) {
    byteArray[i] = binaryStr.charCodeAt(i);
  }
  return byteArray.buffer;
}

// 导入私钥示例
const privateKeyBuffer = privatePemToArrayBuffer(userPrivateKey);
window.crypto.subtle.importKey(
  'pkcs8', // 私钥用pkcs8格式
  privateKeyBuffer,
  algConfig,
  false,
  ['decrypt'] // 私钥只赋予解密权限
)
.then(privateKey => {
  console.log('私钥导入成功!', privateKey);
  // 这里可以开始执行解密操作了
})
.catch(err => {
  console.error('导入私钥失败:', err);
});

4. 完整加解密测试流程

可以用下面的代码验证整个流程是否正常:

// 加密函数
async function encryptWithPublicKey(publicKey, plainText) {
  const encoder = new TextEncoder();
  const encodedText = encoder.encode(plainText);
  return window.crypto.subtle.encrypt(algConfig, publicKey, encodedText);
}

// 解密函数
async function decryptWithPrivateKey(privateKey, encryptedBuffer) {
  const decryptedBuffer = await window.crypto.subtle.decrypt(algConfig, privateKey, encryptedBuffer);
  const decoder = new TextDecoder();
  return decoder.decode(decryptedBuffer);
}

// 测试流程
(async function() {
  try {
    // 导入密钥
    const publicKey = await window.crypto.subtle.importKey('spki', pemToArrayBuffer(myPublicKey), algConfig, false, ['encrypt']);
    const privateKey = await window.crypto.subtle.importKey('pkcs8', privatePemToArrayBuffer(userPrivateKey), algConfig, false, ['decrypt']);
    
    // 测试加解密
    const originalText = '测试Web Crypto RSA-OAEP加解密功能';
    const encryptedData = await encryptWithPublicKey(publicKey, originalText);
    const decryptedText = await decryptWithPrivateKey(privateKey, encryptedData);
    
    console.log('原始文本:', originalText);
    console.log('解密结果:', decryptedText);
    console.log('加解密是否一致:', originalText === decryptedText);
  } catch (err) {
    console.error('流程出错:', err);
  }
})();

注意事项

  • 务必确保用户传入的私钥是完整的PEM格式字符串,头尾标记不能少
  • RSA-OAEP有明文长度限制:4096位密钥搭配SHA-256的话,最大明文长度是446字节,超过会报错
  • 生产环境里要注意私钥的安全性,不要在前端赋予私钥不必要的操作权限

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

火山引擎 最新活动