什么是RSA算法
涉及到的python运算
求模运算(%):是将两个数相除,并返回余数
random.choice(x):从指定的序列x中随机获取一个数据
幂运算:pow(x, y)指x的y次方
注意当pow(x, y, z)有三个参数时,表示(x^y)%z
math.gcd(x, y):返回给定的整数参数的最大公约数
invert(x, y):求x模y的逆元,invert()方法在gmpy2下,(from gmpy2 import invert)
ord():用于返回一个字符对应的ASCII 数值或Unicode 数值。
实现过程
1、随机生成一个素数
这里选择利用埃氏筛来生成1-10000以内的素数序列
埃氏筛
(此处定义摘自维基百科)埃氏筛:是一种用来生成的筛法,得名于古希腊数学家埃拉托斯特尼。其基本步骤是从最小的素数2开始,将该素数的所有倍数标记成合数,而下一个尚未被标记的最小自然数3即是下一个素数。如此重复这一过程,将各个素数的倍数标记为合数并找出下一个素数,最终便可找出一定范围内所有素数。
优化算法(下述推论在此不进行数学证明,直接使用)
1、寻找𝑁以内的素数时,若找到了一个大于根号N的素数,则剩余的所有尚未标记的数也都是素数。
2、标记某一素数𝑝的倍数时,不需要每次皆从2𝑝,3𝑝,…开始,而可直接从𝑝^2开始标记。
示例
如下图所示,使用埃氏筛法找出120以内的所有素数。由于11^2=121>120,当11成为最小的未标记整数时,尚未标记的所有数皆可确认为素数。
代码
prime_list = []
is_prime = [0 for i in range(m + 1)]
for i in range(2):
is_prime[i] = 1
for n in range(2, m):
if is_prime[n] == 0:
if n <= math.sqrt(m):
prime_list.append(n)
for i in range(n, m // 2):
if n * i <= m:
is_prime[n * i] = 1
else:
break
else:
prime_list.append(n)
print(prime_list)
2、求整数e
随机在区间(1, (n))之间选择一个整数e, 使得gcd(e,
(n)) = 1
下面第一段代码为我自己的思路,不如第二种方法简单,但还是放在这里,请大家指正(温馨提示:经我运行,此种求e方法太浪费时间,大家看看就好了)
x = 100
s = x
# 与x互质的数, 则其整数分解后的质因数一定与x互质
# 得到小于x且不是x因数的质数序列
x_list = primes_in_range(x)
del_list = []
for i in x_list:
if x % i == 0:
print(i)
del_list.append(i)
while x % i == 0:
x = x / i
for i in del_list:
x_list.remove(i)
# 在此序列中随机取最多3个数相乘, 来模拟区间(1, x)内与x互质的随机数
t = 1
i = 1
while i <= 3:
z = random.choice(x_list)
if t * z > s:
break
else:
t *= z
i += 1
print(t)
还有一种最简单粗暴的方法
# 在区间(2, x)随机取数,直到取到数e与x互质
while 1:
e = random.randrange(2, x)
if math.gcd(e, x) == 1:
print(e)
3、模逆运算
扩展欧几里得算法
(此方法详解不在此提及,感兴趣的可以见主页)
# 模逆运算
def gcd(a, b):
while b:
a, b = b, a % b
return a
def stein(a, m):
assert a > 0 and m > 1, "a must be positive and m must be greater than 1"
if a == 1:
return 1
g = gcd(a, m)
if g != 1:
if (m // g) % 2 == 0:
return -1
else:
return pow(a // g, (m // g) - 1, m)
u1, u2, u3 = 1, 0, a
v1, v2, v3 = 0, 1, m
while v3 != 0:
q = u3 // v3
v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2), (u3 - q * v3), v1, v2, v3
if u1 < 0:
u1 += m
return u1
扩展Stein算法
def stein(v, u):
# 若不满足a>0且m>1, 则报错AssertionError
assert v > 0 and u > 1, "a must be positive and m must be greater than 1"
# 特例:1模任何数的逆元均为1
if v == 1:
return 1
x1, x2, x3 = 1, 0, v
y1, y2, y3 = 0, 1, u
while x3 != 1 or y3 != 1:
if x3 % 2 == 0:
if x1 % 2 == 0 and x2 % 2 == 0:
x1, x2, x3 = x1 / 2, x2 / 2, x3 / 2
else:
x1 = x1 + u
x2 = x2 - v
elif y3 % 2 == 0:
if y1 % 2 == 0 and y2 % 2 == 0:
y1, y2, y3 = y1 / 2, y2 / 2, y3 / 2
else:
y1 = y1 + u
y2 = y2 - v
else:
if x3 > y3:
x1, x2, x3 = x1 - y1, x2 - y2, x3 - y3
else:
y1, y2, y3 = y1 - x1, y2 - x2, y3 - x3
if x3 == 1:
return x1
else:
return y1
调用现成的函数
(在最终代码中,选择了简洁的调用库,但与上述的stenin算法是等价的,大家根据爱好选择)
# 引入此库
from gmpy2 import invert
# d即为x模y的逆元
d = invert(x, y)
整体代码
在下述代码中实现每次随机生成一对公私钥对,使用者只需记下公私钥对即可实现RSA加解密
(插个题外话,望大家编写代码时仔细一些,某倒霉人误在调用decrypt()函数时将d输为e,检查了一晚上bug)
import random
import math
from gmpy2 import invert
# 生成m以内的素数序列
def primes_in_range(m):
prime_list = []
is_prime = [0 for i in range(m + 1)]
for i in range(2):
is_prime[i] = 1
for n in range(2, m):
if is_prime[n] == 0:
if n <= math.sqrt(m):
prime_list.append(n)
for i in range(n, m // 2):
if n * i <= m:
is_prime[n * i] = 1
else:
break
else:
prime_list.append(n)
return prime_list
# 生成整数e
def create_e(x):
# 在区间(2, x)内随机取数,直到该数与e互质
while 1:
e = random.randrange(2, x)
if math.gcd(e, x) == 1:
return e
# 用公钥(e,n)将明文m进行加密,这里将每个字符存分别存在list列表里面
def encrypt(m, e, n):
# 将明文的每一字符转换为其对应的ASCII码或Unicode数值, 再进行RSA加密后返回
return [pow(ord(x), e, n) for x in m]
# 用私钥(d,n)将密文cipher进行解密
def decrypt(cipher, d, n):
# 将密文对应的每一个数字进行RSA解密后, 转换为对应的字符, 加入到密文串
return ''.join([chr(pow(x, d, n)) for x in cipher])
if __name__ == '__main__':
prime = primes_in_range(10000)
# 得到两个素数p,q
p = random.choice(prime)
while 1:
q = random.choice(prime)
if p != q:
break
# n, euler_n
n = p * q
euler_n = (p - 1) * (q - 1)
e = create_e(euler_n)
d = invert(e, euler_n)
print("此次RSA的随机数p = {}, q = {}, e = {}".format(p, q, e))
print("可求得公钥(n, e) = ({}, {}), 私钥d = {}".format(n, e, d))
# 测试, 输入明文
plainText = input('请输入测试明文:')
# 求密文, plainText^e % n
cipher = encrypt(plainText, e, n)
print('加密结果=', cipher)
# 求明文, C^d % n
M = decrypt(cipher, d, n)
print('解密结果=', M)
参考:
在 Python 中生成随机质数 | D栈 - Delft Stack
欧拉函数(详细证明+推导) 每日一遍,算法再见!-CSDN博客