首先声明,使用 RSA 非对称加密,正常的使用情景是公钥加密、私钥解密。
因为正常使用情景下,公钥是公开的,如果将私钥加密的数据发出去,使用公钥解密,其实理论上并没有起到加密的作用。
私钥加密、公钥解密的使用场景是在于防篡改,确定私钥发来的数据是正确的。
其实某种程度来说,确实就没必要再实现私钥加密,公钥解密的逻辑了。
不过出于强迫症的角度,理论上私钥是可以用于加密的,公钥也可以用于解密,那为什么不实现一下呢。。。
在 .NET 中,使用 BouncyCastle 是可以轻易实现的,但在 JavaScript 中,几乎所有 RSA 的算法库都没有进行支持。
好在有这种奇奇怪怪的需求的人们不少,jsencrypt 也算是使用比较广泛的一个 JavaScript 中的 RSA 算法库了,
总结了一下网上大家的思路,基于 jsencrypt 已有的公钥加密、私钥解密逻辑,修改了一套私钥加密、公钥解密的逻辑出来。
这里面比较核心的点,是 PKCS #1: RSA Encryption Version 1.5 的数据块组成
本身在 jsencrypt 中,私钥公钥的算法都是通用的,只是基于 pkcs 的封装有一些差异,导致无法使用私钥加密、公钥解密。
在此记录一下,
在需要加密时,在进行 RSA 运算前,需要先将源数据 D 封装为 Encryption block(EB)。
其中 PKCS #1: RSA Encryption Version 1.5 的数据块组成如下:
EB = 0x00 + BT + PS + 0x00 + D
说明:EB 为转化后的字节数组(一般以 16 进制表示),EB字节数组长度 = 密钥长度 / 8
0x00:第一位固定为 0x00
BT:公钥加密:0x02,私钥加密:0x00 或 0x01
PS:填充位,除固定位和数据位外,对 EB 的其余字节位进行填充,一般至少保留 8 位。
BT = 0x00 时全部填充 0x00(此种情景下,数据部分的首位字节必须非零,否则将无法准确的识别数据区)
BT = 0x01 时全部填充 0xFF
BT = 0x02 时随机产生数字填充(不允许填充 0x00)
0x00:源数据 D 的前一个字节,固定为 0x00,用于分隔填充位与数据区
D:源数据,填充在 EB 最后,因以上组成部分,源数据字节数组最大长度 = 密钥长度 / 8 - (1 + 1 + 8 + 1),即:EB长度-11
一般 RSA 密钥长度为 1024 位,则:
EB字节数组长度 = 1024 / 8 = 128
源数据字节数组最大长度 = 128 - 11 = 117
同理,解密就是在 RSA 运算后,按以上结构将数据再进行取出即可。
参考文章:
https://www.cnblogs.com/wsss/p/11516318.html
https://datatracker.ietf.org/doc/html/rfc2313#section-8.1
https://github.com/lwenhaoCN/RSA
https://blog.csdn.net/junxuezheng/article/details/109824552
因为看 jsencrypt 项目已经很久不怎么更新了,感觉也不太会有大更新了,直接 fork 了一个进行修改,后续可以直接用了。
修改好的 jsencrypt 项目地址:https://github.com/xwgli/jsencrypt
标签:公钥,加密,0x00,解密,RSA,私钥,jsencrypt From: https://www.cnblogs.com/xwgli/p/18182948