1、RSA实现原理
1.1数学知识准备
# RSA加密算法中,用到素数、互质数、指数运算、模运算等几个简单的数学知识
# 素数
# 素数又称质数,指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数
# 互质数
# 百度百科上的解释是:公因数只有1的两个数,叫做互质数。
# 维基百科上的解释是:互质,又称互素。若N个整数的最大公因子是1,则称这N个整数互质。
# 常见的互质数判断方法主要有以下几种:
# 两个不同的质数一定是互质数。例如,2与7、13与19。
# 一个质数,另一个不为它的倍数,这两个数为互质数。例如,3与10、5与 26。
# 相邻的两个自然数是互质数。如 15与 16。
# 相邻的两个奇数是互质数。如 49与 51。
# 较大数是质数的两个数是互质数。如97与88。
# 小数是质数,大数不是小数的倍数的两个数是互质数。例如 7和 16。
# 2和任何奇数是互质数。例如2和87。
# 1不是质数也不是合数,它和任何一个自然数在一起都是互质数。如1和9908。
# 辗转相除法。
# 指数运算
# 指数运算又称乘方计算,计算结果称为幂。nm指将n自乘m次。把nm看作乘方的结果,叫做”n的m次幂”或”n的m次方”。其中,n称为“底数”,m称为“指数”。
# 模运算
# 模运算即求余运算。“模”是“Mod”的音译。和模运算紧密相关的一个概念是“同余”。数学上,当两个整数除以同一个正整数,若得相同余数,则二整数同余。
# 两个整数a,b,若它们除以正整数m所得的余数相等,则称a,b对于模m同余,记作: a ≡ b (mod m);读作:a同余于b模m,或者,a与b关于模m同余。例如:26 ≡ 14 (mod 12)。
1.2公钥与密钥的产生
# 假设A想要通过一个不可靠的媒体接收B的一条私人讯息。她可以用以下的方式来产生一个公钥和一个私钥:
# 随意选择两个大的质数p和q,p不等于q,计算N=pq。
# 根据欧拉函数,求得r = (p-1)(q-1)
# 选择一个小于 r 的整数 e,求得 e 关于模 r 的模反元素,命名为d。(模反元素存在,当且仅当e与r互质)
# 将 p 和 q 的记录销毁。
# (N,e)是公钥,(N,d)是私钥。A将她的公钥(N,e)传给B,而将他的私钥(N,d)藏起来。
1.3加密消息
# 假设B想给A送一个消息s,他知道A产生的N和e。他使用起先与A约好的格式将s转换为一个小于N的整数n,
# 比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。假如他的信
# 息非常长的话,他可以将这个信息分为几段,然后将每一段转换为n。用下面这个公式他可以将n加密为c:
#
# ne ≡ c (mod N)
#
# 计算c并不复杂。B算出c后就可以将它传递给A。
1.4解密消息
# A得到B的消息c后就可以利用她的密钥d来解码。她可以用以下这个公式来将c转换为n:
#
# cd ≡ n (mod N)
#
# 得到n后,她可以将原来的信息s重新复原。
#
# 解码的原理是:
#
# cd ≡ n e·d(mod N)
#
# 以及ed ≡ 1 (mod p-1)和ed ≡ 1 (mod q-1)。由费马小定理可证明(因为p和q是质数)
#
# n e·d ≡ n (mod p) 和 n e·d ≡ n (mod q)
#
# 这说明(因为p和q是不同的质数,所以p和q互质)
#
# n e·d ≡ n (mod pq)
2、代码实例
import rsa
(publickey, privatekey) = rsa.newkeys(1000) # 对数字1000加密得到公钥和私钥
pub = publickey.save_pkcs1() # 获取公钥
# ***********将公钥保存到文件*************
with open("public.pem", 'w+') as f:
f.write(str(pub, encoding='utf-8'))
pri = privatekey.save_pkcs1() # 获取私钥
# ***********将私钥保存到文件***********
with open("private.pem", 'w+') as f:
f.write(str(pri, encoding='utf-8'))
string = "laomomoblog" # 待加密的字符串
# ***********取出公钥***********
with open('public.pem', 'r') as file_pub:
f_pub = file_pub.read()
pubkey = rsa.PublicKey.load_pkcs1(f_pub)
# ***********取出私钥***********
with open('private.pem', 'r') as file_pri:
f_pri = file_pri.read()
prikey = rsa.PrivateKey.load_pkcs1(f_pri)
# 加密字符串string
crypt = rsa.encrypt(string.encode('utf-8'), pubkey) # 使用公钥去加密字符串
# 解密
de_crypt = rsa.decrypt(crypt, prikey) # 用私钥去解密
print(de_crypt)
# 解出来的de_crypt与string应该是相等的,判断一下
if string == str(de_crypt, encoding='utf-8'):
print('正确')
else:
print('错误')
3、小技巧及注意
3.1生成密钥的时间
生成密钥对可能需要很长时间,具体取决于所需的位数。位数决定了密钥的加密强度,以及可以加密的消息的大小。如果您不介意使用比您要求的更小的密钥,您可以通过accurate=False
加速密钥生成过程。
加速密钥生成过程的另一种方法是并行使用多个进程来加速密钥生成。使用不超过机器可以并行运行的进程数; 双核机器应该使用poolsize=2
; 一个四核超线程机器可以在每个核心上运行两个线程,因此可以使用poolsize=8
。
(pubkey, privkey) = rsa.newkeys(512, poolsize=8)
这些是使用64位CPython 2.7的台式机(Linux 2.6,2.93 GHz四核Intel Core i7,16 GB RAM)的平均时序。由于密钥生成是随机过程,因此即使在类似的硬件上,时间也可能不同。在所有测试中,我们使用默认值accurate=True
。
Keysize(位) | 单一过程 | 八个过程 |
128 | 0.01秒 | 0.01秒 |
256 | 0.03秒 | 0.02秒 |
384 | 0.09秒 | 0.04秒 |
512 | 0.11秒 | 0.07秒 |
1024 | 0.79秒 | 0.30秒 |
2048 | 6.55秒 | 1.60秒 |
3072 | 23.4秒 | 7.14秒 |
4096 | 72.0秒 | 24.4秒 |
如果密钥生成对您来说太慢,您可以使用OpenSSL为您生成它们,然后将它们加载到Python代码中。OpenSSL在3.5秒内在上面使用的同一台机器上生成一个4096位的密钥。
3.2秘钥大小要求
Python-RSA 3.0版引入了PKCS#1风格的随机填充。这意味着您的密钥的11个字节(88位)不再可用于加密,因此小于此的密钥将无法使用。密钥越大,安全性越高。
创建签名还需要一定大小的密钥,具体取决于使用的哈希方法:
哈希方法 | 建议的最小密钥大小(位) |
MD5 | 360 |
SHA-1 | 368 |
SHA-256 | 496 |
SHA-384 | 624 |
SHA-512 | 752 |
3.3签名与验证
使用rsa.sign()函数为消息创建分离签名 :
(pubkey, privkey) = rsa.newkeys(512)
message = 'Go left at the blue tree' # 自定义签名
signature = rsa.sign(message, privkey, 'SHA-1') # SHA-1加密
这使用SHA-1对消息进行哈希处理。其他哈希方法也是可能的,请查看rsa.sign()功能文档以获取详细信息。然后使用私钥对哈希进行签名。
要验证签名,使用rsa.verify() 功能。如果验证成功,此函数返回True:
message = 'Go left at the blue tree'
if rsa.verify(message, signature, pubkey):
print(123) # 正确返回True
修改消息,签名不再有效, rsa.pkcs1.VerificationError抛错
:
message = 'Go right at the blue tree' # 修改签名
rsa.verify(message, signature, pubkey)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/sybren/workspace/python-rsa/rsa/pkcs1.py", line 289, in verify
raise VerificationError('Verification failed')
rsa.pkcs1.VerificationError: Verification failed
3.4 PS:使用大文件
RSA只能加密小于密钥的消息。随机填充会丢失几个字节,其余字节可用于消息本身。例如,512位密钥可以编码53字节的消息(512位= 64字节,11字节用于随机填充和其他内容)。
4、PS
1、加密后文件会变大,因为每个加密块有8个字节的随机填充和3个字节的开销。
2、rsa.sign()并rsa.verify()处理任意长文件,因此它们没有“bigfile”等价物