一、實驗目的
在掌握了ElGamal和Schorr數字簽名算法的基礎上,進一步地學習和掌握DSA簽名算法。深入地理解該算法是如何降低了簽名信息的長度(當其中一個重要參數選爲512bit的素數時,ElGamal簽名的長度爲1024bit,而DSA中通過160bit的素數可以將簽名的長度降低爲320bit),從而減少了存儲空間和傳輸帶寬。
二、實驗要求
4.學習DSA數字簽名算法。
5.掌握如何使用Java BigInteger類,簡單實現最基礎的DSA公私鑰簽名算法。
6.深入地理解DSA簽名算法與RSA算法的區別。
三、開發環境
JDK 1.8,Java開發環境
四、實驗內容
【1-1】DSA簽名算法的實現
- 實現系統參數的設置:根據書本上的知識, DSA公私鑰生成算法首先選擇一個160bit 的素數,接着選擇一個長度在512~1024bit的素數,使得能被整除(),最後選擇,其中是整數,滿足,且。從中隨機地選擇一個整數作爲私鑰,計算,用戶的公鑰爲。具體的代碼如下:
public void initKeys(){
q = new BigInteger(160, 100, new SecureRandom());
do {
BigInteger t = new BigInteger(512, new SecureRandom());
p = t.multiply(q).add(BigInteger.ONE);
System.out.println("~");
}while(!p.isProbablePrime(100));
BigInteger h = _randomInZq();
g = h.modPow(p.subtract(BigInteger.ONE).divide(q), p);
x = _randomInZq();
y = g.modPow(x, p);
System.out.println("p : " + p);
System.out.println("q : " + q);
System.out.println("g : " + g);
System.out.println("x : " + x);
System.out.println("y : " + y);
}
其中,需要在前面定義、、、、、和設置一個隨機函數。代碼分別如下:
public class DSASign {
public BigInteger p,q,g;
public BigInteger x,y;
public BigInteger _randomInZq(){
BigInteger r= null;
do {
System.out.print(".");
r = new BigInteger(160, new SecureRandom());
}while(r.compareTo(q) >=0);
System.out.print(".");
return r;
}
- 實現簽名算法:DSA簽名算法是對待簽名的消息進行簽名,其計算如下兩個分量:
因此,可根據公式,寫代碼如下:
public BigInteger[] signature(byte m[]){
BigInteger k = _randomInZq();
BigInteger sig[] = new BigInteger[2];
sig[0] = g.modPow(k, p).mod(q);
sig[1] = _hashInZq(m).add(x.multiply(sig[0])).mod(q)
.multiply(k.modInverse(q)).mod(q);
return sig;
}
其中選擇的DSA簽名算法中規定了Hash函數爲SHA-1算法:
public BigInteger __hash(byte m[]) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
md.update(m);
byte b[] = new byte[17];
System.arraycopy(md.digest(), 0, b, 1, 16);
return new BigInteger(b);
} catch (NoSuchAlgorithmException e) {
System.out.println("this cannot happen.");
}
return null;
}
3.實現驗證簽名算法:當簽名接收者在接收到消息和簽名信息後,進行如下步驟計算:
實現該驗證算法的代碼如下:
public boolean verify(byte m[], BigInteger sig[]){
BigInteger w = sig[1].modInverse(q);
BigInteger u1 = _hashInZq(m).multiply(w).mod(q);
BigInteger u2 = sig[0].multiply(w).mod(q);
BigInteger v = g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q);
System.out.println("v = " + v);
System.out.println("r = " + sig[0]);
return v.compareTo(sig[0]) == 0;
}
4.實現main方法,在main方法中調用算法進行測試:注意,此處需要修改爲你的名字和學號。
public static void main(String args[]){
DSASign dsa = new DSASign();
dsa.initKeys();
String message = "My name is xxx, my student number is xxxxxx.";
System.out.println(message);
BigInteger sig[] = dsa.signature(message.getBytes());
System.out.println("DSASignture verifies result:" + dsa.verify(message.getBytes(),sig) );
}
【1-2】完整參考代碼。
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
public class DSASign {
public BigInteger p,q,g;
public BigInteger x,y;
public BigInteger _randomInZq(){
BigInteger r= null;
do {
System.out.print(".");
r = new BigInteger(160, new SecureRandom());
}while(r.compareTo(q) >=0);
System.out.print(".");
return r;
}
public BigInteger _hashInZq(byte m[]){
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
md.update(m);
byte b[] = new byte[17];
System.arraycopy(md.digest(), 0, b, 1, 16);
return new BigInteger(b);
}catch (NoSuchAlgorithmException e){
System.out.print("This cannot happen!");
}
return null;
}
public void initKeys(){
q = new BigInteger(160, 100, new SecureRandom());
do {
BigInteger t = new BigInteger(512, new SecureRandom());
p = t.multiply(q).add(BigInteger.ONE);
System.out.println("~");
}while(!p.isProbablePrime(100));
BigInteger h = _randomInZq();
g = h.modPow(p.subtract(BigInteger.ONE).divide(q), p);
x = _randomInZq();
y = g.modPow(x, p);
System.out.println("p : " + p);
System.out.println("q : " + q);
System.out.println("g : " + g);
System.out.println("x : " + x);
System.out.println("y : " + y);
}
public BigInteger[] signature(byte m[]){
BigInteger k = _randomInZq();
BigInteger sig[] = new BigInteger[2];
sig[0] = g.modPow(k, p).mod(q);
sig[1] = _hashInZq(m).add(x.multiply(sig[0])).mod(q)
.multiply(k.modInverse(q)).mod(q);
return sig;
}
public boolean verify(byte m[], BigInteger sig[]){
BigInteger w = sig[1].modInverse(q);
BigInteger u1 = _hashInZq(m).multiply(w).mod(q);
BigInteger u2 = sig[0].multiply(w).mod(q);
BigInteger v = g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q);
System.out.println("v = " + v);
System.out.println("r = " + sig[0]);
return v.compareTo(sig[0]) == 0;
}
public static void main(String args[]){
DSASign dsa = new DSASign();
dsa.initKeys();
String message = "My name is xxx, my student number is xxxxxx.";
System.out.println(message);
BigInteger sig[] = dsa.signature(message.getBytes());
System.out.println("DSASignture verifies result:" + dsa.verify(message.getBytes(),sig) );
}
}
【1-3】算法運行截圖
運行要求:運行算法,DSA簽名驗證結果應該爲true。
【1-4】注意事項:
DSA數字簽名算法主要依賴於整數有限域離散對數難題,素數必須足夠大,且至少包含一個大素數因子以抵抗Pohlig &Hellman算法的攻擊。一般都應採用信息的HASH值。DSA加密算法的安全性主要依賴於和,若選取不當則簽名容易僞造,應保證對於的大素數因子不可約。其安全性與RSA相比差不多。DSA數字簽名算法,它是另一種公開密鑰算法,它不能用作加密,只用作數字簽名(這就是何RSA的區別)。特別要注意的是,要深入地挖掘算法所隱藏的含義,這對我們理解算法和代碼實現極其重要。同時在代碼的實現過程中一定要細心地到底是模還是模,這關乎着驗證過程的正確性。