纯js用bigint实现rsa效率提升关键在于少绕路、不降级、控边界:需统一非负大整数表示、手动大端字节转换、miller-rabin素性测试、web worker分担密钥生成与并行加密,并安全管理私钥。

纯 JS 环境下用 BigInt 实现 RSA 加密,效率提升的关键不在“能算”,而在“少绕路、不降级、控边界”。原生 BigInt 比第三方大数库(如 jsbn 或 rsa-js)更轻量、无依赖、且 V8 引擎对其模幂等关键运算做了底层优化。但若用法不当,反而会因隐式转换、字节序错误或填充缺失导致性能塌方甚至安全漏洞。
明文和密钥必须统一为正向大整数表示
JS 的 BigInt 默认支持带符号解析,而 RSA 要求所有参与运算的数(尤其是明文 m 和模数 n)为非负整数。直接用 BigInt(str) 解析十六进制字符串可能因前导负号或高位字节溢出误判符号。
- 明文转
BigInt:先用new TextEncoder().encode(str)得到 Uint8Array,再调用bytesToBigInt(bytes)—— 手动按大端序拼接:bytes.reduce((acc, byte) => (acc - 密钥(如 PEM 中的
n,e,d)应从 base64 解码后取 DER ASN.1 中的 INTEGER 字段,再用new Uint8Array(raw).reduce(...)转为BigInt,避免用parseInt(str, 16)(会截断)或BigInt("0x" + hex)(对超长 hex 易抛错) - 务必校验
m ,否则 <code>modPow结果无效;若明文过长,需按Math.floor((n.bitLength() - 1) / 8) - 11(PKCS#1 v1.5)分块,而非硬切固定长度
核心运算必须用原生方法,禁用手动实现
V8 对 **(幂)、%(取模)、&(位与)等操作符在 BigInt 上已做内联优化,但 modPow 这类组合运算仍需手动展开。JS 没有内置 modPow,但可借助快速幂+模约减组合,比循环乘快一个数量级。
- 加密
c = m^e mod n:用迭代快速幂,每步都做% n,防止中间值爆炸——result = (result * base) % n,不是先算完整幂再取模 - 私钥指数
d运算同理,但注意d通常比e大得多,必须启用平方-乘算法(binary exponentiation),避免线性循环 -
gcd和modInverse不可手写欧几里得——JS 无尾递归优化,深递归易栈溢出;改用迭代版gcd(a, b),再套用扩展欧几里得求逆元(a * x ≡ 1 (mod b)),返回(x + b) % b确保正数
避免频繁类型混用和内存拷贝
每次 BigInt.toString(16) 或 String(n) 都触发全量字符串生成,对 2048 位数耗时可达毫秒级;而密钥导出/导入阶段最容易掉进这个坑。
- 密文输出不转字符串:加密后保持为
BigInt,需要序列化时用bigintToBytes(c, n)转成定长 Uint8Array(长度 =Math.ceil(n.bitLength() / 8)),再用bytesToBase64() - 禁止用
Number(bn)或bn.valueOf()提取数值——会丢失精度或抛RangeError;真要比较大小,用bn1 > bn2或bn1 === bn2即可 - 生成素数不用
probablePrime(JS 无此 API),改用 Miller-Rabin 测试:对候选数p做 32 轮随机底数测试,isProbablePrime(p, 32)可将合数误判率压到4^(-32)以下
利用浏览器特性减少计算压力
纯 JS RSA 不适合高频加解密,但可通过任务拆分和 Web Worker 隐藏延迟,让主线程不卡顿。
- 密钥生成(尤其是 2048 位素数搜索)必须放 Web Worker,主界面显示进度条;单次 Miller-Rabin 测试控制在 10ms 内,超时则中断并换新候选
- 加密操作若明文较长(如 >1KB),拆成多块并行加密(Web Worker 支持
postMessage传递BigInt),最后合并密文数组 - 缓存已计算的
φ(n)和d,避免重复求逆;但私钥材料绝不能存localStorage或全局变量,用crypto.subtle导入后立即clear引用











