什么是Diffie-Hellman密钥交换算法?
Diffie-Hellman 密钥交换算法的安全性是基于Zp上的离散对数问题。设p是一个满足要求的大素数,并且a(0<a<p)是循环群Zp的生成元,a和p公开,所有用户都可以得到a和p。在两个用户A和B通信时,他们可以通过如下步骤协商通信所使用的密钥:
取素数p和整数a,a是p的一个原根,公开a和p。
A选择随机数XA<p,并计算YA=a^XA mod p。
B选择随机数XB<p,并计算YB=a^XB mod p。
每一方都将X保密而将Y公开让另一方得到。
A计算密钥的方式是:K=(YB) ^XA modp
B计算密钥的方式是:K=(YA) ^XB modp
最终结果:K=a^XA^XB modp,所有两端产生了同样的密钥,实现了交换
个人理解:服务端产生一个大素数和一个原根,然后进行公开(发送给服务端),然后服务端和客户端各产生一个随机数,也就是各自的私钥,然后服务端和客户端各自使用这个大素数、原根和自己的私钥产生一个公钥(publicKey = (g**privateKey)%p)(g为原根),然后进行交换,服务端得到客户端的公钥,客户端得到服务端的公钥,然后各自使用对方的公钥和自己的私钥以及大素数来产生一个最终密钥(Key = (PublicKey**privateKey)%p)
实例代码:
服务端:
# -*- coding: UTF-8 -*-
# 实现环境:python2
# server.py
'''
send函数和recv函数在linux下运行有一定的问题
'''
import socket # 导入 socket 模块
import random
global p
global g
p_Num = [2,3,5,7,11,13,17,19]#小于20的素数
privateKey = random.randint(2**5,2**7) #randint() 返回整数
sharedInformation = {} # 字典,存储p、g,便于传输
#求两个数互素
def relatively_prime(a,b): # a > b
while b != 0:
temp = b
b = a%b
a = temp
if a==1:
return True
else:
return False
#求质数
def creatNumP(min,max):
flag = 0 #判断是否能被前20的素数整除
ind = 1 # 记录while循环次数
print u"求质数:"
while True:
print u"第{0}次:".format(ind)
ind += 1
p = random.randint(min,max)#2^14-2^15
#print u"产生随机数: %d " % p
print u"第一步:\n产生随机数p:{0}".format(p)
if p%2 == 0: #产生寄数
print u"因为{0}不是奇数,重新产生".format(p)
continue
print u"第二步:\n用前20的质数试除:"
for i in p_Num: #先用前20的素数试除
if p%i == 0: #如果可以被除,说明不是素数
flag = 1
print u"{0}能被{1}整除,所以不是质数,重新产生".format(p,i)
break
if flag == 1:
flag = 0
continue
else:
print u"{0}不能被前20的质数整除,所以初步确定是质数".format(p)
#---------------------MR检测算法-------------------------#
rPrime = [] #存放与p互素的数,作为MR检测运算式的底数
for i in range(2,p):
if relatively_prime(i,p):
rPrime.append(i)
if len(rPrime) == 5:
break
print u"第三步:\nMR检测:"
MRflag = 0 #判断是否符合MR检测
# print rPrime
for index,val in enumerate(rPrime):
print u"{0})第{0}次MR检测:".format(index+1)
tmp = p-1
while tmp%2 == 0: #直到幂是奇数
if val**tmp%p==1 or val**tmp%p==p-1:# **幂
print u"{0}^{1}%{2}=={3},满足MR算法检测要求".format(val,tmp,p,val**tmp%p)
tmp = tmp/2
else:
print u"{0}^{1}%{2}=={3},不满足MR算法检测要求,重新产生".format(val,tmp,p,val**tmp%p)
break
if tmp%2 == 0:
MRflag = 1 #未通过
break
if MRflag != 1:
print u"{0} 通过MR检测,产生质数{0}".format(p)
break
else:
print u"{0} 未通过MR检测,重新产生".format(p)
#-------------------------------------------------------#
return p
#求原根目前的做法只能是从2开始枚举,然后暴力判断g^(P-1) = 1 (mod P)是否当且仅当指数为P-1的时候成立。而由于原根一般都不大,所以可以暴力得到。
def creatNumG(p): # 求原根 应用密码学P73
print u"\n求原根:"
while True:
g = random.randint(2**5,2**10) # 产生一个在范围内的随机数
if g**(p-1)%p==1:
print u"{0}^({1}-1)%{1}==1,所以{0}为质数{1}的原根\n".format(g,p)
break
return g
def creatPublicKey(privateKey,p,g): # 需要交换的公钥
publicKey = (g**privateKey)%p
return publicKey
def creatKey(PublicKey,privateKey,p):
Key = (PublicKey**privateKey)%p
return Key #最终密钥
s = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获得本地主机名
port = 6666 # 设置端口
s.bind((host,port)) # 绑定端口
s.listen(5) # 等待客户端连接 5表示挂起的最大连接数量
while True:
print u"等待连接..."
c,addr = s.accept() # 建立客户端连接
print u"连接地址:",addr,"\n"
p = creatNumP(2**14,2**15) # 素数p
g = creatNumG(p) # 原根g
sharedInformation['primitiveRoot'] = g # 原根
sharedInformation['primeNum'] = p # 素数 prime number
myPublicKey = creatPublicKey(privateKey,p,g)
c.send(str(sharedInformation).encode())# 字典转换成字符串
c.send(str(myPublicKey).encode())# 字典转换成字符串
clientPublicKey = c.recv(1024).decode() # 1024接收数据最大字节长度
Key = creatKey(int(clientPublicKey),privateKey,p)
print u"共有信息:",str(sharedInformation),u"\n服务器公钥:",myPublicKey,u"\n客户端公钥:",clientPublicKey,u"\n密钥:",Key,u"\n"
c.close() # 关闭连接
客户端:
#!D:\Python\python.exe
# -*- coding: UTF-8 -*-
#!/usr/bin/python
# client.py
import socket # 导入 socket 模块
import random
privateKey = random.randint(2**5,2**7)
sharedInformation = {}
def creatPublicKey(privateKey,p,g):
publicKey = (g**privateKey)%p
return publicKey
def creatKey(PublicKey,privateKey,p):
Key = (PublicKey**privateKey)%p
return Key
s = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 6666 #设置端口号
s.connect((host,port))
sharedInformation = eval(s.recv(1024)).decode() # 字符串转化成字典
serverPublicKey = s.recv(1024).decode() # 1024接收数据最大字节长度
Key = creatKey(int(serverPublicKey),privateKey,sharedInformation['primeNum'])
myPublicKey = creatPublicKey(privateKey,sharedInformation['primeNum'],sharedInformation['primitiveRoot'])
s.send(str(myPublicKey).encode())
print u"共有信息:",sharedInformation,u"\n服务器公钥:",serverPublicKey,u"\n客户端公钥:",myPublicKey,u"\n密钥:",Key
s.close()
此代码内容简单,但相关功能都大致实现。
核心功能是MR检测算法,MR算法大致概念:就是找到一些与这个素数n互素的数b,然后计算这些数b的次方模素数n的值,结果为1或者n-1,则通过MR检测,说明这个数n大概率是素数,若果有不等于1或者n-1,则这个数n肯定不是素数。注:这里的b的次方要满足一定规律,先是tmp=n-1,tmp=n-1/2,tmp=n-1/4,tmp=n-1/8...,直到tmp为奇数结束检测,只要其中又一次检测失败,则说明未通过MR检测。这里有个我写的一个独立的Miller-Rabin算法,可以看看。
(本人学习编程刚入门,所以在命名方面可能有些难懂,请谅解)