不能直接用官方jsencrypt,因其依赖window和浏览器BigInteger,在uni-app的App端(无window)、小程序(禁用BigInteger)、H5(PEM换行解析不一致)三端均会报错或静默失败。

直接用 jsencrypt 在 uni-app 里做 RSA 加密,不是不行,而是必须绕过三类环境差异:App 端无 window、小程序禁用 BigInteger、H5 和 App 的 PEM 解析行为不一致。纯靠 npm install jsencrypt + new JSEncrypt() 会当场报 JSEncrypt is not defined 或加密返回 false,尤其在真机调试时。
为什么不能直接 npm install jsencrypt
官方 jsencrypt 依赖全局 window 和浏览器原生 BigInteger 实现,在 uni-app 的 App 端(基于 WebView)和微信小程序中,window 不存在,BigInteger 无法加载或计算超时;H5 虽能跑,但若公钥含 Windows 风格换行(\r\n),setPublicKey() 会静默失败。你看到的“加密成功但后端解不出”,90% 是因为公钥格式没清洗、填充方式没对齐。
- App 端编译时
window未定义 → 报ReferenceError: window is not defined - 小程序基础库 crypto.subtle 为空 → 但
jsencrypt本身不依赖它,真正卡点是BigInteger的构造逻辑被沙箱拦截 - H5 下公钥若带多余空格或换行,
setPublicKey()不报错但内部解析失败 →encrypt()返回false,且无提示
怎么让 jsencrypt 在三端都 work
核心是替换掉原始 jsencrypt.js,用已适配 uni-app 的定制版(移除 window 依赖、重写 BigInteger 入口、强制 UTF-8 编码)。你不需要自己改源码,直接用社区验证过的轻量包(37KB,含 jsencrypt.js + rsa.js 封装 + TXT 密钥说明)即可。
- 把定制版
jsencrypt.js放进static/lib/目录,避免被 webpack 试图解析(否则 H5 可能报 module parse failed) - 在
main.js或工具模块里用uni.requireNativePlugin或require('./static/lib/jsencrypt.js')动态加载(小程序需useStrictMode: false) -
rsa.js中导出的rsaEncrypt(data, publicKey)已自动处理:去除 PEM 换行、转UTF-8字节数组、限制输入 ≤117 字节(PKCS#1 v1.5)、Base64 输出 - 别把整个表单对象传进去 ——
rsaEncrypt只接受字符串,中文需确保前端编码与后端解密时一致(推荐全程用encodeURIComponent包一层再加密)
公钥格式和长度限制必须手动对齐
后端用 OpenSSL 生成的公钥,常带 -----BEGIN PUBLIC KEY----- 头尾和 \n 换行,前端 setPublicKey() 会因格式敏感而失效;同时 RSA-1024 最多加密 117 字节明文(PKCS#1 v1.5),超长直接返回 false,不会抛异常。
- 公钥清洗:用
publicKey.replace(/-----[A-Z ]+-----|\r\n|\n/g, '')提纯 Base64 字符串,再传给setPublicKey() - 超长内容必须分段:密码、手机号等字段天然短于 117 字节,但若加密 JSON 字符串,务必先
JSON.stringify再检查new TextEncoder().encode(str).length - 后端若用
RSA/ECB/PKCS1Padding,前端jsencrypt默认即匹配;若后端用 OAEP(如node-forge的RSA-OAEP),则必须换库或改填充参数,jsencrypt不支持 OAEP
真机调试时密文和后端解不出?先离线验签
别急着改前端代码。拿一段固定明文(如 "123456"),用 OpenSSL 命令行在本地加密,和前端产出的密文比对 Base64 是否一致。不一致,说明前端公钥加载失败或编码不一致;一致却后端解不开,大概率是后端私钥格式(PKCS#1 vs PKCS#8)或填充模式没对上。
- OpenSSL 加密命令:
echo -n "123456" | openssl rsautl -encrypt -pubin -inkey public.pem -pkcs | base64 - 前端加密后打印
console.log(encryptedBase64),和命令行输出逐字符比对 - 如果前端加密结果每次都不一样(非确定性),说明你用了带随机盐的 OAEP ——
jsencrypt是确定性的,只用 PKCS#1 v1.5,这点必须和后端确认清楚
最易被忽略的是:密钥生成后没做格式归一化,以及把加密当万能盾牌——RSA 不解决 HTTPS 中间人攻击,也不防前端被 hook,它只保证“即使流量被截获,密码字段仍是密文”。上线前务必用真实设备连 Charles 抓包,确认 password 字段已变成一长串 Base64,且后端能稳定解出。











