前端用RSA加密时,私钥怎么安全传给后端不被窃取?

轩辕玉丹 阅读 104

我在用RSA加密用户密码时遇到个难题,前端生成密钥对后,必须把私钥发给后端解密,但这样私钥不就暴露在请求里了吗?比如这段代码:


const forge = require('node-forge');
const keypair = forge.pki.rsa.generateKeyPair();
const privateKey = keypair.privateKey;

// 尝试加密密码
const encrypted = privateKey.encrypt('userPassword');

// 发现需要把私钥传给后端
fetch('/api/submit', {
  method: 'POST',
  body: JSON.stringify({ privateKey: privateKey.toString(), encrypted })
});

测试时发现这样传输私钥太危险了,但如果不传私钥,后端又无法解密数据。我试过用AES包裹RSA,但前端没有安全保存私钥的办法,感觉陷入死循环了…

我来解答 赞 19 收藏
二维码
手机扫码查看
2 条解答
百里清梅
我之前踩过这个坑,真不是你想象的那样搞的——RSA根本不需要前端生成密钥对再把私钥发给后端,这种设计本身就是反模式。

你想想,如果私钥得从浏览器传出去,那它早就暴露在中间人手里了,连加密的意义都没了。RSA在前端用,一定是公钥加密、私钥解密,而私钥必须死死攥在后端,从来不会也不应该出现在前端。

正确做法是:

后端先生成RSA密钥对,把公钥通过接口(比如 /api/rsa/public-key)返回给前端,前端拿到公钥后用来加密密码,再把加密后的密文发回后端,后端用自己的私钥解密。全程私钥不离后端,公钥随便传。

代码大概长这样:

后端(以 Node.js 为例)先生成一次密钥对,存在内存或配置里(别存数据库里明文存啊):
const forge = require('node-forge');
const { publicKey, privateKey } = forge.pki.rsa.generateKeyPair({ bits: 2048 });

// 提供公钥接口
app.get('/api/rsa/public-key', (req, res) => {
res.json({
modulus: publicKey.n.toString(16),
exponent: publicKey.e.toString(16)
});
});

// 接收加密密码
app.post('/api/login', (req, res) => {
const { encryptedPasswordHex } = req.body;
const encryptedBytes = Buffer.from(encryptedPasswordHex, 'hex');
const decrypted = privateKey.decrypt(encryptedBytes);
// 后续验证密码...
});


前端拿到公钥参数后,用 forge 构造公钥对象来加密:
const forge = require('node-forge');

async function getPublicKey() {
const res = await fetch('/api/rsa/public-key');
const { modulus, exponent } = await res.json();
const publicKey = forge.pki.setRsaPublicKey(
new forge.jsbn.BigInteger(modulus, 16),
new forge.jsbn.BigInteger(exponent, 16)
);
return publicKey;
}

async function login() {
const publicKey = await getPublicKey();
const encrypted = publicKey.encrypt('userPassword');
const encryptedHex = forge.util.bytesToHex(encrypted);

await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ encryptedPasswordHex: encryptedHex })
});
}


注意几点:
- 公钥本身是明文传输没问题,它本来就是公开的
- 前端每次请求前拿一次公钥也可以,但更常见的是后端统一下发一次,前端缓存
- 如果担心公钥被替换(比如中间人篡改),那得上 HTTPS,别想在纯 HTTP 上靠自己实现“更安全”

你原来的思路错在把“密钥生成”和“密钥分发”混在一起了——RSA 的密钥生成必须在解密方(后端)做,加密方(前端)只配用公钥。这就像你寄信,信封上的地址是公开的(公钥),但只有收信人手里的钥匙能开锁(私钥),你不可能把钥匙贴在信封上寄出去。

再补充一句:真要高安全场景,别自己造轮子,直接上 TLS 握手那一套(也就是 HTTPS),RSA 只是它底层用的工具之一。
点赞 4
2026-02-25 20:04
Tr° 智营
你这做法完全搞反了RSA的使用方式。RSA是公钥加密私钥解密,前端应该用公钥加密数据,后端用私钥解密。私钥绝对不能传输,也不能生成在前端。

正确流程应该是:
后端提前生成好RSA密钥对,保存私钥到文件/环境变量
前端登录时请求公钥
前端用公钥加密密码
后端收到加密数据后用私钥解密

补充两点注意事项:
公钥加密的数据只能私钥解密,中间就算被截获也没用
每次加密应该生成随机密钥,用公钥加密这个密钥,再用AES加密数据

给个简单示例:
async function login() {
const publicKey = await fetch('/api/getPublicKey').then(res => res.json());

const encryptor = new RSAEncrypt({ key: publicKey });
const encryptedPass = encryptor.encrypt('userPassword');

fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ encryptedPass })
});
}


后端用crypto模块处理解密:
const { privateDecrypt } = require('crypto');
app.post('/api/submit', (req, res) => {
const buffer = Buffer.from(req.body.encryptedPass, 'base64');
const decrypted = privateDecrypt(privateKey, buffer);
console.log(decrypted.toString()); // 得到原始密码
});
点赞 9
2026-02-06 09:02