目录
1.问题现象
2.填充方式解读
2.1 RSA公钥加密填充解读
2.2 RSA私钥签名填充解读
3.小结
大家好,这里是快乐的肌肉。今天聊聊RSA一些以前没有关注到的小细节。
1.问题现象
最近Muscle v01进入收尾阶段,涵盖了绝大部分密码算法,具体效果如下:
在做RSA的时候发现了使用RSA公钥对相同的数据进行加密,每次得到密文竟然都不一样。如下图:
明文数据为0x11223344,使用同一RSA公钥对同一数据(使用私钥解密可以验证,如上图绿框)进行三次加密,得到的密文数据为:
- 0x80208321b8......
- 0xcf997e6677......
- 0x85213fcf97......
公钥加密是如此现象,那么如果用RSA私钥进行签名(可以认为是签名加密),结果会一致吗?事与愿违,它竟然是一致的,如下图:
签名值均为0xbe34bcb8d3......这个现象就有意思了。
我们来思考下,上述过程参与到加密计算的内容包括:原始数据+填充值、密钥;
现在原始数据和密钥是一致的,那么问题大概率就出现在填充方式上面。
2.填充方式解读
好,在这里我使用的是PKCS1v15进行填充:
根据pyCryptography提供的说明,使用的是RFC 3447标准进行填充,如下图:
这里面有详细的Padding方式解读,但是我本来是想dump出填充后的数据来进行分析,没有找到途径,因此转向Openssl。
2.1 RSA公钥加密填充解读
示例如下:
首先看看该txt的hex,如下:
然后使用public_key.pem对demo.txt进行两次加密,可以看到密文仍不相同:
rsautl -encrypt -in Demo.txt -inkey public_key.pem -pubin -out DemoEnc1.bin
rsautl -encrypt -in Demo.txt -inkey public_key.pem -pubin -out DemoEnc2.bin
接着我们想个办法把加密前填充后的数据dump出来:
rsautl -decrypt -in DemoEnc1.bin -inkey private_key.pem -out DemoPadDec1.bin -raw
rsautl -decrypt -in DemoEnc2.bin -inkey private_key.pem -out DemoPadDec2.bin -raw
通过hex比对,我们可以发现,原始数据被拼接在最后,如下图:
通过上图,很明显可以发现,每次填充的前两个字节均为00 02,中间的填充数据都不一样,因此造成了不同的密文结果。
Openssl默认使用PKCS#1 v1.5进行填充,如下图:
所以还是得去解读下标准填充格式, 原文路径如下:RFC 2313: PKCS #1: RSA Encryption Version 1.5
在8.1 Encryption-block formatting章节,详细解读了加密块格式,如下图:
EB整体由00 || BT || PS || 00 || D拼接组成:
- 第一个00:确保加密块转换成整数后小于模数;
- BT:Block Type,当前版本仅有00、01、02取值;00、01表示私钥操作,00、02表示公钥操作;这里用的是公钥加密,因此为02;
- PS:Padding String,包含k-3-D个字节,k指模数,D为数据;但是!!!
- BT == 00时,填充字节值为0;
- BT == 01时,填充字节值为FF;
- BT == 02时,填充字节应该是伪随机生成的非零值。
- D:原始数据
所以我们来看,使用RSA公钥dump出来的加密前填充数据,PS这一块都是不同的:
因此得到的密文数据肯定是不一样的。
2.2 RSA私钥签名填充解读
为了验证BT对PS的影响,我们继续使用RSA私钥进行签名,代码如下:
dgst -sha224 -sign private_key.pem -out DemoSign1.bin Demo.txt
dgst -sha224 -sign private_key.pem -out DemoSign1.bin Demo.txt
两个签名完全一致,如下图:
那我们老规矩,同样使用公钥验签,并dump出填充后的数据,代码如下:
rsautl -in DemoSign1.bin -out DemoPadSign1.bin -inkey public_key.pem -pubin -verify -raw
rsautl -in DemoSign2.bin -out DemoPadSign2.bin -inkey public_key.pem -pubin -verify -raw
得到的hex,一比较完全一致:BT确实为01,同时PS值全为FF,如下图:
这个填充也满足 EB == 00 || BT || PS || 00 || D;值得注意的是D这里出现了变化,原因也在标准里进行了说明,10.2 Verification process
RSA进行验签时需要有如下步骤:
- Bit-string-to-octet-string conversion
- RSA decryption
- Data decoding
- Message digesting and comparison
在第三步中,对数据D进行ber解码,得到DigestInfo类型的ASN.1值,该值分为消息摘要MD和消息摘要算法标识符。后面这块深入研究了再分析分析。
3.小结
通过上面两次对比,我们可以发现,使用RSA公钥进行加密,因为填充数据每次都是伪随机数生成,因此得到密文不一样;使用RSA私钥进行签名,根据PKCS#1 v1.5的填充规则,PS均为FF,故签名都是一致的。
这种填充方式一旦理解了就变得很容易实现了,所以Cryptography这个库不是那么推荐使用这个进行填充。
It is not recommended that PKCS1v15
be used for new applications, OAEP should be preferred for encryption and PSS should be preferred for signature
后面这几种搞明白了再分享给大家,就酱!!
END
作者:快乐的肌肉
来源:汽车MCU软件设计
推荐阅读:
更多汽车电子干货请关注汽车电子与软件专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。