首页/新闻资讯/正文详情

jsencrypt 前端 RSA 加密解密全攻略:密钥格式、uniapp 适配与避坑清单

发布时间:2026/9/26 16:55:07 来源:云帆数科 栏目:资讯中心
jsencrypt 前端 RSA 加密解密全攻略:密钥格式、uniapp 适配与避坑清单
简介面向需要在前端项目或 uni-app 中实现 RSA 加密解密的前端开发者该资源提供一套已适配 uni-app 的 jsencrypt 改造方案与封装调用示例。针对原生 jsencrypt 在 uni-app 中报错的问题作者对库文件进行了调整并额外提供 rsa.js 封装模块内含 rsaEncrypt 与 rsaDecrypt 两个方法可直接导入业务代码用于重要信息的加密传输与解密处理。资源共 3 个文件包含 2 个 JavaScript 文件和 1 个 txt 说明文档整体压缩包只有 37KB轻量易用jsencrypt.js 用于核心 RSA 加解密rsa.js 负责导出便捷方法txt 文件则记录公钥私钥生成与保存的注意事项。目前已有 2805 人学习下载适合遇到 uni-app 项目 RSA 集成报错、或希望快速上手 jsencrypt 的开发者参考。通过该资源可以省去自行查找和排错的时间直接获得可在 uni-app 与普通前端环境中使用的完整文件并按需扩展自己的加密逻辑。1. 敏感字段不能裸奔jsencrypt 是前端最顺手的 RSA 加密解密方案敏感字段不能裸奔这是前端开发的共识真到动手时多数人第一个想到的就是 jsencrypt——把 RSA 加密解密封装成浏览器里能直接用的 JS 库。它解决的是登录密码、身份证号、手机号这类短敏感值的传输问题前端用公钥加密后上传后端持私钥解密。对 web 前端来说它开箱即用在 uniapp 里做一点适配也能跑适合要对接 Java/PHP 后端接口的开发者也适合跨端项目的同学。本文按这个顺序来先讲清 RSA 的边界再跑通最小闭环然后处理 uniapp 三端差异最后是避坑清单和上线前的验证路径。2. 先把 RSA 和 jsencrypt 的底摸清密钥长度、填充与格式边界直接上库之前有三个约束必须先搞清楚否则后面调参全靠运气密钥长度决定单次能加密多少字节填充模式决定两端能不能对上密钥格式决定 setPublicKey 认不认得你手里的 PEM。这三件事和算法本身同样重要jsencrypt 的报错又特别安静大多数翻车都发生在格式和长度上而不是算法上。2.1 一次能加密多少1024 位连 60 个汉字都塞不下RSA 不是流式加密它每一次 encrypt 是对一个块做模幂运算块长上限由密钥位数和填充开销共同决定。jsencrypt 默认用 PKCS#1 v1.5 填充填充要占 11 个字节开头的 0x00、0x02、一段随机非零字节和分隔用的 0x00所以单次可加密的最大字节数 密钥位数 / 8 - 11。RSA 密钥长度单次最大明文长度字节大约能容纳的 UTF-8 中文1024 位117约 39 个2048 位245约 81 个3072 位373约 124 个中文在 UTF-8 下每个字符占 3 字节所以 1024 位连 60 个汉字都放不下。这个点也是前端面试题里常被追问的RSA 能加密多大数据?标准答案就是由密钥长度和填充共同决定一般只用来加密密码、手机号这类短字段。如果你拿 2048 位去加密一段 300 字节的 JSONencrypt 不会抛异常而是静默地返回 false——这是 jsencrypt 最误导人的行为后面我会专门讲。性能上也说一句2048 位加密一个 100 字节的块浏览器里基本在 15 毫秒解密稍慢但也可接受。真正不建议的是拿 RSA 当 AES 用去加密几 KB 的请求体加密本身不慢但密文膨胀率高、后端要逐块处理属于方向性错误。2.2 jsencrypt 的核心 APIsetPublicKey、encrypt、setPrivateKey、decryptjsencrypt 对外暴露的是一个 JSEncrypt 类常用方法就四个setPublicKey 设置公钥、encrypt 用公钥加密、setPrivateKey 设置私钥、decrypt 用私钥解密。注意它的返回值设计加密成功返回 Base64 字符串失败返回 false 而不是抛异常所以很多人第一次调用 encrypt 得到 false 时完全不知道错在哪。import JSEncrypt from jsencrypt // 公钥加密后端下发的 PEM 字符串必须带 BEGIN/END 行 const publicKey -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...中间是完整内容 -----END PUBLIC KEY----- const encryptor new JSEncrypt() encryptor.setPublicKey(publicKey) const cipherText encryptor.encrypt(密码:abc123) // cipherText 是 Base64一旦密钥格式不对或明文超长这里返回 false console.log(密文:, cipherText) // 私钥解密只在自测/同构场景使用生产环境私钥绝不能进前端包 const privateKey -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSj...中间是完整内容 -----END PRIVATE KEY----- const decryptor new JSEncrypt() decryptor.setPrivateKey(privateKey) const plainText decryptor.decrypt(cipherText) console.log(解密结果:, plainText)参数说明new 出来的实例内部持有独立的密钥状态不要多个请求复用同一个实例setPublicKey 接收的必须是完整 PEM——前后缀、换行缺一个都可能解析失败尤其别把 PEM 压缩成一行时丢掉换行encrypt 输出的是标准 Base64不是 hexdecrypt 入参同样吃 Base64。jsencrypt 另外还有 sign 和 verify用于私钥签名、公钥验签我放到最后一章讲它和加解密是两套用途。2.3 填充模式与 PEM 格式PKCS#1、PKCS#8 对不上时的症状JavaScript 生态的 RSA 实现普遍默认 PKCS#1 v1.5 填充jsencrypt 也是Java 后端常见的RSA/ECB/PKCS1Padding和它一致所以两端默认能对上。另一个常见填充是 OAEPJava 里写RSA/ECB/OAEPWithSHA-256AndMGF1Padding的接口jsencrypt 默认是不认的你会看到 encrypt 一路 false。遇到这种后端要么让后端换成 PKCS1Padding要么前端换支持 OAEP 的库这不是调参能解决的。密钥格式是第二个坑。同样的密钥内容PEM 头不一样解析器的理解就不一样格式公钥头私钥头常见来源PKCS#1BEGIN RSA PUBLIC KEYBEGIN RSA PRIVATE KEY老命令 openssl genrsaPKCS#8 / X.509BEGIN PUBLIC KEYBEGIN PRIVATE KEYopenssl genpkey、Java 默认jsencrypt 3.x 对两种公钥头都能解析但有一个例外带口令的私钥头是ENCRYPTED PRIVATE KEY直接 setPrivateKey 解不出明文必须先对私钥本身做解密。前后端格式对不上时的典型症状就是encrypt 返回 false 或 decrypt 返回 false但代码完全没报错不是算法错了是格式错了。我一般会建议后端统一输出 X.509 公钥、PKCS#8 私钥前端只拿公钥少一个变量少一个坑。3. 最小闭环跑起来openssl 生成密钥前端自加自解一遍这一章的目标是十分钟内在本地把公钥加密→私钥解密完整跑通。密钥文件用 openssl 生成前端用一个 vite 项目演示。先说明白自加自解只用来验证工具链和代码姿势不代表生产环境可以这样写私钥出现在前端代码里那一步看完就删。3.1 openssl 生成 2048 位密钥对两种命令和输出格式说明我一般用 genpkey 生成 PKCS#8 私钥再用 rsa 子命令导出公钥。原因很简单现代 OpenSSL 3.x 里 genrsa 虽然还能用但它直接输出 PKCS#1 私钥后面对接 Java 后端反而要多一步转换。# 生成 PKCS#8 私钥2048 位 openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem # 从私钥导出 X.509 格式公钥 openssl rsa -in private.pem -pubout -out public.pem # 验证私钥没有损坏 openssl rsa -in private.pem -check -noout # 直接看两个 PEM 的头确认格式 head -1 private.pem public.pem说明genpkey 的 -pkeyopt 用来传密钥长度-out 覆盖写文件不会二次确认别把已有文件覆盖掉。生成的 private.pem 头是BEGIN PRIVATE KEYPKCS#8public.pem 头是BEGIN PUBLIC KEYX.509。如果团队里有人习惯老命令等价写法是这样openssl genrsa -out rsa_private.pem 2048 # 产出 PKCS#1 私钥 openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem # 公钥仍是 X.509两种命令产出的私钥头不一样后端装配时如果写死只认某一种头换生成命令就会翻车。我的建议是把公钥 X.509、私钥 PKCS#8、无口令写进接口文档前后端都按这一套来避免两边各生成各的最后互相对不上。3.2 vite jsencrypt 的最小 demo加密一段中文再解密回来新建一个 vite 项目装好 jsencrypt 之后写一个最直接的脚本验证闭环。vite 里读 PEM 文件用?raw后缀最省事它会把文件内容按原样当成字符串导入。// test-rsa.js —— 在任意模块里 import 即执行 import JSEncrypt from jsencrypt import publicKey from ./public.pem?raw import privateKey from ./private.pem?raw const enc new JSEncrypt() enc.setPublicKey(publicKey) const cipherText enc.encrypt(中文敏感字段:密码abc123) console.log(密文长度:, cipherText.length, 密文:, cipherText.slice(0, 32) ...) const dec new JSEncrypt() dec.setPrivateKey(privateKey) const plainText dec.decrypt(cipherText) console.log(解密结果:, plainText) // 期望输出中文敏感字段:密码abc123说明?raw是 vite 的静态资源导入方式PEM 里的换行会被完整保留这点很重要手动去掉换行的 PEM 反而可能解析失败。如果在普通 HTML 里做同样的事就得用 fetch 拿到文本。这段代码把 privateKey 也 import 进了前端仅仅是为了在本地验证解密链路真实项目里前端代码永远只出现 publicKey。跑通之后对照三个现象密文是 Base64同一段明文每次跑出的密文不同解密结果和原文一致。如果明文是纯英文一切正常、一换中文就崩溃直接跳到第 5 章的乱码排查。顺便提醒一句网上有人找encryptLong之类的超长加密方法jsencrypt 官方没有这个 API市面上的封装都是自己写分段别把第三方实现当成库自带的能力。3.3 与后端对接的密文形态Base64、URL 安全与参数传递jsencrypt.encrypt 的输出是标准 Base64字符集是A-Za-z0-9/。放在 JSON body 里没有问题但放进 URL query 或表单时 和 / 会出事。前端传参最经典的翻车就是把密文拼在?dataxxxyyy上后端解析出来 已经变成了空格。// 方式一前端 encodeURIComponent后端先 decode 再 Base64 解码 const queryData encodeURIComponent(cipherText) // 请求形如/api/login?dataxxx%2Byyy%3D // 方式二直接转 base64url后端用 urlsafe 解码 const b64url cipherText.replace(/\/g, -).replace(/\//g, _).replace(/$/, )说明方式一后端改动小但依赖对方记得先 decode方式二更规范Java 端用Base64.getUrlDecoder()Python 端用base64.urlsafe_b64decodeNode 15 直接用Buffer.from(s, base64url)。我倾向于把密文字段一律 base64url写进接口文档前端在出口统一转换后端在入口统一还原任何人都不需要猜。对比下来方式二多两行代码但把传输层对特殊字符负责这件事彻底收口到前端内网网关也不会因为要转义 而报 400。4. uniapp 里让 jsencrypt 三端可用小程序、App、H5 的适配与封装uniapp 项目里直接import JSEncrypt from jsencryptH5 端通常没事打包成微信小程序或 App 就可能报错。这一章把原因、垫片和封装讲全。这也是 uniapp 面试题里三端差异的一个典型实例同一个库在不同容器里行为不同适配的关键是找到它缺的那个全局对象。4.1 报错根源为什么小程序里没有 window.cryptojsencrypt 做 PKCS#1 v1.5 填充时需要生成随机字节来填满填充区浏览器里它拿的是window.crypto.getRandomValues。小程序运行环境没有 window也没有全局 cryptoApp 端某些版本的 webview 虽然能跑 JS但 crypto 对象被裁剪或只暴露了部分方法。所以你看到的报错通常有两种编译期或运行时报window is not defined——库在加载阶段就直接引用了 window调用 encrypt 时报crypto.getRandomValues is not a function——window 有了但随机数接口不存在。这两个报错不是同一个原因前者是模块顶层作用域问题后者是运行时能力缺失。区分方法很简单展开报错堆栈看它发生在 import 那一行还是 encrypt 那一行。import 行报错说明加载时机不对需要在加载前垫 windowencrypt 行报错只需要垫 crypto。多数 uniapp 项目里两个会一起出现所以最省事的做法是加载之前把 globalThis.crypto 和 globalThis.window 一起垫好。4.2 先挂垫片再加载库crypto.getRandomValues 的跨端 polyfill垫片的目标是让 jsencrypt 调用加密时能拿到crypto.getRandomValues(typedArray)并被正确填入随机字节。微信小程序从较新的基础库开始提供wx.getRandomValues但不同平台返回结构不完全一样所以我的 polyfill 写成尽量兼容多种返回的形态。// utils/rsa-polyfill.js —— 必须在 import jsencrypt 之前执行 export function ensureRsaEnv() { // App 端和小程序端默认没有完整 window先给一个最小 window 壳 if (typeof globalThis.window undefined) { globalThis.window globalThis } const hasCrypto typeof globalThis.crypto ! undefined const hasRandom hasCrypto typeof globalThis.crypto.getRandomValues function if (hasRandom) return if (typeof wx ! undefined typeof wx.getRandomValues function) { globalThis.crypto { getRandomValues(target) { // 部分基础库返回 ArrayBuffer拷回调用方传入的 Uint8Array const res wx.getRandomValues({ length: target.length }) const raw res res.randomValues ? res.randomValues : res const bytes raw instanceof ArrayBuffer ? new Uint8Array(raw) : raw for (let i 0; i target.length; i) target[i] bytes[i] return target } } } else { // 最后的兜底伪随机源仅限自测或非敏感场景上线前必须接平台真随机 globalThis.crypto { getRandomValues(target) { for (let i 0; i target.length; i) target[i] Math.floor(Math.random() * 256) return target } } } }说明第一步把 window 指到 globalThis是为了兼容那些在顶层读 window 的版本。第二步的兜底用了 Math.random它不是密码学安全的随机源适用场景只有demo 或暂时拿不到平台随机接口时先让链路跑通账号、支付类场景上线前一定要换成平台提供的真随机接口。不同基础库对 wx.getRandomValues 的返回方式有差异我在代码里做了兼容如果某个平台只支持异步回调你还需要把它包成同步等待。这个 polyfill 的执行时机是关键中的关键必须在 jsencrypt 第一次被加载之前执行。ESModule 的 import 会被静态提升到模块顶部所以下面这种写法是错的// 错误写法polyfill 会排在 import 之后才执行 import JSEncrypt from jsencrypt import { ensureRsaEnv } from ./rsa-polyfill ensureRsaEnv()正确做法是在入口文件main.js 或 App.vue 的 onLaunch里先执行ensureRsaEnv()再用动态 import 或 require 去加载业务模块。顺序错了垫片就白垫报错一个不少。4.3 封装成统一的 rsaEncrypt 模块公钥下发与条件编译跨端项目里我不建议每个页面各自 new JSEncrypt公钥来源、垫片时机、错误处理散落各处早晚出幺蛾子。常见做法是收敛成一个工具模块只暴露 rsaEncrypt 和可选的 rsaDecrypt 两个函数调用方不需要知道 jsencrypt 的存在。// utils/rsa.js import { ensureRsaEnv } from ./rsa-polyfill ensureRsaEnv() // 先垫环境再加载依赖 const JSEncrypt require(jsencrypt) // 用 require 避免静态提升抢跑 // 公钥获取H5 可以直接 import 常量小程序/App 建议首次登录时从接口拉 // #ifdef H5 import publicKeyPem from /config/rsa-public.pem?raw const DEFAULT_PUBLIC_KEY publicKeyPem // #endif // #ifndef H5 let DEFAULT_PUBLIC_KEY // #endif export function setPublicKey(key) { DEFAULT_PUBLIC_KEY key } export function rsaEncrypt(plain, pubKey DEFAULT_PUBLIC_KEY) { if (!pubKey) throw new Error(RSA 公钥未设置先调用 setPublicKey 或等待接口下发) const enc new JSEncrypt() enc.setPublicKey(pubKey) const out enc.encrypt(String(plain)) if (out false) { throw new Error(encrypt 返回 false检查公钥格式、明文长度2048 位上限 245 字节) } return out }说明这个模块用 require 加载 jsencrypt刻意避开 ESM 静态提升ensureRsaEnv 在模块顶层先执行加载顺序可控。H5 端把公钥放在配置里小程序和 App 端公钥留空、等服务器下发避免把密钥写进能被反编译的包里。调用方只需要关心 rsaEncrypt(plain) 这一个入口密文拿到手就是 Base64出错时错误信息直接给了排查方向。界面上再包一层 try/catch把encrypt 返回 false翻译成用户能看懂的话后端联调时也能更早暴露问题。5. jsencrypt 避坑清单encrypt 返回 false、乱码、加号变空格五条踩坑记录以下五条是实际项目里反复出现的问题每条按现象→原因→解决写。前三条在浏览器里就会碰到后两条往往要等到打包或上线才冒头提前看过等于省一次线上事故。5.1 encrypt 返回 false先查密钥格式、明文长度和实例状态现象encrypt 调用没有任何异常返回 false。这是 jsencrypt 最让人困惑的设计——失败不抛错给个 false 让你自己猜。原因分成三类一是密钥没设置或格式不完整setPublicKey 没调用或者 PEM 从接口文档复制时把-----END PUBLIC KEY-----弄丢了二是明文超长1024 位密钥上限 117 字节、2048 位上限 245 字节一个中文占 3 字节一小段 JSON 就超了三是公钥内容本身不是 PEM有人把裸 Base64 传了进去甚至传成了私钥。解决在调用前做三个快速断言把 false 变成具体错误function safeEncrypt(enc, pubKey, plain) { if (!pubKey || !pubKey.startsWith(-----BEGIN)) { throw new Error(公钥格式异常必须是以 -----BEGIN 开头的完整 PEM) } const bytes new TextEncoder().encode(String(plain)) console.log(明文长度(字节):, bytes.length) if (bytes.length 245) { throw new Error(明文超出 2048 位单次加密上限(245 字节)考虑改用 AESRSA 混合方案) } const out enc.encrypt(String(plain)) if (out false) throw new Error(encrypt 返回 false确认公钥与当前密钥位数匹配) return out }说明TextEncoder 在浏览器、小程序 H5、App 端都可用用它算 UTF-8 字节数最稳如果是在和 1024 位的老系统对接把 245 换成 117 再跑一次问题通常立刻定位。记住一条经验凡是自己本地自加自解成功、一到后端就 false十有八九是密钥格式或位数不一致不是算法写法问题。5.2 解密后中文乱码大多是字符集链路不统一不是 jsencrypt 的锅现象英文数字解密正常中文解密出来是 或一堆乱码有时长度也不对。原因jsencrypt 内部按 UTF-8 处理字符串对中文没有特殊门槛。乱码几乎都出在密文传输链路上——后端用平台默认编码Windows 下常见 GBK解析了请求体里的 Base64 串或者前端 fetch 时没指定 charset后端按 ISO-8859-1 读了一遍再转回字符串原始字节已经被改写。解决两端把字符集钉死。前端请求头写明白fetch(/api/login, { method: POST, headers: { Content-Type: application/json; charsetutf-8 }, body: JSON.stringify({ encData: cipherText }) })后端拿到 JSON 字段后先以 UTF-8 解码文本再做 Base64 解码。一个有效的验证方法把密文在浏览器 console 里 JSON.stringify 一下和请求体里的字符串逐字符对比如果多出\u转义或%就是链路某层偷偷改了密文。另外注意 jsencrypt 不吃 hex 格式的密文后端如果返的是 16 进制串先转成 Base64 再喂给 decrypt。5.3 密文进 URL 后加号变空格encodeURIComponent 还是 base64url现象H5 页面把密文拼在?data上发给后端后端解密第一段就失败打开 network 面板发现请求 URL 里的 变成了空格。原因URL query 走的是 application/x-www-form-urlencoded 解析规则这个规则把 当作空格而 Base64 里 是合法字符/ 也会被某些网关特殊处理。加密结果里出现 的概率是 1/64字段一长基本必中。解决规则只有一个——密文只要离开 JSON body就必须先做 URL 安全化。二选一前端encodeURIComponent(cipherText)后端先 decode 再 Base64 解码更干净的是前端直接转 base64urlconst toBase64Url (s) s.replace(/\/g, -).replace(/\//g, _).replace(/$/, ) // 用法/api/login?data${toBase64Url(cipherText)}别把希望寄托在这次刚好没有 上——密钥一变、明文一变密文立刻变撞上一次就是线上事故。传参前先问自己这串密文是要进 URL、进表单还是进 JSON分别对应不同处理。5.4 明文超长就分段先想想 AES RSA 混合方案现象把 300 字节的 JSON 直接 encrypt返回 false有人改成循环按 245 字节切块每块加密后端再拼起来解。短时间能用但密文膨胀严重拼接顺序、补位、异常中断全是补不完的洞。原因RSA 是定长块加密分段后每段仍受密钥上限约束密文每段至少和密钥等长传输成本成倍上涨。分段本身不解决RSA 不适合大数据这个本质问题。解决敏感数据超过 200 字节时常用可靠做法是混合加密。流程是前端生成一个随机会话密钥用 AES-GCM 加密真正的数据再用 RSA 只加密这个会话密钥把两份密文一起交给后端。// 混合方案设计参考RSA 只保护 AES 密钥不碰正文 const sessionKey crypto.getRandomValues(new Uint8Array(16)) // 1. 数据体用 AES-GCM 加密Web Cryptocrypto.subtle.encrypt // 2. 会话密钥转 Base64 后用 rsaEncrypt 加密 // 3. 请求体形如 { encKey: RSA加密后的AES密钥, encData: AES-GCM密文, iv: ... }说明Web Crypto 的 subtle 在 H5 和小程序的某些环境可用性不一致小程序端可以退一步用纯 JS 的 AES 实现但核心思路不变——RSA 永远只处理短数据。这也是很多后端接口设计的默认形态登录接口只 RSA 加密密码业务数据走 HTTPS 或走 AES。分段这个方案我见过三次三次最后都重构成了混合加密不如一开始就选对。5.5 uniapp 打包后白屏或编译报错polyfill 时机和 import 顺序现象H5 本地跑得好好的打包成微信小程序后页面白屏控制台报window is not defined或者打成 App 后 iOS 端白屏、Android 端正常。原因打包器把 jsencrypt 打进了产物但它引用的浏览器全局对象在小程序环境不存在iOS 的 JavaScriptCore 对某些全局对象的行为和 V8 不一致运行到一半就抛错。另一个隐藏坑是 polyfill 写在业务模块里 importESM 静态提升导致它排在 jsencrypt 之后执行垫片等于没垫。解决严格按垫片 → 库 → 业务的顺序加载并用条件编译把差异隔离// main.js 或 App.vue 的 onLaunch // #ifndef H5 import { ensureRsaEnv } from ./utils/rsa-polyfill ensureRsaEnv() // #endif // #ifdef H5 // H5 不需要垫片正常加载业务模块即可 // #endif小程序端如果仍有兼容问题可以把 jsencrypt 的 npm 产物加入编译白名单或在页面里用 require 动态加载把加载时机延后到 ensureRsaEnv 之后。排查这类白屏的通用路径是看对应平台控制台的第一条报错——白屏的根因几乎都是第一行报错后面几百行全是它的连锁反应。在 uniapp 里做跨端加密我习惯上先问一句这个库在我目标平台上有 window 和 crypto 吗比等报错再猜快得多。6. 从能自解到敢上线业务壳、随机填充和 openssl 交叉验证最小闭环能跑离上线还差三件事防重放、确认随机性、和标准实现互相验证。做完这三步一套 RSA 方案我才敢说放心。6.1 明文里塞 nonce 和时间戳解密后先验壳再放行RSA 只能保证能被正确私钥解出来的就是加密时的样子保证不了这条请求不是被录下来重放的。常见做法是加密前给业务数据包一层壳让密文自带防重放信息function wrapPayload(data) { return JSON.stringify({ v: 1, t: Date.now(), // 解密后校验与服务器时间差超过 5 分钟拒绝 nonce: Math.random().toString(36).slice(2), // 服务端记录用过的 nonce重复即拒 data }) }说明解密成功只是第一步后端要 JSON.parse 之后校验 t 的时效和 nonce 是否重复。这两项挡不住高水平的攻击但能挡掉拿着密文无限重放的脚本成本极低。这个壳我习惯封装在 rsaEncrypt 内部调用方无感。6.2 两次密文必须不一样PKCS#1 v1.5 的随机填充是安全底线自测时有人会问同样一段明文为什么两次加密结果不同这不是 bug是填充里带了随机字节。PKCS#1 v1.5 在数据前面塞了随机非零字节同一明文在不同时刻的密文必然不同。如果你看到两次结果完全一样反而要怀疑是不是用了静态填充或固定随机源——那种实现等于把 RSA 退化成了确定性加密密文会泄露明文模式。所以测试断言里应该写两次密文不相等而不是密文等于某个预期值。还要分清用途公钥加密、私钥解密是保密私钥加密、公钥解密是签名验签。拿反了就会能加不能解这不是玄学是密钥用途没配对。6.3 用 openssl 交叉验证 jsencrypt上线前 5 分钟的后悔药自加自解只能证明jsencrypt 自己跟自己能玩证明不了它和后端生态互认。上线前我习惯用 openssl 做一次交叉验证这是最便宜的后悔药# 生成测试明文注意不要用 echo 自带的换行 printf hello-rsa-check plain.txt # 用公钥加密openssl pkeyutl 默认即 PKCS#1 v1.5 填充 openssl pkeyutl -encrypt -pubin -inkey public.pem -in plain.txt -out cipher.bin # 转 Base64得到一份标准实现的密文 base64 cipher.bin把这段 Base64 交给前端用 jsencrypt 的私钥解密如果能解出hello-rsa-check说明密钥解析、填充模式、编码链路全部和后端生态一致。反过来把 jsencrypt 加密的密文用openssl pkeyutl -decrypt -inkey private.pem解密结果一致才算双向互认。换密钥对、换后端框架、调填充模式之后我都会重跑一遍这条交叉验证。我的习惯是把这个验证写成固定脚本每次密钥轮换顺手回归一次。正因为能自解和能互解是两个世界——互解通了上线后后端联调基本不会在加解密本身卡住。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

Git 常用命令实战:从安装配置到分支管理、撤销回滚与远程协作
Git 常用命令实战:从安装配置到分支管理、撤销回滚与远程协作

1. 安装与环境准备1.1 Git 安装方式小结Git 是当下开发者绕不开的工具,就算平时用 IDE 的图形按钮提交代码,底层的还是这一套命令。与其等出了问题对着错误提示干瞪眼,不如先把常用指令摸透。这篇文章没有废话,也不按什么“入门到… · 2026/9/26 16:55:07

XMLViewer xml查看器实战:格式化、XSD校验与XPath定位
XMLViewer xml查看器实战:格式化、XSD校验与XPath定位

简介:XMLViewer是一款面向开发人员与数据处理人员的XML文件查看工具,能快速打开、浏览XML文档并辅助检查语法是否正确。它解决了手动用记事本查看XML时格式杂乱、层级不清的问题,安装后右键点击XML文件选择“View”即可直接调用,操… · 2026/9/26 16:55:07

XML查看器实战指南:格式化、XPath查询与MyBatis XML配置
XML查看器实战指南:格式化、XPath查询与MyBatis XML配置

简介:XMLViewer是一款面向开发、测试及运维人员的XML查看工具,专注于快速打开XML文件并帮助检查语法是否正确,适合日常处理配置文件、接口数据报文、日志片段等场景。该工具采用msi安装包形式,一次性集成到Windows右键菜单&#x… · 2026/9/26 16:55:00

PostgreSQL numeric类型全解析:存储格式、内存表示与精度实践
PostgreSQL numeric类型全解析:存储格式、内存表示与精度实践

先说明一下,这篇文章不是给你讲“numeric怎么存进内存”这种教科书定义,而是把我在实际项目里和 PostgreSQL 的 numeric 搏斗过几轮之后,积累下来的完整链路梳理。从数据库磁盘上的存储格式,到进程内存里的表示,再到客… · 2026/9/26 17:25:49

Python机器学习入门与Scikit-learn
Python机器学习入门与Scikit-learn

机器学习入门与-learn一、正式踏上学习机器知识的道路, 开始接触由这一个专门库带来的初步体验。关于第一章的小节, 也就是第一小节所提到的内容, 它是为了梳理清楚这门技术在过往岁月和当前阶段的演变历史, 这其实是一场因为数据运用而引发的巨大变革过程。在计算机科学这片范… · 2026/9/26 17:25:49

18个最热深度学习Github项目逐一介绍
18个最热深度学习Github项目逐一介绍

摘要: 在前几天, 我们列举出了一百个是关于深度学习的源代码项目, 不过, 这些项目之中的大部分, 目前都不怎么活跃了, 所以呢, 我们在这里特别挑出来了一十八个最为活跃的此类项目, 并为每一个项目都制作了一张专门的信息卡片, 这样一来, 就可以让人感到一目了然了, 这就比较方… · 2026/9/26 17:25:49

SQL约束实战指南:从数据完整性到防重防脏的完整设计
SQL约束实战指南:从数据完整性到防重防脏的完整设计

作为常年跟SQL打交道的人,我翻看自己的笔记时发现“约束”这一章被画满了记号。很多初学者觉得约束不过是建表时顺手写的几个单词,实际上一旦数据量上来、业务逻辑变复杂,约束设计得好不好,直接决定你是优雅地维护数据&#xff0c… · 2026/9/26 17:25:43

Claude Code 完整指南:从安装配置到实战排障
Claude Code 完整指南:从安装配置到实战排障

1. 先搞明白:Claude Code 到底是个什么东西 1.1 一句话说清楚:终端里的 AI 编程搭档 Claude Code 是 Anthropic 推出的一款面向开发者的 AI 编程工具,它不像 ChatGPT 那样只是聊天窗口,而是直接跑在终端里的命令行程序。你输入一… · 2026/9/26 17:25:43

医疗器械包装验证方案:ISO 11607框架下的密封强度与泄漏试验实战指南
医疗器械包装验证方案:ISO 11607框架下的密封强度与泄漏试验实战指南

简介:《医疗器械包装性试验验证方案》是面向医疗器械生产、研发及质量管理人员的验证文档,用于指导包装系统完整性验证工作的开展。文档以YY/T0681.1、YY/T0313等标准为依据,系统梳理了试验目的、样品选择、验证依据、试验工程、结论以及验证… · 2026/9/26 17:25:34

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码