仓库:travist/jsencrypt,底层封装 Tom Wu 的 jsbn 大数库,RSA 非对称加密前端库 ,浏览器、Node.js 都可用,兼容 OpenSSL 标准 PEM 密钥 默认填充:PKCS#1 v1.5 (
encrypt());新版支持 OAEP 填充encryptOAEP()
一、核心原理与业务流程
RSA 一对密钥:公钥 publicKey(公开)、私钥 privateKey(仅后端保存) ✅ 标准业务:前端公钥加密 → 传给后端 → 后端私钥解密
口诀:公钥加密,私钥解密;私钥签名,公钥验签 ❌ 严禁前端存放私钥!前端私钥等于裸奔,任何人F12都能拿到
密钥格式(重点,对接失败90%是密钥格式不对)
jsencrypt 识别标准 PEM:
- 公钥:
-----BEGIN PUBLIC KEY-----(PKCS#8) - 私钥:
-----BEGIN RSA PRIVATE KEY-----(PKCS#1)> > 注意:不要带多余空格换行、不要传 JWK、不要用 openssl 新格式 PKCS#8 私钥(BEGIN PRIVATE KEY),这个库不认!
二、安装 & 引入
1. CDN(浏览器直接用)
xml
<script src="[https://cdn.jsdelivr.net/npm/jsencrypt/bin/jsencrypt.min.js](https://cdn.jsdelivr.net/npm/jsencrypt/bin/jsencrypt.min.js)"></script>
全局变量:JSEncrypt
2. npm 方式
javascript
npm i jsencrypt
import { JSEncrypt } from 'jsencrypt'
// 或者 commonjs
const { JSEncrypt } = require('jsencrypt')
三、完整 API 清单
arduino
// 1. 实例化
const crypt = new JSEncrypt()
// 可选:实例化时指定密钥长度,用于前端生成密钥对(仅测试,生产不要前端生成密钥)
// const crypt = new JSEncrypt({default_key_size: 2048})
| 方法 | 作用 | 备注 |
|---|---|---|
| setPublicKey(pubKey) | 设置公钥,用于加密 | 传完整PEM字符串 |
| setPrivateKey(priKey) | 设置私钥,用于解密/签名 | 前端一般不用 |
| encrypt(plainText) | RSA加密,PKCS#1 v1.5 | 返回base64密文;超长返回false |
| encryptOAEP(plainText, hash) | OAEP加密(更安全) | hash默认sha1,推荐sha256 |
| decrypt(cipherBase64) | 解密,PKCS#1 v1.5 | 私钥必须正确 |
| sign(str, hashName) | 私钥签名 | hash:sha256/sha1/md5 |
| verify(str, signStr, hashName) | 公钥验签 | 返回布尔值 |
| getPublicKey() | 获取当前公钥PEM | |
| getPrivateKey() | 获取当前私钥PEM | |
| getKey() | 获取底层jsbn key对象 | 可拿n、e、d等大数 |
最简加密示例
vbnet
const crypt = new JSEncrypt()
crypt.setPublicKey(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs...
-----END PUBLIC KEY-----`)
const cipher = crypt.encrypt('123456')
console.log(cipher) // base64密文
解密示例(仅后端/本地调试用,禁止线上前端解密)
vbnet
const crypt = new JSEncrypt()
crypt.setPrivateKey(`-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQ...
-----END RSA PRIVATE KEY-----`)
const plain = crypt.decrypt(cipher)
签名验签(防篡改)
arduino
const crypt = new JSEncrypt()
crypt.setPrivateKey(priKey)
const sign = crypt.sign('hello', 'sha256') // 私钥签名
crypt.setPublicKey(pubKey)
const ok = crypt.verify('hello', sign, 'sha256') // 公钥验签
四、致命限制:RSA明文长度上限(高频踩坑)
PKCS#1 填充固定占用 11 字节开销
- 1024 bit RSA:最大明文字节 = 128 - 11 = 117字节
- 2048 bit RSA:最大明文字节 = 256 - 11 = 245字节
⚠️ 大坑:汉字是UTF-8,一个汉字占3字节! 比如2048密钥,最多只能传80个左右汉字,直接超长返回 false,不会抛异常,很容易漏判。
✅ 两种正确方案:
- 混合加密(生产推荐) :前端随机生成AES密钥,RSA加密AES密钥,AES加密业务长文本;后端RSA解出AES密钥,再AES解正文
- 分段RSA加密(不推荐,容易中文截断、兼容性差,仅老项目兼容)
ini
// 【不推荐】分段加密示例(仅了解,优先AES+RSA)
JSEncrypt.prototype.encryptLong = function(text) {
const k = this.getKey()
const max = ((k.n.bitLength()+7)>>3)-11
let res = ""
for(let i=0;i<text.length;i+=max){
const seg = text.substr(i,max)
res += k.encrypt(seg)
}
return res
}
五、安全边界(非常重要)
- jsencrypt 不能替代 HTTPS HTTPS是传输底层加密;RSA在这里只是应用层额外保护敏感字段(密码、手机号) ,抓包只能拿到密文,拿不到明文。HTTPS还是必须开。
- 前端加密只是防明文抓包,不是防逆向:公钥公开,JS代码可被逆向,RSA本身无法防重放攻击,业务层要加时间戳、随机nonce、签名校验。
- 密钥长度:生产禁止1024位,最低2048,推荐4096;1024已经不安全。
- 填充模式:PKCS#1 v1.5有历史漏洞;新项目优先
encryptOAEP。
六、常见报错 & 对接坑
encrypt返回false:明文超长、密钥格式错误、密钥首尾标记丢失、换行空格乱码- 后端解密失败:
-
后端用的是PKCS#8私钥,前端是PKCS#1;
-
后端填充模式不一致(Java默认有些是OAEP,jsencrypt默认PKCS1)
- 中文解密乱码:统一UTF-8,不要用GBK
- 密钥生成命令(openssl)
csharp
# 生成PKCS#1 RSA私钥(jsencrypt兼容)
openssl genrsa -out private.key 2048
# 从私钥导出公钥(PKCS#8,jsencrypt兼容)
openssl rsa -in private.key -pubout -out public.key
七、和其他加密库对比
| 库 | 算法 | 用途 |
|---|---|---|
| jsencrypt | RSA | 非对称,公钥加密、签名验签 |
| crypto-js | AES/DES/SHA | 对称加密、摘要 |
| jsrsasign | RSA/ECC | 功能最全,体积大,支持更多标准 |
八、生产标准架构(最佳实践)
- 后端启动生成RSA2048密钥对,私钥保存在服务端,公钥返回前端
- 前端:随机生成AES-256密钥
- 用RSA公钥加密AES密钥
- 用AES加密业务表单/长文本
- 请求传给后端:
rsaAesKey + aesData - 后端RSA解密得到AES密钥,AES解密正文> > 兼顾RSA密钥分发安全 + AES高速加密长文本,解决RSA长度限制问题