我已经完成了hash值计算和DES部分,目前hash值利用Python内建的hashlib计算,DES采用了pyDes库,暂时还算顺利吧。
RSA部分找到了Python-RSA,这样一来我用了3个库。真是自找罪受,Python有很多用于加解密的库,可以进行所有的加解密操作,比如PyCryptodome、oscrypto、cryptography。以后重写一个就是了~~~
前方高能预警! 都是干货!
一、RSA的一点点知识
RSA的历史和细节我就不操心了,只关注一些编程中会用到的内容。
密钥用途
RSA是公钥密码体制,它的密钥分为公钥和私钥,由n,e,d三个数字组成,公钥为(e,n),私钥为(d,n),对应实例下面会写。
其中公钥是全网可知的,私钥只有用户自己知道,即只有d是用户自己保管的。
RSA的实际用途有两个:
若要加密,是希望密文只有接收方可以解密,也就只能由接收方用自己的私钥解密,所以发送方用接收方的公钥加密信息后发送给接收方。
若要签名,是希望接收方可以肯定信息是由发送方发来的,那么信息就必须用只有发送方知道的密钥加密也就是用发送方的私钥加密,然后接收方用发送方的公钥解密,以判断信息是不是由发送方发来。
可以看出来,不论哪种用途,要正常使用RSA,公钥(e,n)就是收发双方已知的。
密钥长度
在RSA中密钥是成对出现的,我们所说的密钥长度就是n的位数,现在主流的位长有1024、2048、3072、4096,在考虑安全和时延的平衡下,建议用1024位即128字节。512位RSA已不建议使用。
关于公钥和私钥的长度完全不用操心,实际中他们的长度必小于n的位长,且由于分组密码的特性,在加密过程中会分别填充至n的位长。但在编程中,除非要造轮子,一般是没必要纠结这个的。
密文长度
用RSA加密有两种情况:
- 明文未分块加密,密文长度肯定是n的长度
- 明文长度过长,分块加密,密文的长度就是n的长度*分块数
研究长度为什么?因为程序混合加密需要用RSA加密DES密钥,还要和密文放在一个文件里发送,知道了长度就容易把密钥密文单独分离出来。
明文长度
按照通用惯例,明文使用PKCS1PADDING填充,这会默认占用11个字节的明文空间,若n为1024位即128字节,则只有117字节明文可以加密。
如果明文为118字节,就只能分成两块加密;当然,此时密文是两个1024位也就是2048位即256字节。在单独用RSA加密部分会用到这个方法。
还有一点,‘分块’中的块是英文的‘block’,这在很多地方也翻译成分组加密。
二、Python-RSA介绍
github有文档和源码,很多地方还是要研究下滴。
安装
这一步还是老规矩pip install rsa
,光看安装总有种官方正版的赶脚。
生成密钥对
> import rsa
> (pub_key,priv_key) = rsa.newkeys(1024)
此时会生成n为1024位的密钥对,pub_key包含e和n,priv_key包含e、n、d、p、q。其中n的十进制数字是p和q的乘积。
> priv_key.n == priv_key.p * priv_key.q
True
这两个数字在加解密中是不会用到的,却是这一组密钥的核心,一旦泄露,攻击者就可以计算出d的值。
作者给公钥和私钥定义了两个数据类型,所以此时的密钥对是这样的:PublicKey(n,e)和PrivateKey(n,e,d,p,q),如果要访问n,可以使用pub_key.n
加解密
加解密使用非常简单,通过例子一目了然,沿用上面生成的密钥对:
> message = 'I love Python'
> crypt_message = rsa.encrypt(message.encode('utf-8'),pub_key)
> len(crypt_message)
128
> dec_message = rsa.decrypt(crypt_message,priv_key)
> dec_message
b'I love Python'
> dec_message.decode('utf-8')
'I love Python'
请小心,python-rsa以前是写给Python2用的,因此文档例子里message是没有编码就进行加密的,而在Python3中这行不通,不编码就不给我加密~~
我测了密文的长度是128字节,也就是1024位,和n相同。
然后,在解密的时候只能先解密,再解码。因为密文是不能解码的,最起码是不能用’utf-8’解码的。
签名和验证
所谓签名,就是用发送方的私钥加密信息,因私钥只有发送方拥有,所以可以证明消息的来源。
验证是指验证消息的完整性,它的原理是hash值的单向性和抗碰撞性,即由hash值是无法推出明文的,找到任何两段hash值相同的明文是不可行的。因此验证的工作就是将收到的明文再进行一次hash计算,并与收到的hash值比较,相同则明文可信。
python-rsa提供了rsa.sign()和rsa.verify()方法实现签名和验证。
# 签名,参数为明文、发送方私钥、hash算法
signature = rsa.sign(message,priv_key,'SHA-1')
#验证,参数为明文、签名信息、发送方公钥
verification = rsa.verify(message,signature,pub_key)
rsa.sign()中可选的hash算法有’MD5’,‘SHA-1’,‘SHA-256’,‘SHA-384’,‘SHA-512’,在rsa.verify()中可以自动识别出hash值采用的算法,因为每种hash算法得到的hash值位数是不同的。
注:因为这里有简单的签名和验证方法,所以我在最前面写的hash部分不会出现在最终的程序里。
本篇最后搞明白易混淆的数字位数概念
在写DES部分时,32位8字节的密钥需要输入8个字母或数字,一位数字在存储时会占一个字节,请注意这里是‘一位’和‘存储’。但在1024位RSA中生成的n是308个数字(有时也是309个),为什么?
请正确区分‘存储’和‘表示’!用Python的bin()转换二进制并去掉前面的’0b’,n的长度就是1024位。可以这么说,我选择1024位RSA要生成的是大小可用1024位二进制数或309位十进制数表示的n,而这个n,在存储时占的空间就是308字节。
可简单的验证:
> import rsa
> (pub_key,priv_key) = rsa.newkeys(1024)
> len(str(pub_key.n)) #len()只能测量字符串的长度,而n是一个int
308
> with open('pub_n.txt','w') as f:
f.write(str(pub_key.n))
f.close()
308
生成的pub_n.txt大小正是308字节:
Done!完成RSA部分后再发下一篇。