DSA数字签名原理及JAVA实现
DSA数字签名
DSA数字签名是Elgamal和Schnorr数字签名的一个变种,DSA数字签名优于Elgamal数字签名的地方在于它的签名长度较短,并且某些可以**Elgamal方案的攻击不适用DSA数字签名,DSA数字签名的原理如下:
1. 首先生成一个素数p,p满足 2^L-1<p<2^L ;
注:关于L的值的范围看到两种不同的说法
a):L是1024、2048、3072三个值中的一个
b):L满足512<=L<=1024 ,且L是64的倍数
2. 然后再生成q,再此处,假定L=1024,找到一个素数q,q满足 q能整除p-1,即 (p-1)mod q=0,且2^159< q <2^160;
3. 然后再生成g;
g=h^(p-1)/qmod p, h满足1<h<(p-1),并且g>1
4. 最后生成用户的私钥x和公钥y
x是随机数或者伪随机数,且满足:0< x <q
y 满足 y=g^x mod p
5. 签名,DSA签名也是由两个整数r、s构成,下面是r、s的获取方式:
r =(g^k mod p) mod q
s = [k^-1(H(M) + xr) ] mod q
注:M是明文消息,H(M)是明文消息的哈希值,k是临时**
6.验证,假设收到的明文为M’,收到的签名为s’、r’,则验证方式如下:
w=(s’)^-1mod q
u1=[H(M’)w] mod q
u2=(r’)wmod q
v=[(g^u1 · y^u2) mod p] mod q
检验 v=r’ 签名有效,反之则签名无效;
举例:B 发消息给A,使用DSA算法进行签名
1.生成素数p=59、素数q=29、h=11、私钥x=7,临时**k=10,消息摘要H(M)=26
2.生成g:
g=h^(p-1)/qmod p → g=11^2 mod 59 → g=3
3.计算公钥y
y=g^xmod p → y=3^7 mod 59 →y=2187 mod 59 →y=4
4.进行签名计算
r = (g^k mod p) mod q → r=(59049 mod 59) mod 29 →r=20
s = [k^-1 (H(M) + xr) ] mod q → s=3·(26+140)mod 29 → s=5
5.A收到消息后进行签名验证
w=(s’)^-1mod q → w=6 mod 29 =6
u1=[H(M’)w] mod q → u1=156 mod 29 = 11
u2=(r’)wmod q → u2=120 mod 29=4
v=[(g^u1 · y^u2) mod p] mod q → v= (45349632 mod 59) mod 29 =20
v=r=20
6.验证成功;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.spec.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class DsaSignCode {
private static final String SIGNATURE_ALGORITHM="SHA1withDSA";
private static final String ALGORITHM = "DSA";
private static final int KEY_SIZE = 1024;
// 该方法取一个随机x,x小于q并且大于0,
private static BigInteger randbint(BigInteger n){
Random rnd = new Random();
int maxNumBitLength = n.bitLength();
BigInteger aRandomBigInt;
do{
aRandomBigInt = new BigInteger(maxNumBitLength, rnd);
}while (aRandomBigInt.compareTo(n) > 0);
return aRandomBigInt;
}
public Map<String,BigInteger> DsaKey() throws NoSuchAlgorithmException {
KeyPairGenerator ****** = KeyPairGenerator.getInstance(ALGORITHM);
//初始化
******.initialize(KEY_SIZE);
KeyPair keyPair = ******.genKeyPair();
//使用KeyPair生成**
DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
// DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
DSAParams dsaParams = privateKey.getParams();
BigInteger p = dsaParams.getP();
BigInteger q = dsaParams.getQ();
BigInteger g = dsaParams.getG();
BigInteger x = randbint(q);
BigInteger y = g.modPow(x, p);
//使用HASHMAP存储p,q,g,x,y
Map<String,BigInteger> map = new HashMap<String,BigInteger>(5);
map.put("KEY_P",p);
map.put("KEY_Q",q);
map.put("KEY_G",g);
map.put("KEY_X",x);
map.put("KEY_Y",y);
return map;
}
//得到标准的公钥
public PublicKey getPublicKey(BigInteger y,BigInteger p,BigInteger q,BigInteger g) throws NoSuchAlgorithmException, InvalidKeySpecException {
DSAPublicKeySpec dsaPublicKeySpec = new DSAPublicKeySpec(y,p,q,g);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
//根据提供的**规范(**材料)生成公钥对象
return keyFactory.generatePublic(dsaPublicKeySpec);
}
//得到标准的私钥
public PrivateKey getPrivateKey(BigInteger x,BigInteger p,BigInteger q,BigInteger g) throws NoSuchAlgorithmException, InvalidKeySpecException {
DSAPrivateKeySpec dsaPrivateKeySpec = new DSAPrivateKeySpec(x,p,q,g);
PrivateKey privateKey =null;
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
//根据提供的**规范(**材料)生成私钥对象
privateKey= keyFactory.generatePrivate(dsaPrivateKeySpec);
return privateKey;
}
import javax.xml.bind.DatatypeConverter;
import java.math.BigInteger;
import java.util.Map;
public class DsaSignTest {
public static void main(String args[]) throws Exception {
String str="123456";
byte[] content=str.getBytes();
//获取DsaKey()内的HASHMAP里面的键值对
DsaSignCode dsaCode= new DsaSignCode();
Map keyMap = dsaCode.DsaKey();
BigInteger p= (BigInteger)keyMap.get("KEY_P");
BigInteger q= (BigInteger)keyMap.get("KEY_Q");
BigInteger g= (BigInteger)keyMap.get("KEY_G");
BigInteger y= (BigInteger)keyMap.get("KEY_Y");
BigInteger x= (BigInteger)keyMap.get("KEY_X");
DsaSignCode dsaSignCode = new DsaSignCode();
//签名并存储到数组内,再将其转为16进制
byte[] dsaSignc = DsaSignCode.sign(content,dsaSignCode.getPrivateKey(x,p,q,g));
String stringDate = DatatypeConverter.printHexBinary(dsaSignc);
//验证
boolean veriftDsa= DsaSignCode.verify(content,dsaSignCode.getPublicKey(y,p,q,g),dsaSignc);
System.out.println("P:"+p);
System.out.println("Q:"+q);
System.out.println("X:"+x);
System.out.println("Y:"+y);
System.out.println("G:"+g);
System.out.println("签名为:"+stringDate);
System.out.println("签名验证:"+veriftDsa);
}
}
上一篇: Python实现RSA数字签名