數字簽名在數據的交互中一直都佔據着很重要的地位,因此,這篇文章對其原理進行整理總結一下。最後再給出代碼的實現。
一、簡單認識
相信我們都寫過信,在寫信的時候落款處總是要留下自己的名字,用來表示寫信的人是誰。我們籤的這個字就是生活中的簽名:
而數字簽名呢?其實也是同樣的道理,他的含義是:在網絡中傳輸數據時候,給數據添加一個數字簽名,表示是誰發的數據,而且還能證明數據沒有被篡改。
OK,數字簽名的主要作用就是保證了數據的有效性(驗證是誰發的)和完整性(證明信息沒有被篡改)。下面我們就來好好的看一下他的底層實現原理是什麼樣子的。
二、基本原理
爲了理解的清楚,我們通過案例一步一步來講解。話說張三有倆好哥們A、B。由於工作原因,張三和AB寫郵件的時候爲了安全都需要加密。於是張三想到了數字簽名:
整個思路是這個樣子的:
第一步:加密採用非對稱加密,張三有三把鑰匙,兩把公鑰,送給朋友。一把私鑰留給自己。
第二步:A或者B寫郵件給張三:A先用公鑰對郵件加密,然後張三收到郵件之後使用私鑰解密。
第三步:張三寫郵件給A或者B:
(1)張三寫完郵件,先用hash函數生成郵件的摘要,附着在文章上面,這就完成了數字簽名,然後張三再使用私鑰加密。就可以把郵件發出去了。
(2)A或者是B收到郵件之後,先把數字簽名取下來,然後使用自己的公鑰解密即可。這時候取下來的數字簽名中的摘要若和張三的一致,那就認爲是張三發來的,再對信件本身使用Hash函數,將得到的結果,與上一步得到的摘要進行對比。如果兩者一致,就證明這封信未被修改過。
上面的流程我們使用一張圖來演示一下:
首先把公鑰送給朋友A和B:
然後呢,就是朋友A或者B給張三發郵件:
還有就是最後一個比較麻煩的,張三給A或者B發郵件:
OK,上面的這幾張圖想必你應該能夠理解清楚了,其實還有一些很複雜的情況,因爲上面的數字簽名是在理想狀態下完成的,但是如果遇到了公鑰錯誤,摘要不正確該如何處理呢?這裏就涉及到數字證書了,我們來分析一下。
三、數字證書
上面提到我們對簽名進行驗證時,需要用到公鑰。如果公鑰是僞造的,那我們無法驗證數字簽名了,也就根本不可能從數字簽名確定對方的合法性了。這時候證書就閃亮登場了。我們可能都有考各種證書的經歷,比如說普通話證書,四六級證書等等,但是歸根結底,到任何場合我們都能拿出我們的證書來證明自己確實已經考過了普通話,考過了四六級。這裏的證書也是同樣的道理。
如果不理解證書的作用,我們可以舉一個例子,比如說我們的畢業證書,任何公司都會承認。爲什麼會承認?因爲那是國家發的,大家都信任國家。也就是說只要是國家的認證機構,我們都信任它是合法的。
那麼這個證書是如何生成的呢?我們再來看一張圖:
此時即使張三的朋友A把公鑰弄錯了,張三也可以通過這個證書驗證。
四、代碼驗證
常用的數字簽名算法有:RSA、DSA、ECDSA。這裏的代碼參考了慕課網。下面給出三種方式的代碼實現:
1、RSA
public static void jdkRSA() {
try {
//1.初始化密鑰
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
//2.執行簽名
PKCS8EncodedKeySpec pkcs8EncodedKeySpec =
new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
signature.update(src.getBytes());
byte[] result = signature.sign();
//3.驗證簽名
X509EncodedKeySpec x509EncodedKeySpec =
new X509EncodedKeySpec(rsaPublicKey.getEncoded());
keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
signature.update(src.getBytes());
boolean bool = signature.verify(result);
System.out.println("jdk rsa verify : " + bool);
} catch (Exception e) {
e.printStackTrace();
}
}
2、DSA
public static void jdkDSA() {
try {
//1.初始化密鑰
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
DSAPublicKey dsaPublicKey = (DSAPublicKey) keyPair.getPublic();
DSAPrivateKey dsaPrivateKey = (DSAPrivateKey)keyPair.getPrivate();
//2.執行簽名
PKCS8EncodedKeySpec pkcs8EncodedKeySpec =
new PKCS8EncodedKeySpec(dsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("SHA1withDSA");
signature.initSign(privateKey);
signature.update(src.getBytes());
byte[] result = signature.sign();
System.out.println("jdk dsa sign : " + Hex.encodeHexString(result));
//3.驗證簽名
X509EncodedKeySpec x509EncodedKeySpec =
new X509EncodedKeySpec(dsaPublicKey.getEncoded());
keyFactory = KeyFactory.getInstance("DSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
signature = Signature.getInstance("SHA1withDSA");
signature.initVerify(publicKey);
signature.update(src.getBytes());
boolean bool = signature.verify(result);
System.out.println("jdk dsa verify : " + bool);
} catch (Exception e) {
e.printStackTrace();
}
}
3、ECDSA
public static void jdkECDSA() {
try {
//1.初始化密鑰
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(256);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey ecPublicKey = (ECPublicKey)keyPair.getPublic();
ECPrivateKey ecPrivateKey = (ECPrivateKey)keyPair.getPrivate();
//2.執行簽名
PKCS8EncodedKeySpec pkcs8EncodedKeySpec =
new PKCS8EncodedKeySpec(ecPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("SHA1withECDSA");
signature.initSign(privateKey);
signature.update(src.getBytes());
byte[] result = signature.sign();
System.out.println("jdk ecdsa sign : " + Hex.encodeHexString(result));
//3.驗證簽名
X509EncodedKeySpec x509EncodedKeySpec =
new X509EncodedKeySpec(ecPublicKey.getEncoded());
keyFactory = KeyFactory.getInstance("EC");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
signature = Signature.getInstance("SHA1withECDSA");
signature.initVerify(publicKey);
signature.update(src.getBytes());
boolean bool = signature.verify(result);
System.out.println("jdk ecdsa verify : " + bool);
} catch (Exception e) {
e.printStackTrace();
}
}
代碼已經在這裏了,可以自己去實現一遍即可