RSA 加密解密与签名验签

用 RSA 公私钥做四件事:加密、解密、签名、验签。页面会先告诉你这把密钥在当前填充下最多能加密多少字节——这是 RSA 最容易踩的坑。

当前用公钥。加密只能由公钥发起、私钥解开;签名只能由私钥发起、公钥验证——这四条路只有两个方向是通的。

参数

内容

当前明文 0 字节。

当前参数:PKCS#1 v1.5。解密和验签必须和对方用的是同一套填充与摘要,任一项不一致都会直接失败。

5 个容易踩的点

RSA 加密有长度上限。RSA 一次能加密的数据不可能超过模长,2048 位密钥的密文固定 256 字节,其中一大半是填充。PKCS#1 v1.5 要占 11 字节,OAEP 配 SHA-256 要占 66 字节,所以实际能加密的明文只有 245 或 190 字节。超过这个长度必须换个思路:随机生成一把 AES 密钥加密正文,再用 RSA 加密这把密钥,这就是混合加密,TLS 用的也是这个办法。

填充不是为了对齐,是为了安全。没有填充的裸 RSA(教材里那种 m^e mod n)是确定性加密,同一个明文永远得到同一个密文,攻击者能直接判断两段密文是否相同,还容易受到选择密文攻击。PKCS#1 v1.5 补上了随机性,但在 1998 年被 Bleichenbacher 攻破过(选择密文攻击),所以现在只用于兼容老系统。新系统一律用 OAEP,它的安全性有可证明的归约。

加密不等于签名。加密解决的是保密性:只有持私钥的人能看懂。签名解决的是完整性和来源:内容有没有被改、是不是真的来自那个私钥持有者。两者经常被搞混,典型错误是「用私钥加密就是签名」——虽然在某些填充下数学形式相似,但安全性质完全不同,而且用私钥加密意味着任何人都能用公钥解密,起不到任何保密作用。真的要保密就加密,要证明确实是本人就签名,两件事都做就两个都做。

PSS 每次签名都不一样。RSA-PSS 是随机化签名,里面加了随机盐,所以同样的消息和密钥,每次签名结果都不同,但都能验证通过。这是设计特性,不是 bug。带来的实际影响是:不能用「比较两次签名是否相同」来判断消息是否相同,必须老老实实调用验签接口。反过来,PKCS#1 v1.5 是确定性的,同样的输入永远得到同样的签名,做缓存或比对会方便一些。

私钥的用途只有一个方向。公钥加密、私钥解密;私钥签名、公钥验签。四条路只有这两条是通的。想「用公钥解密」或「用私钥验签」都做不到,因为公开指数的逆运算需要私钥里的素数因子。另外注意:私钥一旦泄露,攻击者既能解密历史消息,也能伪造签名,所以密钥泄露的正确处理是立刻轮换并废弃旧密钥,而不是改个名字继续用。

一段完整的自测流程

想确认参数配置是否正确,最快的办法是自己跑一遍:先在「加密」里贴公钥,输入一段短文本,拿到 Base64 密文;切到「解密」贴上同一把私钥,用完全相同的填充和摘要解回去,能还原出原文就说明这一对密钥本身是好的。再用同样的方式走一遍签名和验签。真出问题时先看报错里的字节数——RSA 的报错基本都能靠「密文或签名长度必须等于模长」和「明文不能超过上限」这两条定位。

跨语言对接时最容易出错的不是算法本身,而是参数命名。同一套东西在不同库里可能叫 RSA/ECB/PKCS1Padding、RSAES-PKCS1-V1_5、RSA_PKCS1_PADDING,OAEP 还要额外约定 MGF1 用的摘要。对接前把这几项写清楚,能省掉大半的联调时间。