RSA 加密 / 解密

在浏览器中生成 RSA 密钥对,用 RSA-OAEP 加密解密,用 RSA-PSS 或 PKCS#1 v1.5 签名,并在 SPKI、PKCS#8 与 PKCS#1 PEM 格式之间转换密钥。一切都在本地用 WebCrypto 引擎完成 —— 密钥和消息绝不离开本页。
RSA 的用途:小消息和签名,不是批量数据。RSA 单次最多加密 k−2·hashLen−2 字节(2048 位密钥配 SHA-256 时为 190 字节),而且比 AES 慢约 1000 倍。真实系统用的是混合加密:RSA 包裹一把随机 AES 密钥,AES 再加密数据(参见 AES 工具)。本页用于学习、测试和互操作验证。
模长:
哈希: 标签:
PKCS#1 v1.5 加密是刻意不实现的:它没有密文完整性,会败给填充预言攻击(Bleichenbacher)。OAEP 是新设计唯一应该使用的加密模式。
方案: 哈希:
方案: 哈希:
一次 RSA 调用能装多少? OAEP 消息上限 = 密钥字节数 − 2×哈希字节数 − 2:
模长SHA-256SHA-384SHA-512
1024 位62 字节30 字节0 字节
2048 位190 字节126 字节62 字节
3072 位318 字节254 字节190 字节
4096 位446 字节382 字节318 字节
签名没有消息长度限制 —— 消息先做哈希;签名本身总是恰好等于密钥长度(2048 位密钥为 256 字节)。
密钥强度与生命周期 (NIST SP 800-57 第 1 部分第 5 版):1024 位自 2013 年起不允许用于新用途;2048 位是当前的最低标准,可用至 2030 年;3072 位及以上是 2031 年以后的从容之选。8192 位在本页可用,但浏览器中生成可能耗时一分钟以上,而且买到的安全余量目前谁也用不上 —— 建议 3072/4096。
与 OpenSSL 互操作 —— 本页生成的 PEM 可在命令行往返使用:
openssl pkeyutl -encrypt -pubin -inkey pub.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256
openssl pkeyutl -decrypt -inkey priv.pem -pkeyopt rsa_padding_mode:oaep ...
openssl dgst -sha256 -sign priv.pem -out msg.sig msg.txt(PKCS#1 v1.5)/ openssl dgst -sha256 -verify pub.pem -signature msg.sig msg.txt
PSS:在两条 dgst 命令中加 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32。密钥格式转换:openssl rsa -in key.pem -RSAPublicKey -out pubkey.pem 可在磁盘上做 SPKI ↔ PKCS#1 转换;本页则在浏览器里完成。
自检
用独立的锚点数据重跑本页引擎:三种模长的密钥生成、OAEP 加解密往返、PSS 与 PKCS#1 v1.5 在各哈希下的签名验签、一条逐字节确定的 v1.5 签名向量(由独立的原生实现生成)、一条由 OpenSSL 生成的固定 OAEP 密文与 PSS 签名(本页必须能解密/验证,跨实现互操作)、PEM 格式转换往返,以及反向测试(被篡改的签名、错误的密钥必须失败)。不会向任何地方发送数据。
从零讲清 RSA 加密与签名
一句话:RSA 是公钥密码 —— 任何人都能用公钥给你加密或验证你的签名,但只有私钥能解密或签名 —— 它的安全性建立在“把一个 2048 位的数分解成两个素数是现有任何计算机都做不到的”这一事实之上。
RSA 到底在算什么

两个素数与一扇活板门。RSA 密钥始于两个大随机素数 p 和 q;它们的乘积 n(模数,即“2048 位密钥”里的 2048 位)连同指数 e(几乎总是 65537)一起公布在公钥里。私钥是 d —— e 的模逆元,由 p、q 算出 —— 外加由素数导出的几条捷径,能把私钥运算提速 4 倍。

加密和签名是同一种幂运算。加密消息块 m 就是算 me mod n;解密是 cd mod n。用私钥指数对预格式化的块做运算、再用公钥验证,本质上是同一运算的另一个方向。本页其余的一切 —— OAEP、PSS、PKCS#1 —— 都是在安全地格式化那个块,使裸幂运算从不直接暴露。

为什么它慢。2048 位密钥上的一次 RSA 运算,CPU 开销约是 AES 加密一个分组的一百万倍。这就是 TLS 只用 RSA(或 ECC)协商一把随机 AES 密钥、然后切换到对称加密的原因。你自己构建系统时同理:RSA 管密钥,AES 管数据。

密钥长度。模长就是安全旋钮:1024 位属于“政策性告破”(分解已接近国家级能力),2048 位 ≈ 112 位对称强度,3072 位 ≈ 128 位,15360 位才相当于 256 位对称密钥。后量子备注:Shor 算法在足够大的量子计算机上会直接击溃 RSA —— 这也是要把密钥生命周期限定在有限年限内的又一个理由。

OAEP:唯一应该使用的加密填充

裸 RSA 的问题。教科书 RSA(对原始消息直接算 me mod n)是确定且可塑的:相同消息加密结果相同,攻击者可以把密文乘上“2 的加密”使明文翻倍,短消息还会败给中间相遇。它绝不能直接使用。

OAEP 做了什么。OAEP(PKCS#1 v2,RFC 8017)在幂运算之前,通过一个基于哈希的 Feistel 网络把消息与随机种子混合。效果:加密随机化(同一明文每次密文都不同 —— 把 OAEP 示例跑两遍对比即可见)、解密时可检出篡改,而且消息必须连同哈希的余量一起装进模数 —— 即上面表格里的 k−2·hashLen−2 上限。

标签。一个可选的、绑定进填充的公开字符串。两侧必须提供完全相同的标签,否则解密失败 —— 就像 AES-GCM 里的 AAD,它让你把密文绑定到上下文(文件名、算法编号等)。留空是常态。

PKCS#1 v1.5 加密是刻意缺席的。1993 年代的这种填充没有完整性,还会通过解密报错行为泄露信息(Bleichenbacher 1998 年的攻击用几百万个特制密文把填充预言变成完整解密;它至今仍能攻破真实系统,即 2017 年的 ROBOT)。WebCrypto 拒绝实现它,本页也一样。

PSS 与 PKCS#1 v1.5 签名

签名 = 哈希,再填充,再幂运算。两种方案都先对消息哈希(此处为 SHA-256/384/512),把摘要格式化成块,再施加私钥指数。验证用公钥重新格式化块并比对。区别在格式化方式:

方案块内容性质
RSASSA-PKCS1-v1_5摘要 + digest-info 头,确定性同一消息的每次签名逐字节相同(见自检:其 v1.5 向量是固定的)。旧系统普遍支持:X.509 证书、JWT RS*、TLS 1.3 之前。在验证实现正确的前提下没有已知伪造。
RSA-PSS摘要 + 哈希长度的随机盐,MGF1 掩码随机化 —— 每次签名都不同。在随机预言模型下可证安全;TLS 1.3 的未来证书和更新协议要求使用。验证方必须知道盐长(本页用哈希长度,即常见默认值)。

怎么选。新设计:PSS。要与现有证书、JWT(RS256)或旧系统互通:v1.5。两者作为签名方案都是安全的 —— v1.5 的黑历史说的是*加密*填充,那是另一种构造,只是名字像。

PKCS#1、PKCS#8 与 SPKI:同一把密钥为什么有四种 PEM

PEM 只是套了壳的 base64 DER。PEM 文件 = 一个标签 + 一段 DER(二进制 ASN.1)结构的 base64。标签告诉你里面是哪种结构 —— 这就是本页生成的四种格式的全部区别:

PEM 标签DER 结构内容
-----BEGIN PUBLIC KEY-----SubjectPublicKeyInfo(SPKI)算法 OID + 公钥(n, e)。各处默认的“公钥”。
-----BEGIN RSA PUBLIC KEY-----PKCS#1 RSAPublicKey裸的(n, e)。老版 OpenSSL 和一些嵌入式库。
-----BEGIN PRIVATE KEY-----PKCS#8 PrivateKeyInfo算法 OID + 私钥。算法无关的容器,现代默认。
-----BEGIN RSA PRIVATE KEY-----PKCS#1 RSAPrivateKeyn, e, d, p, q, …。经典 OpenSSL genrsa 的输出。

同一把密钥,不同的包装。点“生成”后上面四个框里是同一密钥对 —— SPKI 与 PKCS#1 公钥含完全相同的 n、e;PKCS#8 与 PKCS#1 私钥含完全相同的私钥值。本页在浏览器里无损地互相转换(自检会往返验证这些转换)。在工具之间粘贴密钥时报“密钥无效”,最常见的原因就是搞混了格式。

加密的 PEM(-----BEGIN ENCRYPTED PRIVATE KEY-----)是带口令加密层的 PKCS#8。本页不接受它们 —— 请先离线解密(openssl pkcs8 -in enc.pem -out plain.pem)。

常见错误
  • 用 RSA 加密批量数据。除了 OAEP 的硬性长度上限,它还慢得离谱。正确的模式是:生成随机 AES 密钥,用 RSA-OAEP 加密这把 AES 密钥,用 AES-GCM 加密数据,两部分一起发送。
  • 因为“只是测试”就把私钥发出去。本页向量里内嵌的示例密钥是刻意公开的 —— 它们只能签测试数据。你的密钥不是这样。
  • 用私钥做验证。数学上通常“行得通”,有些工具也允许,但把私钥发给验证方就毁了整个模型。验证请用公钥(本页强制如此)。
  • 逐字节比较签名来判断代码对不对。PSS(和 OAEP)是随机化的:两次正确的运行产生不同输出。要比较就用验证来比,不要比字节相等 —— 只有 v1.5 签名是确定性的。
  • “因为快”就用 1024 位密钥。它同时也进入了资金充裕者的分解射程。速度论据已随混合加密而死:RSA 每个会话只跑一次,不是每个字节跑一次。
  • 给 PSS 硬编码一个不等于哈希长度的盐长。验证方必须一致;哈希长度(本页的选择,也是 OpenSSL 的 -sigopt rsa_pss_saltlen:32)才是可互通的默认值。
常见问题

为什么我的解密失败,提示“密钥错、哈希错或密文损坏”?OAEP 解密在以下任一情况都会失败:不是配对的私钥、哈希与加密方不同、标签不同,或密文任一字节被改过。错误提示刻意对所有情况完全一致 —— 泄露“哪项检查失败了”本身就是一次填充预言泄露。

能从私钥导出公钥吗?能 —— 私钥格式里同样含 n 和 e。本页在“生成”后把两者都展示出来;OpenSSL 用 openssl pkey -in priv.pem -pubout 实现。

为什么指数是 65537?它是素数,二进制只有两个 1(幂运算快),又足够大,能避开 e=3 时代的低指数攻击。1990 年代起它就是标准选择;“奇怪”的指数通常意味着弱密钥或非标准密钥。

RSA 因为量子计算机死了?还没有 —— 目前没有任何机器能在 2048 位模数上跑 Shor 算法。但必须保密到 2030 年代以后的数据已经在迁移;PQC 混合方案是工程上的答案。

那 openssl rsautl 呢?它是 pkeyutl 的废弃前身。默认用 v1.5 加密填充 —— 这也是它被淘汰的又一个原因。