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

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

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

14.一、數字簽名算法git

特色:算法

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

過程:apache

1)消息發送者產生一個密鑰對(私鑰+公鑰),而後將公鑰發送給消息接收者數組

2)消息發送者使用消息摘要算法對原文進行加密(加密後的密文稱做摘要)安全

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

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

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

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

7)比較content1是否是與content2相等,若相等,則說明消息沒有被篡改(消息完整性),也說明消息倒是來源於上述的消息發送方(由於其餘人是沒法僞造簽名的,這就完成了「抗否定性」和「認證消息來源」)

類比:

手寫簽名:

  • 張三在合同上籤了本身的名字,這樣張三在後來想否定本身的這個合同內容時,就不行了(抗否定性)
  • 在張三簽過名以後,若合同內容又發生了變化,這個時候簽名就能夠看作無效了
  • 固然,咱們經過合同上張三的簽名,咱們就能夠知道簽名的來源是張三(認證數據來源)

常見的數字簽名算法:

  • RSA(數字簽名算法的經典,也是最經常使用的數字簽名算法)
  • DSA(是後續數字簽名算法的基礎)
  • ECDSA(ECC+DSA的結合體,相較於其餘數字簽名算法,速度快,強度高,簽名短,可是使用尚未RSA普遍)

14.二、RSA

常見算法:

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

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

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

  • JDK
  • BC

14.2.一、基於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(摘要值的二進制本身數組長度),不知道究竟是怎麼與密鑰長度相同的?

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

相關文章
相關標籤/搜索