第十四章 數字簽名算法--RSA


注意:本節內容主要參考自

  • 《Java加密與解密的藝術(第2版)》第9章“帶密鑰的消息摘要算法--數字簽名算法”
  • 《大型分布式網站架構(設計與實踐)》第3章“互聯網安全架構”

14.1、數字簽名算法

特點:

  • 非對稱加密算法+消息摘要算法的結合體
  • 抗否認性、認證數據來源、防止數據被篡改(具體意思與做法查看下邊的過程與類比部分)
  • 私鑰加密(簽名)、公鑰解密(驗證)

過程:

1)消息發送者產生一個密鑰對(私鑰+公鑰),然后將公鑰發送給消息接收者

2)消息發送者使用消息摘要算法對原文進行加密(加密后的密文稱作摘要)

3)消息發送者將上述的摘要使用私鑰加密得到密文--這個過程就被稱作簽名處理,得到的密文就被稱作簽名(注意,這個簽名是名詞)

4)消息發送者將原文與密文發給消息接收者

5)消息接收者使用公鑰對密文(即簽名)進行解密,得到摘要值content1

6)消息接收者使用與消息發送者相同的消息摘要算法對原文進行加密,得到摘要值content2

7)比較content1是不是與content2相等,若相等,則說明消息沒有被篡改(消息完整性),也說明消息卻是來源於上述的消息發送方(因為其他人是無法偽造簽名的,這就完成了“抗否認性”和“認證消息來源”)

類比:

手寫簽名:

  • 張三在合同上簽了自己的名字,這樣張三在后來想否認自己的這個合同內容時,就不行了(抗否認性)
  • 在張三簽過名之后,若合同內容又發生了變化,這個時候簽名就可以看做無效了
  • 當然,我們通過合同上張三的簽名,我們就可以知道簽名的來源是張三(認證數據來源)

常見的數字簽名算法:

  • RSA(數字簽名算法的經典,也是最常用的數字簽名算法)
  • DSA(是后續數字簽名算法的基礎)
  • ECDSA(ECC+DSA的結合體,相較於其他數字簽名算法,速度快,強度高,簽名短,但是使用還沒有RSA廣泛)

14.2、RSA

常見算法:

  • MD5WithRSA(JDK6)
  • SHA1WithRSA(JDK6)
  • SHA256WithRSA(>=JDK1.6.45,Bouncy Castle-->簡稱BC)

注意:在《Java加密與解密(第二版)》一書中,說JDK6不支持SHA256WithRSA,但是經過我自己測試1.6.45是支持的。

實現方式:(參考上邊三行)

  • JDK
  • BC

14.2.1、基於JDK6實現的MD5WithRSA或SHA1WithRSA或SHA256WithRSA算法

  1 package com.uti.rsa.digital;
  2 
  3 import java.io.UnsupportedEncodingException;
  4 import java.security.InvalidKeyException;
  5 import java.security.KeyFactory;
  6 import java.security.KeyPair;
  7 import java.security.KeyPairGenerator;
  8 import java.security.NoSuchAlgorithmException;
  9 import java.security.PrivateKey;
 10 import java.security.PublicKey;
 11 import java.security.Signature;
 12 import java.security.SignatureException;
 13 import java.security.spec.InvalidKeySpecException;
 14 import java.security.spec.PKCS8EncodedKeySpec;
 15 import java.security.spec.X509EncodedKeySpec;
 16 
 17 import org.apache.commons.codec.binary.Base64;
 18 
 19 /**
 20  * 基於BC的RSA數字簽名算法
 21  */
 22 public class RSACoderBC {
 23     private static final String ENCODING = "UTF-8";
 24     private static final String KEY_ALGORITHM = "RSA";//非對稱加密密鑰算法
 25     private static final String SIGNATURE_ALGORITHM = "MD5withRSA";//指定數字簽名算法(可以換成SHA1withRSA或SHA256withRSA)
 26     private static final int KEY_SIZE = 512;//非對稱密鑰長度(512~1024之間的64的整數倍)
 27     
 28     /**
 29      * 生成發送方密鑰對
 30      */
 31     public static KeyPair initKey() throws NoSuchAlgorithmException{
 32         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);//密鑰對生成器
 33         keyPairGenerator.initialize(KEY_SIZE);//指定密鑰長度
 34         KeyPair keyPair = keyPairGenerator.generateKeyPair();//生成密鑰對
 35         return keyPair;
 36     }
 37     
 38     /**
 39      * 還原公鑰
 40      * @param pubKey 二進制公鑰
 41      */
 42     public static PublicKey toPublicKey(byte[] pubKey) throws NoSuchAlgorithmException, 
 43                                                               InvalidKeySpecException{
 44         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密鑰工廠
 45         return keyFactory.generatePublic(new X509EncodedKeySpec(pubKey));//還原公鑰
 46     }
 47     
 48     /**
 49      * 還原私鑰
 50      * @param priKey 二進制私鑰
 51      */
 52     public static PrivateKey toPrivateKey(byte[] priKey) throws NoSuchAlgorithmException, 
 53                                                                 InvalidKeySpecException{
 54         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密鑰工廠
 55         return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(priKey));//還原私鑰
 56     }
 57     
 58     /**
 59      * 私鑰加密(簽名)
 60      * @param data     待加密數據
 61      * @param keyByte  私鑰
 62      */
 63     public static byte[] encryptPriKey(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
 64                                                                            InvalidKeySpecException, 
 65                                                                            InvalidKeyException, 
 66                                                                            SignatureException, 
 67                                                                            UnsupportedEncodingException {
 68         PrivateKey priKey = toPrivateKey(keyByte);//還原私鑰
 69 
 70         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
 71         signature.initSign(priKey);
 72         signature.update(data.getBytes(ENCODING));
 73         
 74         return signature.sign();
 75     }
 76     
 77     /**
 78      * 公鑰解密(驗證)
 79      * @param data        原文(待加密數據,也成為“待校驗數據”)
 80      * @param keyByte    公鑰
 81      * @param sign        密文(也稱作“簽名”)
 82      */
 83     public static boolean decryptPubKey(String data, byte[] keyByte, byte[] sign) throws NoSuchAlgorithmException, 
 84                                                                                          InvalidKeySpecException, 
 85                                                                                          InvalidKeyException, 
 86                                                                                          SignatureException, 
 87                                                                                          UnsupportedEncodingException {
 88         PublicKey pubKey = toPublicKey(keyByte);//還原公鑰
 89         
 90         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
 91         signature.initVerify(pubKey);
 92         signature.update(data.getBytes(ENCODING));
 93         
 94         return signature.verify(sign);
 95     }
 96     
 97     /**
 98      * 獲取公鑰
 99      */
100     public static byte[] getPublicKey(KeyPair keyPair){
101         return keyPair.getPublic().getEncoded();
102     }
103     
104     /**
105      * 獲取私鑰
106      */
107     public static byte[] getPrivateKey(KeyPair keyPair){
108         return keyPair.getPrivate().getEncoded();
109     }
110     
111     /**
112      * 測試
113      */
114     public static void main(String[] args) throws NoSuchAlgorithmException, 
115                                                   InvalidKeyException, 
116                                                   InvalidKeySpecException, 
117                                                   SignatureException, 
118                                                   UnsupportedEncodingException {
119         byte[] pubKey1;//甲方公鑰
120         byte[] priKey1;//甲方私鑰
121         
122         /*********************測試是否可以正確生成以上2個key*********************/
123         KeyPair keyPair1 = RSACoderBC.initKey();//生成甲方密鑰對
124         pubKey1 = RSACoderBC.getPublicKey(keyPair1);
125         priKey1 = RSACoderBC.getPrivateKey(keyPair1);
126         
127         System.out.println("甲方公鑰pubKey1-->"+Base64.encodeBase64String(pubKey1)+"@@pubKey1.length-->"+pubKey1.length);
128         System.out.println("甲方私鑰priKey1-->"+Base64.encodeBase64String(priKey1)+"@@priKey1.length-->"+priKey1.length);
129         
130         /*********************測試甲方使用私鑰加密數據向乙方發送,乙方使用公鑰解密數據*********************/
131         System.out.println("甲方-->乙方");
132         String data = "找一個好姑娘啊!你好嗎,孩子";
133         byte[] encodeStr = RSACoderBC.encryptPriKey(data, priKey1);//加密(簽名)
134         System.out.println("甲方加密后的數據-->"+Base64.encodeBase64String(encodeStr)+"@@encodeStr.length-->"+encodeStr.length);
135         boolean decodeStr = RSACoderBC.decryptPubKey(data, pubKey1, encodeStr);
136         System.out.println("乙方檢驗結果-->"+decodeStr);
137     }
138 }
View Code

注意點:

  • 代碼與RSA非對稱加密算法(查看第十二章)基本一樣,只是將其中的私鑰加密與公鑰解密部分的加解密算法改為簽名和驗證算法,當然沒有“公鑰加密,私鑰解密”

疑問:在《Java加密與解密的藝術(第二版)》一書中,作者說:“RSA數字簽名算法的簽名值與密鑰長度相同”,但是在我的測試中,結果不一致:

1)在我配置非對稱密鑰為512的時候,測出來的公鑰長度是96位,私鑰長度是345位,與512差很遠,那這里的512到底是怎么計算的?

2)消息摘要的結果長度是64(摘要值的二進制自己數組長度),不知道到底是怎么與密鑰長度相同的?

以上兩個問題,有知道的朋友請指點一下,謝謝!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM