混合加密时公钥硬编码到前端安全吗?加密后的数据怎么传给后端?

涵博的笔记 阅读 47

我在用Vue做文件上传功能时想用混合加密保护数据,但卡在非对称密钥的传输上了。现在用RSA加密AES密钥,但直接把公钥写死在前端代码里(如下),这样会不会被轻易抓包获取公钥?



  



import CryptoJS from 'crypto-js'
const RSA_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----...'; // 这个公钥硬编码有问题吗?

export default {
  methods: {
    uploadFile(file) {
      const aesKey = CryptoJS.lib.WordArray.random(16);
      const encryptedData = CryptoJS.AES.encrypt(file, aesKey).toString();
      // 这里用RSA加密AES密钥再传给后端
      const encryptedKey = RSAEncrypt(aesKey, RSA_PUBLIC_KEY); // 这个方法还没实现
      // ...
    }
  }
}

另外试过把公钥放在接口返回,但测试时发现每次请求都要多一次API调用感觉不太对。还有看到别人用Base64编码公钥,但直接放明文传输公钥本身是不是就有风险?如果后端要求用PKCS#1格式,我的公钥格式对吗?

我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
打工人燕丽
你的问题很常见,特别是前端加密场景。我们一步一步来。

---

### 第一步:前端硬编码公钥到底安不安全?

**结论:公钥本身是可以硬编码的,它是公开的,不会泄露加密数据。**
RSA 公钥本来就是用来对外公开的,用于加密数据,不涉及私密信息,所以即使别人能抓包看到公钥内容,也不会导致你的加密被破解。

不过要注意几点:

1. **确保你使用的加密库是安全且无漏洞的**,比如 crypto-js 本身并不支持 RSA 加密(你用的是 RSAEncrypt,这个方法你得自己实现或引入别的库)。
2. **确保公钥格式正确**,比如你提到 PKCS#1,那需要确认你生成的公钥是否是这个格式。PKCS#1 一般是这样的:

-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----


而标准的 PEM 格式公钥一般是:

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----


后端要看的是哪种格式,你要匹配好,否则会报错。

---

### 第二步:如何安全地在前端使用 RSA 加密 AES 密钥

crypto-js 本身不支持 RSA 加密,你可以使用 [node-rsa](https://www.npmjs.com/package/node-rsa) 或者 [encryptlong](https://www.npmjs.com/package/encryptlong) 来处理长文本(因为 AES 密钥较短,一般可以直接加密)。

#### 推荐做法:

1. 在前端硬编码 RSA 公钥(安全)
2. 生成随机 AES 密钥
3. 用 AES 加密文件内容
4. 用 RSA 公钥加密 AES 密钥
5. 把加密后的文件内容 + 加密后的 AES 密钥一起发给后端
6. 后端用私钥解密 AES 密钥,再用 AES 解密文件内容

---

### 第三步:实现代码示例(使用 node-rsa)

先安装:

npm install node-rsa


然后代码大概是这样:

import { JSEncrypt } from 'encryptlong'; // 或者使用 node-rsa
import CryptoJS from 'crypto-js';

const RSA_PUBLIC_KEY = -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuQLSU7Z...
-----END PUBLIC KEY-----
;

export default {
methods: {
uploadFile(file) {
// 1. 生成随机 AES 密钥
const aesKey = CryptoJS.lib.WordArray.random(16); // 16字节 = 128位

// 2. 用 AES 加密文件内容(假设文件是文本)
const encryptedData = CryptoJS.AES.encrypt(file, aesKey).toString();

// 3. 使用 RSA 加密 AES 密钥(因为密钥较短,可以用 RSA 加密)
const encryptor = new JSEncrypt();
encryptor.setPublicKey(RSA_PUBLIC_KEY);
const encryptedKey = encryptor.encrypt(aesKey.toString()); // 加密后的密钥

// 4. 发送到后端(比如用 axios 或 fetch)
axios.post('/upload', {
data: encryptedData,
key: encryptedKey
});
}
}
}


---

### 第四步:后端怎么解密

假设你用的是 Node.js + Express,可以这样做:

const fs = require('fs');
const { JSEncrypt } = require('encryptlong');
const CryptoJS = require('crypto-js');

// 假设你的私钥是这样的
const RSA_PRIVATE_KEY = fs.readFileSync('private.pem', 'utf8');

app.post('/upload', (req, res) => {
const { data, key } = req.body;

// 1. 用私钥解密 AES 密钥
const decryptor = new JSEncrypt();
decryptor.setPrivateKey(RSA_PRIVATE_KEY);
const decryptedKey = decryptor.decrypt(key); // 得到明文密钥

// 2. 用 AES 解密数据
const bytes = CryptoJS.AES.decrypt(data, decryptedKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);

// 3. 保存 originalText 到文件或其他处理
});


---

### 第五步:关于性能和调用次数

你说“每次上传都得先调接口获取公钥有点麻烦”,这确实是权衡问题。

- 如果你是 SPA,**可以在登录或初始化时一次性获取公钥**存到内存里,不需要每次请求都获取。
- 另一个方法是让后端把公钥写进 HTML 页面的