數(shù)字簽名技術(shù) -- DSA算法
DSA算法是1991年美國國家標準技術(shù)協(xié)會公布的數(shù)字簽名標準(DSS)的核心算法。DSA算法本質(zhì)是ElGamal數(shù)字簽名算法,僅能與SHA系列算法結(jié)合,沒有相應的MD融合算法。
DSA算法和RSA算法的異同
DSA算法是DSS技術(shù)的核心算法,與RSA算法的異同如下:
- 二者都是數(shù)字簽名算法中的重要組成,缺一不可;
- DSA算法僅僅包含數(shù)字簽名算法,一般不用于加密算法中;
- DSA算法僅產(chǎn)生數(shù)字證書,信息僅能用于驗證,不適合進行加密通信,所以HTTPS不會使用這個算法;
- RSA算法包含加解密的密鑰信息,同時可作為數(shù)字簽名算法;
下面是一段DSA和RSA的不同的比較原文,可以參考下:
The main difference is in RSA ,message hash value is generated then this hash value is encryption using sender's private key this is treated as a signature and this signature is pretended with message .then receiver side perform decryption using sender's public key and this compare with new hash value that are generate by msg if both are same then msg is accepted otherwise rejected.
But in DSS approach additional add a signing algorithm that take input as a hash value of msg,random key ,global public key and sender's private key then the output is pretended to the msg . at receiver side pass the result (output), signature and msg, as well as global public key using this four take as the input of verification function then output of verification function is compare with result. The main difference is in RSA compare with hash value other hand in DSS output of verification is compare with result.
算法基本流程
- 發(fā)送方:使用信息摘要算法對原始數(shù)據(jù)計算
摘要; - 發(fā)送方:產(chǎn)生一個隨機數(shù),將
摘要信息、隨機數(shù)、私鑰、全局公鑰(不是私鑰對應的公鑰)傳入到算法中; - 發(fā)送方:算法計算一個簽名串,將簽名串和原始數(shù)據(jù)一起傳輸;
- 接收方:對原始數(shù)據(jù)計算
摘要; - 接收方:將接受到的簽名串發(fā)送給驗證函數(shù),驗證函數(shù)使用
發(fā)送方公鑰和全局公鑰來比較驗證簽名信息;
算法家族
包含:SHA1withDSA、SHA224withDSA、SHA256withDSA、SHA384withDSA、SHA512withDSA五種算法。
密鑰長度都是512-1024位。
Java中的算法實現(xiàn)
JDK實現(xiàn)了DSA算法,但僅僅實現(xiàn)了SHA1withDSA算法。
示例代碼,代碼和RSA數(shù)字簽名算法的一樣,只不過是算法名字變了下,可見Java的API設計還是很不錯的。
public class SignatureTest {
//唯一不一樣的是這里
public static final String SIGN_ALGORITHM = "SHA1withDSA";
private static final String KEY_ALGORITHM = "RSA";
private static final int KEY_SIZE = 1024;
public static void main(String[] args) throws Exception {
KeyPair keyPair = initKey();
String input = "Sign Me";
byte[] sign = sign(input.getBytes(), keyPair.getPrivate().getEncoded());
boolean verify = verify(input.getBytes(), sign, keyPair.getPublic().getEncoded());
String msg = String.format("原始數(shù)據(jù): %s , Sign : %s , Verify : %s", input, toBase64(sign), verify);
System.out.println(msg);
// 從二進制位角度看,sign的長度和密鑰長度一致
System.out.println("Sign Size : " + (sign.length * 8) + " Key Size : " + KEY_SIZE);
}
public static KeyPair initKey() throws Exception {
KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGr.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGr.generateKeyPair();
return keyPair;
}
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 將byte[]的key格式化回來
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 獲取算法實例并初始化
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
byte[] sign = signature.sign();
return sign;
}
public static boolean verify(byte[] data, byte[] sign, byte[] publicKey) throws Exception {
// 獲取算法實例并初始化
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
// 驗證數(shù)據(jù)和簽名是否一致,放否認,放篡改
boolean verify = signature.verify(sign);
return verify;
}
public static String toBase64(byte[] data) {
return new String(Base64.getEncoder().encode(data));
}
}