不足 90行代码,只引入了 random
这一个模块,运行速度5秒以内。写了详细的注释。
复杂的只有两个算法
- 欧几里得算法:代码来自维基百科
- 素性检测算法:其中可以设置检测次数
k
import random
def is_prime(num):
"判断是否为素数,10次素性检测"
if num < 2:return False
small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
if num in small_primes: return True # 如果是小素数,那么直接返回true
# 如果大数是这些小素数的倍数,那么就是合数,返回false
for prime in small_primes:
if num % prime == 0:return False
# 用miller_rabin算法对n进行10次检测(错误概率(1/4)^k)
d = num - 1
r = 0
while d % 2 == 0:
d = d // 2
r += 1
for _ in range(10):
a = random.randint(2,num-1) # 随机数
x = pow(a, d, num)
if x == 1 or x == num - 1:continue
for _ in range(r - 1):x = pow(x, 2, num)
if x == 1:return False
if x == num - 1:break
else:return False
return True
def get_prime(key_size=512):
# 生成一个素数
while True:
num = random.randrange(2**(key_size-1), 2**key_size)
if is_prime(num):return num
def ext_euclid(a, b):
"扩展欧几里得算法 返回(x,y 最大公约数): 给定二个整数a、b,必存在整数x、y使得ax + by = gcd(a,b)"
old_s,s=1,0
old_t,t=0,1
old_r,r=a,b
if b == 0:
return 1, 0, a
else:
while(r!=0):
q=old_r//r
old_r,r=r,old_r-q*r
old_s,s=s,old_s-q*s
old_t,t=t,old_t-q*t
while(old_s<0):old_s+=b
return old_s, old_r
if __name__ == "__main__":
# 第一步,随机选择两个不相等的质数p和q
while True:
choice = input("1、手动输入两个素数\n2、随机生成两个大素数\n请选择:")
if choice=='1':
p = int(input("素数1:"))
q = int(input("素数2:"))
else:
p,q = get_prime(),get_prime()
if is_prime(p) and is_prime(q): break
else:print("检测到非质数,重新输入!")
print("生成的大质数为:\n%d\n%d"%(p,q))
# 第二步,计算p和q的乘积n
n=p*q
length=len(bin(n))-2
# 第三步,计算n的欧拉函数φ(n)
e = eular=(p-1)*(q-1)
print("n的欧拉函数φ(n) = %d"%eular)
# 第四步,随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质
while eular%e==0:
e = get_prime(key_size=random.randint(1, length))
print("选择的整数e = %d"%e)
# 第五步,计算e对于φ(n)的模反元素d
d, temp = ext_euclid(a=e,b=eular) # ed ≡ 1(mod eular)
assert temp == 1
print("模反元素d = %d"%d)
# 第六步,将n和e封装成公钥,n和d封装成私钥
print("公钥(n,e)=(%d,%d)"%(n, e))
print("私钥(n,d)=(%d,%d)"%(n, d))
# 第七步,实现加密
print("当前N长度为 %s 位,最长加密长度 %d 字节"%(length,length//8))
plaintext = list(input("请输入要加密的内容:\n").encode())
plaintext_int=int("".join(["{:08b}".format(x) for x in plaintext]),2)
ciphertext = pow(plaintext_int, e, n)
print("密文是:\n",ciphertext)
# 第八步,实现解密
cliphertext_bin = bin(pow(ciphertext, d, n))[2:]
if len(cliphertext_bin)%8!=0:cliphertext_bin = '0' * (8 - len(cliphertext_bin) % 8) + cliphertext_bin # 补0
plaintext = bytes([int(cliphertext_bin[i * 8:(i + 1) * 8], 2) for i in range(len(cliphertext_bin) // 8)]).decode() # 转码
print("解密后的内容是:\n", plaintext)