1.概述:
DSA(Digital Signature Algorithm)是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard) 数字签名的标准。
DSA是一种更高级的验证方式,它是一种公开密钥算法,不能用来加密数据,一般用于数字签名和认证。DSA 不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥来验证签名的真实性,包括数据的完整性以及数据发送者的身份。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。
DSA数字签名可以理解为是单向加密的升级,不仅校验数据完整性,还校验发送者身份,同时还由于使用了非对称的密钥来保证密钥的安全,所以相比消息摘要算法更安全。
DSA只是一种算法,和RSA不同之处在于它不能用作加密和解密,也不能进行密钥交换,只用于签名,它比RSA要快很多。
2.算法原理
DSA是基于整数有限域离散对数难题的,其安全性与RSA相比差不多。DSA的一个重要特点是两个素数公开,这样,当使用别人的p和q时,即使不知道私钥,也能确认它们是否是随机产生的,还是作了手脚。
3.算法参数定义:
p:一个素模数,其值满足:2^(L-1) < p < 2^L,其中L是64的倍数,且满足512≤ L ≤ 1024
q:(p-1)的素因子,其值满足2^159 < q < 2^160,即q长度为160位。
g:g=h(p-1)/q mod p。h为满足1 < h < p-1 的任意整数,从而有powm(h,(p-1)/q,p) > 1
x:私钥。x为一个随机或伪随机生成的整数,其值满足 0 < x < q。
y:公钥。y=gx mod p
注:
整数p,q,g可以公开,也可以仅由一组特定用户共享。
私钥x和公钥y称为一个密钥对(x,y),私钥只能由签名者本人独自持有,公钥则可以公开发布。密钥对可以在一段时间内持续使用。
4.签名过程:
产生一个随机数k,其值满足 0 < k < q
计算r = (gk mod p) mod q,其值满足 r > 0
计算 s = (k^(-1)(SHA(M) + x * r)) mod q,其值满足 s > 0
注:
k^(-1) 表示整数k关于某个模数的逆元,并非指k的倒数。k在每次签名时都要重新生成,用于不要将同样的k用于进行其他的签名运算!
逆元:满足(a * b) mod m = 1 的a 和 b 互为关于模数 m 的逆元,表示为 a = b^(-1) 或 b = a^(-1)。如(2 * 5) mod 3 = 1,则 2 和 5 互为模数 3 的逆元。
SHA(M): M 的 hash 值,M为待签名的明文。SHA 是一个单向散列函数。DSS中选用SHA1算法,此时SHA(M) 为160 bits长的数字串,其满足不可逆和抗碰撞性。
最终的签名就是证书对(r, s),它们和 M 一起发送到验证方。
尽管 r 和 s 为 0 的概率相当小,但只要有任何一个为 0 ,必须重新生成 k,并重新计算 r 和 s 。
5.验证签名过程:
用(r', s', M') 来表示验证方通过某种途径获得的签名结果,之所以这样表示是因为你不能保证你这个签名的结果一定是发送方生成的真签名相反有可能被人篡改过,甚至掉了包。为了描述简便,下面仍用(r, s, M) 代替(r', s', M')。
为了验证(r, s, M) 的签名是否确由发送方所签,验证方需要有(g, p, q, y),验证过程如下:
计算 w = s^(-1) mod q
计算 u1 = (SHA(M) * w) mod q
计算 u2 = (r * w) mod q
计算 v = ((gu1 × yu2) mod p) mod q
若 v等于 r,则通过验证,否则验证失败
注:
验证通过说明:签名(r, s) 有效,即(r, s, M) 确为发送方的真实签名结果,真实性可以高度信任,M未被篡改,为有效信息。
验证失败说明:签名(r, s) 无效,即(r, s, M) 不可靠,或者M被篡改过,或者签名是伪造的,或者M的签名有误,M为无效信息。
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int xy[22];
int myPow(int a, int b, int m) {
int res = 1;
a %= m;
while (b != 0) {
if ((b & 1) == 1)
res = (res * a) % m;
a = (a * a) % m;
b >>= 1;
}
return res;
}
int calculate(int h,int p,int q){
int a = (p-1)/q;
int k=1;
for(int i = 0;i<a;i++){
k=k*h;
}
return k%p;
}
int calculate1(int g,int x,int p){
int k=1;
for(int i = 0;i<x;i++){
k=k*g;
}
return k%p;
}
// 求 a mod b 的逆元
void exGcd(int a, int b) {
if (b == 0) {
xy[0] = 1;
xy[1] = 0;
} else {
exGcd(b, a % b);
int x = xy[0];
xy[0] = xy[1];
xy[1] = x - (a / b) * xy[1];
}
}
main()
{
int p,q,g,x,y,s,k,m,w,u1,u2,v,h,r;
//初始化
printf("请输入大素数 p和q ,且满足(p-1)能够被q整除");
scanf("%d%d",&p,&q);//素数p和q
srand(time(NULL)); //随机数种子
h=12;//rand()%p-1+2 ;//随机数
g=calculate(h,p,q);
x=10;//rand()%p-1+2 ;//私钥
y=calculate1(g,x,p);//计算公钥
printf("公钥是(%d,%d,%d,%d)\n",p,q,g,y);
printf("私钥为%d\n",x);
//签名过程
k=9;//rand()%p-1+2 ;//随机数k
r=calculate1(g,k,p)%q;
exGcd(k, q);
k = xy[0];
if(k < 0) k += (p-1);
m=13;
s=(m+x*r)*k%q;
printf("签名为(%d,%d)\n",r,s);
//验证
exGcd(s,q);
w =xy[0];
if(w < 0) w += (q);
u1=(m*w)%q;
u2=r*w%q;
v=myPow(g, u1, p)*myPow(y, u2, p)%p%q;
printf("(w,u1,u2,v)=(%d,%d,%d,%d)\n",w,u1,u2,v);
if(v==r){
printf("接受");
}else{
printf("不接受");
}
}