RSA加密解密與加簽驗簽


  RSA公鑰加密算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。1987年7月首次在美國公布,當時他們三人都在麻省理工學院工作實習。RSA就是他們三人姓氏開頭字母拼在一起組成的。

  RSA是目前最有影響力和最常用的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰數據加密標准。

  RSA公開密鑰密碼體制。所謂的公開密鑰密碼體制就是使用不同的加密密鑰與解密密鑰,是一種“由已知加密密鑰推導出解密密鑰在計算上是不可行的”密碼體制。

  在公開密鑰密碼體制中,加密密鑰(即公開密鑰)PK是公開信息,而解密密鑰(即秘密密鑰)SK是需要保密的。加密算法E和解密算法D也都是公開的。雖然解密密鑰SK是由公開密鑰PK決定的,但卻不能根據PK計算出SK。

  基於這種理論,1978年出現了著名的RSA算法,它通常是先生成一對RSA 密鑰,其中之一是保密密鑰,由用戶保存;另一個為公開密鑰,可對外公開,甚至可以在網絡服務器中注冊。為提高保密強度,RSA密鑰至少為500位長,一般推薦使用1024位。這就使加密的計算量很大。

  RSA算法是第一個能同時用於加密和數字簽名的算法,也易於理解和操作。RSA是被研究得最廣泛的公鑰算法,從提出到現今的三十多年里,經歷了各種攻擊的考驗,逐漸為人們接受,截止2017年被普遍認為是最優秀的公鑰方案之一。

  SET(Secure Electronic Transaction)協議中要求CA采用2048bits長的密鑰,其他實體使用1024比特的密鑰。RSA密鑰長度隨着保密級別提高,增加很快。

  RSA算法是一種非對稱密碼算法,所謂非對稱,就是指該算法需要一對密鑰,使用其中一個加密,則需要用另一個才能解密。

  我們接下來看下Java中如何實現RSA加密解密與加簽驗簽。我們先來看RSA加密解密。  

 1 import javax.crypto.BadPaddingException;
 2 import javax.crypto.Cipher;
 3 import javax.crypto.IllegalBlockSizeException;
 4 import javax.crypto.NoSuchPaddingException;
 5 import java.security.*;
 6 import java.util.Base64;
 7 /**
 8  * RSA加密解密操作步驟
 9  */
10 public class Test1 {
11     public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
12         //先給出一個待加密的字符串
13         String data="青青子衿,悠悠我心。但為君故,沉吟至今。";
14         //1.構建公私鑰匙對
15         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
16         KeyPair keyPair = keyPairGenerator.generateKeyPair();
17         //2.獲取鑰匙對中的公鑰
18         PublicKey publicKey = keyPair.getPublic();
19         //3.獲取鑰匙對中的私鑰
20         PrivateKey privateKey = keyPair.getPrivate();
21         //4.對待加密的數據進行加密
22         Cipher cipher = Cipher.getInstance("RSA");
23         cipher.init(Cipher.ENCRYPT_MODE,publicKey);
24         byte[] bytesEncrypt = cipher.doFinal(data.getBytes());//產生的是亂碼,需要用Base64進行轉碼
25         //5.Base64編碼
26         byte[] encodeBase64 = Base64.getEncoder().encode(bytesEncrypt);
27         System.out.println("加密后的數據:"+new String(encodeBase64));
28         //6.在解密時,先對用Base64編碼的信息進行解碼
29         byte[] bytesDecode = Base64.getDecoder().decode(encodeBase64);
30         //7.解密
31         Cipher cipher2=Cipher.getInstance("RSA");
32         cipher2.init(Cipher.DECRYPT_MODE,privateKey);
33         byte[] bytesDecrypt = cipher2.doFinal(bytesDecode);
34         System.out.println("解密后的數據:"+new String(bytesDecrypt));
35     }
36 }

  公鑰和私鑰本身存儲的信息是亂碼,在實際使用中,我們還可以通過Base64將這些亂碼編碼為可識別的ASCII碼,然后將公鑰和私鑰信息持久化存儲到文件中,在以后需要使用時,可以從文件中讀取公鑰和私鑰信息。為此,我們可以寫一個RSA的工具類,從一個儲存公鑰和私鑰信息的文件里讀取公鑰和私鑰信息,然后定義獲取公鑰和私鑰的方法,以及加密和解密數據的方法。首先,我們提供一對公私鑰信息,假定公鑰信息儲存在一個名稱為rsa_public_key.pem的文件里,信息如下:

-----BEGIN RSA PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDY90KtriCa4KjNe3mgrGGbDB95
8A2byBKf+wOmPmOopP3gGeg7+DFAPNYCC+tL8h2bpUI3IPKOm2Hon8kM/p628i1J
Z7JjopYVZW6JKqA2ImyneeUEK748FXwXTRAAMCTqQG/7a178BGawTdHi6hk+M6UF
lT0EhL6JA8ULKFoiHwIDAQAB
-----BEGIN RSA PUBLIC KEY-----

  假定私鑰信息儲存在一個名稱為rsa_private_key.pem的文件里,信息如下::

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDY90KtriCa4KjNe3mgrGGbDB958A2byBKf+wOmPmOopP3gGeg7
+DFAPNYCC+tL8h2bpUI3IPKOm2Hon8kM/p628i1JZ7JjopYVZW6JKqA2ImyneeUE
K748FXwXTRAAMCTqQG/7a178BGawTdHi6hk+M6UFlT0EhL6JA8ULKFoiHwIDAQAB
AoGBAIJFhF2wLZeQyQoH13Gnzzs/Pi8C+cjNipFQMFLDJyd9WYoTRCOt1DST0pOM
AI2rJCfuRCHBwKHrnhAE0LzirPxkmvyHTIBXIoz3fHiSkIKkUVG04BcgTYpNKWPB
ISlzdhSaw7CnmJjTthTrD5LLPtpqUl350lUYFEHVNR6Ys9JRAkEA9JUEVxzSvQkV
V6hxhbvlxl0mATbPfiNKDBTPdr48dyYdgluAoGfAPf9rmgoCpdEd2hZBIfdy7xdL
LvP7ztb/rQJBAOMYNC/lZLz9A9cDJ5bibrJnmyRG0SAGAzu4ffYdBoGb0kRRKzTe
5jxfRnbiUPQU4GQXhADfikGn2ogRqbtDsnsCQQCJdp+D3n1LJanLJK75PQv9myjb
EdU4zdi2RZP85xrQ1KlNNORsQyO3NLFjWDD4xTmD83IUByGf43WsJBDoxcnZAkA3
i84IARX42/I6fz0JvOzSmmDqKKAyMwZLbz7wGf1jalet+iSVVAgAsFUt8wFWEl0o
XlAdXpAUqxfavGdFtLNNAkABS576xgLcLmyw51f9hoM9RiamLn+WNzoA5TLOZjGI
dZZnX/A8SoFYGoJoN1O0hp5DxDdl+gjW/mH51+gliEIB
-----END RSA PRIVATE KEY-----

  接下來,我們寫一個RSAUtil工具類,代碼如下:  

  1 import javax.crypto.BadPaddingException;
  2 import javax.crypto.Cipher;
  3 import javax.crypto.IllegalBlockSizeException;
  4 import javax.crypto.NoSuchPaddingException;
  5 import java.io.BufferedReader;
  6 import java.io.FileReader;
  7 import java.io.IOException;
  8 import java.security.*;
  9 import java.security.spec.InvalidKeySpecException;
 10 import java.security.spec.PKCS8EncodedKeySpec;
 11 import java.security.spec.X509EncodedKeySpec;
 12 import java.util.Base64;
 13 /**
 14  * 工具類
 15  */
 16 public class RSAUtil {
 17     /*
 18         讀取秘鑰數據
 19      */
 20     public static byte[] readKeyDatas(String keyFilePath){
 21         BufferedReader bufferedReader=null;
 22         try{
 23             bufferedReader = new BufferedReader(new FileReader(keyFilePath));
 24             String str=null;
 25             StringBuilder stringBuilder=new StringBuilder();
 26             while ((str=bufferedReader.readLine())!=null){
 27                 if(str.contains("---")){
 28                     continue;
 29                 }
 30                 stringBuilder.append(str);
 31             }
 32             return stringBuilder.toString().getBytes();
 33         }catch (IOException e) {
 34             e.printStackTrace();
 35         }finally {
 36             try {
 37                 bufferedReader.close();
 38             } catch (IOException e) {
 39                 e.printStackTrace();
 40             }
 41         }
 42         return null;
 43     }
 44     /*
 45         生成公鑰
 46      */
 47     public static PublicKey getPublicKey(String publicKeyPath){
 48         //1.讀取公鑰文件,獲取公鑰數據
 49         byte[] bytesPublicBase64 = readKeyDatas(publicKeyPath);
 50         //2.對讀取回來的數據進行Base64解碼
 51         byte[] bytesPublic = Base64.getDecoder().decode(bytesPublicBase64);
 52         //3.把解碼后的數據,重新封裝成一個PublicKey對象
 53         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytesPublic);
 54         KeyFactory keyFactory=null;
 55         try {
 56             keyFactory = KeyFactory.getInstance("RSA");
 57             PublicKey publicKey = keyFactory.generatePublic(keySpec);
 58             return publicKey;
 59         } catch (NoSuchAlgorithmException e) {
 60             e.printStackTrace();
 61         } catch (InvalidKeySpecException e) {
 62             e.printStackTrace();
 63         }
 64         return null;
 65     }
 66     /*
 67         生成私鑰
 68      */
 69     public static PrivateKey getPrivateKey(String privateKeyPath){
 70         //1.讀取私鑰文件,獲取私鑰數據
 71         byte[] bytesPrivateBase64 = readKeyDatas(privateKeyPath);
 72         //2.對讀取回來的數據進行Base64解碼
 73         byte[] bytesPrivate = Base64.getDecoder().decode(bytesPrivateBase64);
 74         //3.把解碼后的數據,重新封裝成一個PrivateKey對象
 75         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytesPrivate);
 76         KeyFactory keyFactory=null;
 77         try {
 78             keyFactory = KeyFactory.getInstance("RSA");
 79             PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
 80             return privateKey;
 81         } catch (NoSuchAlgorithmException e) {
 82             e.printStackTrace();
 83         } catch (InvalidKeySpecException e) {
 84             e.printStackTrace();
 85         }
 86         return null;
 87     }
 88     /*
 89         加密數據
 90      */
 91     public static String encodeData(PublicKey publicKey,String originData){
 92         try {
 93             Cipher cipher = Cipher.getInstance("RSA");
 94             cipher.init(Cipher.ENCRYPT_MODE,publicKey);
 95             byte[] bytesEncrypt = cipher.doFinal(originData.getBytes());
 96             //Base64編碼
 97             byte[] bytesEncryptBase64 = Base64.getEncoder().encode(bytesEncrypt);
 98             return new String(bytesEncryptBase64);            
 99         } catch (NoSuchAlgorithmException e) {
100             e.printStackTrace();
101         } catch (NoSuchPaddingException e) {
102             e.printStackTrace();
103         } catch (InvalidKeyException e) {
104             e.printStackTrace();
105         } catch (BadPaddingException e) {
106             e.printStackTrace();
107         } catch (IllegalBlockSizeException e) {
108             e.printStackTrace();
109         }
110         return null;
111     }
112     /*
113         解密數據
114      */
115     public static String decodeData(PrivateKey privateKey,String encodeData){
116         try {
117             //Base64解碼
118             byte[] bytesEncrypt = Base64.getDecoder().decode(encodeData);
119             //加密
120             Cipher cipher = Cipher.getInstance("RSA");
121             cipher.init(Cipher.DECRYPT_MODE,privateKey);
122             byte[] bytesDecrypt = cipher.doFinal(bytesEncrypt);
123             return new String(bytesDecrypt);
124         } catch (NoSuchAlgorithmException e) {
125             e.printStackTrace();
126         } catch (NoSuchPaddingException e) {
127             e.printStackTrace();
128         } catch (InvalidKeyException e) {
129             e.printStackTrace();
130         } catch (BadPaddingException e) {
131             e.printStackTrace();
132         } catch (IllegalBlockSizeException e) {
133             e.printStackTrace();
134         }
135         return null;
136     }   
137 }

  這樣的話,以后需要使用公鑰和私鑰,以及加密解密時,調用上述工具類中的相應方法即可。

  最后,我們再來看用RSA如何對數據進行加簽驗簽,具體代碼如下:

 1 import java.io.UnsupportedEncodingException;
 2 import java.security.*;
 3 /**
 4  * 加簽  驗簽    簽名驗證:驗證數據的合法來源   即驗證數據來源的合法性
 5  * 加簽:私鑰
 6  * 驗簽:公鑰
 7  */
 8 public class Test2 {
 9     private static String privateKeyPath="儲存私鑰信息的文件路徑";
10     private static String publicKeyPath="儲存公鑰信息的文件路徑";
11     public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
12         String data="驗證該數據是否為合法的服務器發送";
13         /**
14          * 加簽過程
15          */
16         PrivateKey privateKey = RSAUtil.getPrivateKey(privateKeyPath);
17         Signature signature = Signature.getInstance("Sha1WithRSA");
18         signature.initSign(privateKey);
19         signature.update(data.getBytes("UTF-8"));
20         byte[] signed = signature.sign();
21         /**
22          * 驗簽過程
23          */
24         PublicKey publicKey = RSAUtil.getPublicKey(publicKeyPath);
25         Signature signature2 = Signature.getInstance("Sha1WithRSA");
26         signature2.initVerify(publicKey);
27         signature2.update(data.getBytes("UTF-8"));
28         boolean verify = signature2.verify(signed);
29         System.out.println("驗簽結果:"+verify);
30     }    
31 }


免責聲明!

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



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