一、实验目的
掌握利用Hash算法对要传送信息提取消息摘要的方法,理解数字签名的作用及数字签名算法的工作原理,了解多种非对称加密算法都可以用来设计数字签名算法。综合运用前面实验掌握的知识和技术,利用C语言或Java语言设计并实现数字签名程序。
二、实验原理
以往的文件或书信可以通过亲笔签名来证明其真实性,而通过计算机网络传输的信息则通过数字签名技术实现其真实性的验证。
数字签名目前采用较多的是非对称加密技术,其实现原理简单的说,就是由发送方利用Hash算法对要传送的信息进行计算得到一个固定位数的消息摘要值,用发送者的私有密钥加密此消息的Hash值所产生的密文即数字签名。然后将数字签名和消息一同发给接收方。接收方收到消息和数字签名后,用同样的Hash算法对消息进行计算得出新的Hash值,然后用发送者的公开密钥对数字签名解密,将解密后的结果与新的Hash值相比较,如相等则说明报文确实来自发送方。
下面我们以DSA(Digital Signature Algorithm)为例,介绍数字签名算法。DSA源于ElGamal和Schnorr签名算法,被美国NIST采纳作为DSS(DigitalSignature Standard)数字签名标准。
DSS数字签名算法的具体实现过程见图5-1。
首先介绍DSS算法的主要参数:
1.全局公开密钥分量
(1)素数P,2511<p<2512;
(2)q是(p-1)的一个素因子,2159<q<2160:
(3)g=h(p-1)/q mod p,其中h是整数,l<h<(p-1)。
2.私钥
私钥x是随机或伪随机整数,其中0<x<q。
3.公钥
y=gx mod p,(p,q,g,y)为公钥。
4.用户的随机选择数
k为随机或伪随机整数,其中0<k<q。
基于以上参数,DSS的签名过程如下:
r=-(gk mod p)mod q
s=[k-1 (H(M)+xr)]mod q
则形成了对信息M的数字签名(r’,s’),数字签名和信息M一同发送给接收方。接收方接收到信息M’和数字签名(r’,s’)后,对数字签名的验证过程如下:
w=(s’)-1 mod q
ul=[H(M’)w]mod q,u2=(r’)w mod q
V=[(gu1yu2)mod p]mod q
如果v=r’,则说明信息确实来自发送方。
实验代码:
DSA:数字签名和验证
#-*- coding: UTF-8 -*-
"""
DSA:数字签名和验证
@author WQ
@time 2020/12/20
"""
import random
from Crypto.Hash import SHA
import math
class DSA():
"""
签名和验证
"""
def __init__(self):
pass
def Globalpublickey(self):
"""
p:满足2^(L-1)<p<2^L的大素数 其中512<=L<=1024 且L为64的倍数
q:p-1的素因子,满足2^159<2^160,即q长160比特
g:g=h^((p-1)/q)mod p,h是满足1<h<p-1且h^((p-1)/q)mod p>1的任意整数
"""
n=160#q的长度
while True:
Q=random.randrange(2**(n-1)+1,2**n-1,2)
if self.MillerRabin(Q):
self.q=Q
break
while True:
i=random.randint(0,8)#满足L是64的倍数
L=512+i*64
k=random.randint((2**(L-160)-2**(L-163)),(2**(L-160)+2**(L-163)))
p=k*self.q+1
if self.MillerRabin(p):
self.p=p
break
while True:
h=random.randint(2,self.p-2)
#print("shdjsa",(self.p-1)//self.q,k)
g=self.modExp(h,(self.p-1)//self.q,self.p)
if g>1:
self.g=g
break
def UserKey(self):
x=random.randint(1,self.q-1)
self.x = x #Usersecret
self.y=self.modExp(self.g,self.x,self.p) #Publickey
def MillerRabin(self,n):#素数检测算法
"""
判断一个数是否是素数,使用Miller-Rabin概率检测法
n:待检验的数
a:小于n的数
如果n暂时测试为素数,则返回true
"""
if n<2:
return False
if n<=3:
return True
binstr = bin(n-1)[2:]
for i in range(8):
a = random.randint(2, n)
d = 1
for i in binstr:
x = d
d = d*d%n
if d==1 and x!=1 and x!=n-1:
return False
if i=='1':
d = d*a%n
if d!=1:
return False
return True
def modExp(self,a,b,n):
"""
快速幂取模,a^b%n
args{
a:底数
b:指数
n:取模数
}
"""
ans=1
while b:
if b&1:
ans=self.modMul(ans,a,n)
a=self.modMul(a,a,n)
b>>=1
return ans
def modMul(self,a,b,n):
"""
快速积取模,a*b%n
args{
a:乘数
b:乘数
n:取模数
}
"""
ans=0
while b:
if b&1:
ans=(ans+a)%n
a=(a+a)%n
b>>=1
return ans
def Inverse(self, a,mod):
#求秘钥a的逆元 欧几里得算法
x1,x2,x3 = 1,0,mod
y1,y2,y3 = 0,1,a
while(1):
if(y3==0):
g=x3
break
if(y3==1):
g=y3
break
q=math.floor(x3/y3)#向下取整
t1,t2,t3=x1-q*y1,x2-q*y2,x3-q*y3
x1,x2,x3=y1,y2,y3
y1,y2,y3=t1,t2,t3
if y2<0:
y2+=mod
return y2 #逆元求得为y2,y3为gcd(a,26),最大公因数
def signature(self):
self.k=random.randint(1,self.q-1)
self.k_Reverse=self.Inverse(self.k,self.q)
print("用户为待签消息选取的秘密数k:",self.k)
print("秘钥数k的逆元:",self.k_Reverse)
choice=int(input("请选择待签数据类型-对字符串签名[0],对文件签名[1]:"))
if choice==0:
message=input("请输入待签消息:")
self.data=message
Hash=SHA.new(message.encode("utf8")).hexdigest()
self.Hash=int(Hash,16)
self.r=self.modExp(self.g,self.k,self.p)%self.q
self.s=self.modMul(self.k_Reverse,self.Hash+self.x*self.r,self.q)
print("用户对消息的签名为:(r,s)\nr:{}\ns:{}".format(self.r,self.s))
elif choice==1:
filename=input("请输入文件:")
with open(filename,'rb') as fp:
if not fp:
print("请输入正确文件名!")
exit(0)
data=fp.read()
self.data=data
Hash=SHA.new(data).hexdigest()
self.Hash=int(Hash,16)
self.r=self.modExp(self.g,self.k,self.p)%self.q
self.s=self.modMul(self.k_Reverse,self.Hash+self.x*self.r,self.q)
print("用户对消息的签名为:(r,s)\nr:{}\ns:{}".format(self.r,self.s))
return choice
def verification(self,message,flag=0):
if flag==0:
Hash=SHA.new(message.encode("utf8")).hexdigest()
elif flag==1:
Hash=SHA.new(message).hexdigest()
#print("验证",message,Hash)
#print("签名",self.data,hex(self.Hash).replace("0x",""))
Hash=int(Hash,16)
print("接受到的消息的哈希值:",Hash)
w=self.Inverse(self.s,self.q)
u1=self.modMul(Hash,w,self.q)
u2=self.modMul(self.r,w,self.q)
v=self.modMul(self.modExp(self.g,u1,self.p),self.modExp(self.y,u2,self.p),self.p)%self.q
#v1=hex(v).replace("0x","")
#print("v:",v1,len(v1))
#print("r:",hex(self.r).replace("0x",""))
#print(len(str(bin(self.q).replace("0b",""))),len(str(bin(self.p).replace("0b",""))))
#print(v,self.r)
if v==self.r:
print("签名有效!")
else:
print("签名无效!")
if __name__ == "__main__":
test=DSA()
test.Globalpublickey()
test.UserKey()
print("全局公开钥\nP:{}\nQ:{}\nG:{}".format(test.p,test.q,test.g))
print("用户密钥\n秘密钥x:{}\n公开钥y:{}".format(test.x,test.y))
opntion=test.signature()
print("消息哈希值为",test.Hash)
test.verification(test.data,opntion)
while True:
flag=input("是否继续验证?[y][n]:")
if flag.lower()=='y':
#opntion=int(input("验证签名的消息类型-[0]字符串[1]文件:"))
#test.signature()
if opntion==0:
message=input("验证消息:")
test.verification(message)
elif opntion==1:
filename=input("请输入文件:")
with open(filename,'rb') as fp:
if not fp:
print("请输入正确文件名!")
continue
data=fp.read()
test.verification(data,opntion)
elif flag.lower()=='n':
break
运行结果为: