在 JavaScript 中,公私钥的保存位置取决于生成方式和用途。

1. 使用 Web Crypto API 生成密钥

        
const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSA-PSS",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  false, // 私钥不可导出
  ["sign", "verify"]
);
        
      

默认情况

  • privateKey:只存在于当前页面内存中的 CryptoKey 对象里

  • publicKey:也存在内存中,可导出后发送给服务器

  • 页面刷新或关闭后:内存中的密钥会丢失

        
const { privateKey, publicKey } = keyPair;
        
      

2. 持久化保存到 IndexedDB

CryptoKey 可以保存到 IndexedDB:

        
const dbRequest = indexedDB.open("keyDB", 1);

dbRequest.onupgradeneeded = () => {
  dbRequest.result.createObjectStore("keys");
};

dbRequest.onsuccess = () => {
  const db = dbRequest.result;
  const tx = db.transaction("keys", "readwrite");

  tx.objectStore("keys").put(keyPair.privateKey, "privateKey");
  tx.objectStore("keys").put(keyPair.publicKey, "publicKey");
};
        
      

一般建议:

  • 私钥保存为不可导出密钥:extractable: false

  • 私钥可保存到 IndexedDB,但只能被同源页面使用

  • 不要把私钥直接转成字符串放进 localStorage

  • 不要把私钥放进 Cookie、URL 或发送到服务器

3. 导出为 JWK 或 PEM

如果生成密钥时允许导出:

        
const jwk = await crypto.subtle.exportKey("jwk", privateKey);
        
      

此时私钥可能被保存到:

  • localStorage

  • sessionStorage

  • IndexedDB

  • 浏览器扩展存储

  • 服务器端

但不建议把私钥保存到 localStorage,因为一旦发生 XSS,脚本可能直接读取它。

4. 如果使用 WebAuthn / Passkey

WebAuthn 的私钥通常保存在:

  • 手机或电脑的安全硬件

  • 操作系统凭据管理器

  • 浏览器关联的 Passkey 存储

  • 外部安全密钥

JavaScript 无法直接读取 WebAuthn 私钥。JS 只能调用:

        
navigator.credentials.create()
navigator.credentials.get()
        
      

服务器保存的是公钥和凭据 ID。

总结

场景

私钥保存位置

JS 变量

当前页面内存

Web Crypto + IndexedDB

浏览器 IndexedDB

WebAuthn / Passkey

系统或硬件安全存储,JS 不可读取

导出为 JWK/PEM

由应用自行保存,风险较高

公钥

通常发送并保存到服务器

如果是浏览器端登录认证,优先考虑 WebAuthn/Passkey,不要在前端自行保存可导出的私钥。