在 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);
此时私钥可能被保存到:
localStoragesessionStorageIndexedDB
浏览器扩展存储
服务器端
但不建议把私钥保存到 localStorage,因为一旦发生 XSS,脚本可能直接读取它。
4. 如果使用 WebAuthn / Passkey
WebAuthn 的私钥通常保存在:
手机或电脑的安全硬件
操作系统凭据管理器
浏览器关联的 Passkey 存储
外部安全密钥
JavaScript 无法直接读取 WebAuthn 私钥。JS 只能调用:
navigator.credentials.create()
navigator.credentials.get()
服务器保存的是公钥和凭据 ID。
// 创建凭据(注册)
const credential = await navigator.credentials.create({
publicKey: {
challenge: crypto.getRandomValues(new Uint8Array(32)),
rp: { name: "示例网站", id: location.hostname },
user: {
id: new TextEncoder().encode("user-123"),
name: "alice@example.com",
displayName: "Alice"
},
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256
{ type: "public-key", alg: -257 } // RS256
],
authenticatorSelection: {
userVerification: "preferred"
},
timeout: 60000,
attestation: "none"
}
});
console.log(credential);
// 获取凭据(登录)
const credential = await navigator.credentials.get({
publicKey: {
challenge: crypto.getRandomValues(new Uint8Array(32)),
rpId: location.hostname,
userVerification: "preferred",
timeout: 60000
}
});
console.log(credential);总结
场景 | 私钥保存位置 |
|---|---|
JS 变量 | 当前页面内存 |
Web Crypto + IndexedDB | 浏览器 IndexedDB |
WebAuthn / Passkey | 系统或硬件安全存储,JS 不可读取 |
导出为 JWK/PEM | 由应用自行保存,风险较高 |
公钥 | 通常发送并保存到服务器 |
如果是浏览器端登录认证,优先考虑 WebAuthn/Passkey,不要在前端自行保存可导出的私钥。