上次写过一个,后来才发现那个纯属是胡扯的;前面找原根的方法是错的离谱的,这次我改进了一下方案,可以运行,但是运行时间还是有个十几秒到三十秒左右~~
题目描述
题目其实很好理解,总的来说就只有一个难点----求原根
首先来说说什么是原根,想了解什么是原根就要了解什么是阶(描述的可能不准确),还有欧拉函数,详情请自行百度~,首先我不是信息安全专业的,没学信息安全数学基础,所以请不要强求我,我只是一个码代码滴!
第一步:取一个随机的素数q,并且这个素数满足p = 2q + 1也是一个素数
第二步:随机取一个数g(在[2,p - 2]之间),判断g^2 mod p 与 g ^q mod p是否为1;只要有一个为1,说明g不是p的原根,就重新取随机数
第三步:产生公钥与私钥
第四步:利用公钥进行加密
第五步:利用私钥进行加密
第六步:输出要求要展示的数
解析:
第一步:产生素数,其实本身也是一件困难的事(素数要在150位左右),本人能力有限,没有相关筛法的知识,上网搜了搜,在csdn中借鉴了一位大佬的算法,后面自己也写了出来,但是我懒得改了,就用这个了,如果侵犯版权,立马修改!!取出素数q之后,利用费马素性检验算法检测其是不是素数,这个较简单
第二步:第一步中为什么要满足p = 2q + 1也是素数呢?这就要从欧拉函数与原根说起。首先,素数p的欧拉函数为p - 1,它有2个素因数:2,q;聪明的小伙伴已经想出来了,没错根据原根相关的知识,p有φ(φ( p)) 个原根,那么既然如此,只需检验2,q就行(至于为什么请查阅相关原根的资料,我自己一知半解,不敢在此造次);如果检验出来的结果为至少有一个为1那么就可以断言,这个g必定不是原根,若g是模p的原根那么只有当i= p - 1时,g^i mod p == 1!;只有两个检验的数都不为1时,g才是p的原根
第三步:没啥要说的,私钥a请根据要求来取
第四步:利用公钥来加密m时,请注意取的随机数K,它可不是乱取的,它是取之有理;总之k的取值是有要求的,请自行体会,到时候老师会提问的
第五步:利用私钥进行解密,就是计算
相关代码:
费马素性检验模块,其中包括快速模指数算法和求一个数的逆
模块名:Format
import random
# 定义一个函数,用来求 a**b mod c 的值
def quick_algorithm(a, b, c): # 求a**b mod c
a = a % c
ans = 1
while b != 0:
if b & 1:
ans = (ans * a) % c
b >>= 1
a = (a * a) % c
return ans
def Isprime(p,k):
for i in range(k):
a = random.randint(2,p - 2)
if quick_algorithm(a,p - 1, p) != 1:
return 0
return 1
def gcd(a,b):
if b == 0:
return a
else:
return gcd(b,a % b)
def get_inverse(a, m): # 求一个数a 的逆 再模m 的值 这个函数返回的是一个值不是列表
if gcd(a, m) != 1:
return None
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
return u1 % m
取素数模块(完全借鉴的,侵删)
模块名:getprime
# 检测大整数是否是素数,如果是素数,就返回True,否则返回False
import random
def rabin_miller(num):
s = num - 1
t = 0
while s % 2 == 0:
s = s // 2
t += 1
for trials in range(5):
a = random.randrange(2, num - 1)
v = pow(a, s, num)
if v != 1:
i = 0
while v != (num - 1):
if i == t - 1:
return False
else:
i = i + 1
v = (v ** 2) % num
return True
def is_prime(num):
# 排除0,1和负数
if num < 2:
return False
# 创建小素数的列表,可以大幅加快速度
# 如果是小素数,那么直接返回true
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
# 如果大数是这些小素数的倍数,那么就是合数,返回false
for prime in small_primes:
if num % prime == 0:
return False
# 如果这样没有分辨出来,就一定是大整数,那么就调用rabin算法
return rabin_miller(num)
# 得到大整数,默认位数为
def get_prime(key_size=500):
while True:
num = random.randrange(2**(key_size-1), 2**key_size)
if is_prime(num):
return num
获取原根模块:
模块名:Getroot
import Format,random
import getprime
def get_root():
while True:
list = []
q = getprime.get_prime() # 获取一个素数
p = 2 * q + 1
if not Format.Isprime(p, 5): # 判定p是否为素数,不是就结束此次循环,开始下一次循环
continue
a = random.randint(2,p - 1) # 获取一个随机整数,随机数范围为 2 ~ p - 1
if Format.quick_algorithm(a, 2, p) == 1 or Format.quick_algorithm(a, q, p) == 1:
# 若a**2(mod p)或a**q(mod p)等于1,那么说明,这个a不是原根
continue
else:
list.append(p)
list.append(a)
return list
# print(get_root())
ELGamal密钥算法:
import random # 导入random模块
import Getroot
import Format # 导入费马检测算法
# 第二步求模p的原根列表list_g
def get_Ga(): # 定义一个函数,用来求取g**a mod p a是一个随机整数,暂定为[100,1000]
list_key = Getroot.get_root()
a = random.randint(10**100, 10**101) # 获得一个随机整数a
Ga = Format.quick_algorithm(list_key[1], a, list_key[0]) # 调用Format模块中的quick_algorithm()函数
list_key.append(a) # 初始化list_key
list_key.append(Ga)
return list_key
# 将公钥与私钥输出 公钥序数:
list_key = get_Ga()
print('公钥:\np={}\ng={}\n(g**a mod p )={}\n私钥:a={}'.format(list_key[0],list_key[1],list_key[2],list_key[3]))
# Bob用公钥对明文m进行加密
# Bob选取一个随机的整数k
# C1(mod p) = g**k(mod p)
# C2(mod p) = (m*((g**a)**k))(mod p)
with open(r'C:\Users\Acer\Desktop\实验四验收数据\secret2.txt','r',encoding = 'utf8') as file:
m = int(file.read())
# m = int(input('请输入您要进行加密的消息:'))
k = random.randint(2,list_key[0] - 2) # 取一个随机的整数 k
C_1 = Format.quick_algorithm(list_key[1],k,list_key[0])
C1 = C_1 % list_key[0] # 计算C1
C2 = m * Format.quick_algorithm(list_key[1] , list_key[3] * k , list_key[0]) % list_key[0] # 计算C2
print('C1 = {}\nC2 = {}'.format(C1,C2))
# Alice解过程
# V = C1**a(mod p)
# m = C2*V_inverse (mod p)
# V_inverse = Format.get_inverse(v,p)
V = Format.quick_algorithm(C1,list_key[3],list_key[0])
V_inverse = Format.get_inverse(V,list_key[0])
# print(V_inverse * C2)
messege = C2*V_inverse % list_key[0]
print(messege % list_key[0])
本文仅供自己参考
~
~
~
~
~
~
~
~
还有运行时间有些长,要有耐心,男人就是要持久,我是指写代码时能够坐下的时间;可能有些人不服气,他说我这个没用,我说我这个有用,其实根本不在于长短也不在于自己的持久能力;而在于自己的技巧------写代码的技巧,现在这个运行时间可能有些长,但以后,你如果有了一定的技巧,而且又持久,那还不是手到擒来!