Java RSA 加密解密算法 入门

一、入门闲话

    最近在学javase,想拿个小题目练习。拿到一个关于socket接口实现基于TCP协议的通信(准确的说是多进程程序中通信问题。)。通信过程中需要用RSA算法进行加解密。

要求进程应用软件A 键盘输入数据后和第一端口号-1存入一SendDate对象sd1中,然后将sd1进行序列化后,将序列化后的数据进行RSA加密(此处的RSA加密用最终接收方D的公钥加密)。将第一次RSA加密后的数据和第二端口号P2,存入第二SendDate对象sd2中,接着再将sd2进行序列化,再将sd2 序列化后的数据进行RSA加密(用中间中转进程应用软件C的公钥加密)和第三端口号P3,存入到第三SendDate对象sd3中。将sd3序列化,并用用中间中转进程应用软件B的公钥加密。然后起始的应用软件A将加密后的sd3,用端口号 P0发送到B中。

   B在接到后,用B的私钥进行解密,能自动获取到下个发送的端口号P3,将数据发给用中间中转进程应用软件C,C用自己的私钥进行解密后,可获取下个端口号P2。将解密的数据发给最终的D。 这样D用自己的私钥进行解密后,即可猎取A发送给来的原文了。

   题目的整体思路是清晰的,如图:

  但悲剧的时,对RSA加密与解密 一无所知。郁闷,只好去查相关的资料。 于是有了此遍博文。

二、正题,RSA简介及深入浅出的剖析原理:

      RSA是第一个比较完善的公开密钥算法,它既能用于加密,也能用于数字签名。RSA以它的三个发明者Ron Rivest, Adi Shamir, Leonard Adleman的名字首字母命名,这个算法经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明该算法有一定的可信性,目前它已经成为最流行的公开密钥算法。

     RSA的安全基于大数分解的难度。其公钥和私钥是一对大素数(100到200位十进制数或更大)的函数。从一个公钥和密文恢复出明文的难度,等价于分解两个大素数之积(这是公认的数学难题)。
     RSA的公钥、私钥的组成,以及加密、解密的公式可见于下表:

         

算法描述:
(1)选择一对不同的、足够大的素数p和q。
(2)计算n=pq。
(3)计算f(n)=(p-1)(q-1),同时对p和q严加保密,不让任何人知道。
(4)找一个与f(n)互质的数e,且1<e<f(n)。
(5)计算d,使得de mod f(n)≡1 mod f(n)。这个公式也可以表达为d≡e-1 mod f(n)
这里要解释一下,≡是数论中表示同余的符号。公式中,≡符号的左边必须和符号右边同余,也就是两边模运算结果相同。显而易见,不管f(n)取什么值,符号右边1 mod f(n)的结果都等于1;符号的左边d与e的乘积做模运算后的结果也必须等于1。这就需要计算出d的值,让这个同余等式能够成立。
(6)公钥KU=(e,n),私钥KR=(d,n)。
(7)加密时,先将明文变换成0至n-1的一个整数M。若明文较长,可先分割成适当的组,然后再进行交换。设密文为C,则加密过程为:C≡Me (mod n)
(8)解密过程为:M≡Cd (mod n)
实例描述:
为了更好的理解这里以简单的小例子来剖析下RSA的工作原理。为了便于计算。在以下实例中只选取小数值的素数p,q,以及e,假设用户A需要将明文“key”通过RSA加密后传递给用户B,过程如下:
(1)设计公私密钥(e,n)和(d,n)。
令p=3,q=11,得出n=p×q=3×11=33;f(n)=(p-1)(q-1)=2×10=20;取e=3,(3与20互质)则e×d≡1 mod f(n),即3×d≡1 mod 20。d怎样取值呢?可以用试算的办法来寻找。试算结果见下表:

通过试算我们找到,当d=7时,e×d≡1 mod f(n)同余等式成立。因此,可令d=7。从而我们可以设计出一对公私密钥,加密密钥(公钥)为:KU =(e,n)=(3,33),解密密钥(私钥)为:KR =(d,n)=(7,33)。
(2)英文数字化。
将明文信息数字化,并将每块两个数字分组。假定明文英文字母编码表为按字母顺序排列数值,即:

则得到分组后的key的明文信息为:11,05,25。
(3)明文加密 
用户加密密钥(3,33)将数字化明文分组信息加密成密文。由C≡Me (mod n)得:

因此,得到相应的密文信息为:11,31,16。,求C1、C2..的过程
(4)密文解密。
用户B收到密文,若将其解密,只需要计算M≡Cd (mod n),即:

用户B得到明文信息为:11,05,25。根据上面的编码表将其转换为英文,我们又得到了恢复后的原文“key”。 
您看,它的原理就可以这么简单地解释!当然,实际运用要比这复杂得多,由于RSA算法的公钥私钥的长度(模长度)要到1024位甚至2048位才能保证安全,因此,p、q、e的选取、公钥私钥的生成,加密解密模指数运算都有一定的计算程序,需要仰仗计算机高速完成。
最后简单谈谈RSA的安全性。
首先,我们来探讨为什么RSA密码难于破解?在RSA密码应用中,公钥KU是被公开的,即e和n的数值可以被第三方窃听者得到。破解RSA密码的问题就是从已知的e和n的数值(n等于pq),想法求出d的数值,这样就可以得到私钥来破解密文。从上文中的公式:d ≡e-1 (mod((p-1)(q-1)))或de≡1 (mod((p-1)(q-1))) 我们可以看出。密码破解的实质问题是:从pq的数值,去求出(p-1)和(q-1)。换句话说,只要求出p和q的值,我们就能求出d的值而得到私钥。
当p和q是一个大素数的时候,从它们的积pq去分解因子p和q,这是一个公认的数学难题。比如当pq大到1024位时,迄今为止还没有人能够利用任何计算工具去完成分解因子的任务。因此,RSA从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。
然而,虽然RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价。即RSA的重大缺陷是无法从理论上把握它的保密性能如何。
此外,RSA的缺点还有:A)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。B)分组长度太大,为保证安全性,n 至少也要 600 bits 以上,使运算代价很高,尤其是速度较慢,较对称密码算法慢几个数量级;且随着大数分解技术的发展,这个长度还在增加,不利于数据格式的标准化。因此,使用RSA只能加密少量数据,大量的数据加密还要靠对称密码算法。

三、Java中 RSA加解密的示例

1. 关键类的简单介绍:

   javax.crypto.Cipher   此类为加密和解密提供密码功能,还可以根据提供的byte[]数据进行加密或解密(当然前提是要提供一个密钥,公钥或私钥)

   java.security.PrivateKey

   java.security.PublicKey 公私钥对象,不用说。它们可以由KeyPair来获取

   sun.misc.BASE64Encoder

   sun.misc.BASE64Decoder  是Base64的一对加密与解码算法,这个比较简单。其实它不是主要用来加密与解密的,它主要是用来对数据进行再编码以防止某些部分数据与特殊字符有冲突,而将数据内容提升。比如它的加密过程是将数据的每6位取出后,在前两位补两个0,组成一个新的字节。最终组成新的数据byte[]。而解密过程是将数据取出后,删除去掉每个字节中的前两位同时向下个字节要两位,组成新的字节就OK了。  举例如  aaaabbbb  ccccdddd eeeeffff  这样的三个字节,被BASE64Encoder后,就变成了00aaaabb  00bbcccc   00ddddee  00eeffff 四个字节了。然后经BASE64Decoder解码,去掉前面两个0后就还原了。这个比较简单不作本文重点就不再细谈了。

2.上面说了一堆废话,现在给个代码示例:

2.1 Coder,BASE64进行简单的数据处理。

01 package com.rsa;
02  
03 import java.security.MessageDigest;
04  
05 import sun.misc.BASE64Decoder;
06 import sun.misc.BASE64Encoder;
07  
08 /**
09  * 小加解密类,主要是BASE64的加解密
10  * @author chen
11  *
12  */
13 public class Coder {  
14     public static final String KEY_SHA = "SHA";  
15     public static final String KEY_MD5 = "MD5";  
16    
17     /** 
18      * BASE64解密 
19      *  
20      * @param key 
21      * @return 
22      * @throws Exception 
23      */ 
24     public static byte[] decryptBASE64(String key) throws Exception {  
25         return (new BASE64Decoder()).decodeBuffer(key);  
26     }  
27    
28     /** 
29      * BASE64加密 
30      *  
31      * @param key 
32      * @return 
33      * @throws Exception 
34      */ 
35     public static String encryptBASE64(byte[] key) throws Exception {  
36         return (new BASE64Encoder()).encodeBuffer(key);  
37     }  
38    
39     /** 
40      * MD5加密 
41      *  
42      * @param data 
43      * @return 
44      * @throws Exception 
45      */ 
46     public static byte[] encryptMD5(byte[] data) throws Exception {  
47    
48         MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
49         md5.update(data);  
50    
51         return md5.digest();  
52    
53     }  
54    
55     /** 
56      * SHA加密 
57      *  
58      * @param data 
59      * @return 
60      * @throws Exception 
61      */ 
62     public static byte[] encryptSHA(byte[] data) throws Exception {  
63    
64         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
65         sha.update(data);  
66    
67         return sha.digest();  
68    
69     }  
70 }


2.2  RSACoder类,RSA算法的类,主要进行公钥、私钥加解密。  公钥与私钥的获取及私钥数据签名,公钥签证数据签名的方法。

001 package com.rsa;
002 import java.security.Key;
003 import java.security.KeyFactory;
004 import java.security.KeyPair;
005 import java.security.KeyPairGenerator;
006 import java.security.PrivateKey;
007 import java.security.PublicKey;
008 import java.security.Signature;
009 import java.security.interfaces.RSAPrivateKey;
010 import java.security.interfaces.RSAPublicKey;
011 import java.security.spec.PKCS8EncodedKeySpec;
012 import java.security.spec.X509EncodedKeySpec;
013 import java.util.HashMap;
014 import java.util.Map;
015  
016 import javax.crypto.Cipher;
017  
018 /** *//**
019 * RSA安全编码组件
020 *  
021 * @version 1.0
022 * @since 1.0
023 */
024 public class RSACoder  extends Coder{  
025     public static final String KEY_ALGORITHM = "RSA";  
026     public static final String SIGNATURE_ALGORITHM = "MD5withRSA";  
027  
028     private static final String PUBLIC_KEY = "RSAPublicKey";  
029     private static final String PRIVATE_KEY = "RSAPrivateKey";  
030  
031     /** *//**
032      * 用私钥对信息生成数字签名
033      *  
034      * @param data
035      *            加密数据
036      * @param privateKey
037      *            私钥
038      *  
039      * @return
040      * @throws Exception
041      */
042     public static String sign(byte[] data, String privateKey) throws Exception {  
043         // 解密由base64编码的私钥  
044         byte[] keyBytes = decryptBASE64(privateKey);  
045  
046         // 构造PKCS8EncodedKeySpec对象  
047         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
048  
049         // KEY_ALGORITHM 指定的加密算法  
050         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
051  
052         // 取私钥匙对象  
053         PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
054  
055         // 用私钥对信息生成数字签名  
056         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
057         signature.initSign(priKey);  
058         signature.update(data);  
059  
060         return encryptBASE64(signature.sign());  
061     }  
062  
063     /** *//**
064      * 校验数字签名
065      *  
066      * @param data
067      *            加密数据
068      * @param publicKey
069      *            公钥
070      * @param sign
071      *            数字签名
072      *  
073      * @return 校验成功返回true 失败返回false
074      * @throws Exception
075      *  
076      */
077     public static boolean verify(byte[] data, String publicKey, String sign)  
078             throws Exception {  
079  
080         // 解密由base64编码的公钥  
081         byte[] keyBytes = decryptBASE64(publicKey);  
082  
083         // 构造X509EncodedKeySpec对象  
084         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
085  
086         // KEY_ALGORITHM 指定的加密算法  
087         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
088  
089         // 取公钥匙对象  
090         PublicKey pubKey = keyFactory.generatePublic(keySpec);  
091  
092         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
093         signature.initVerify(pubKey);  
094         signature.update(data);  
095  
096         // 验证签名是否正常  
097         return signature.verify(decryptBASE64(sign));  
098     }  
099  
100     /** *//**
101      * 解密<br>
102 * 用私钥解密 http://www.5a520.cn http://www.feng123.com
103      *  
104      * @param data
105      * @param key
106      * @return
107      * @throws Exception
108      */
109     public static byte[] decryptByPrivateKey(byte[] data, String key)  
110             throws Exception {  
111         // 对密钥解密  
112         byte[] keyBytes = decryptBASE64(key);  
113  
114         // 取得私钥  
115         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
116         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
117         Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
118  
119         // 对数据解密  
120         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
121         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
122  
123         return cipher.doFinal(data);  
124     }  
125  
126     /** *//**
127      * 解密<br>
128      * 用私钥解密
129      *  
130      * @param data
131      * @param key
132      * @return
133      * @throws Exception
134      */
135     public static byte[] decryptByPublicKey(byte[] data, String key)  
136             throws Exception {  
137         // 对密钥解密  
138         byte[] keyBytes = decryptBASE64(key);  
139  
140         // 取得公钥  
141         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
142         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
143         Key publicKey = keyFactory.generatePublic(x509KeySpec);  
144  
145         // 对数据解密  
146         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
147         cipher.init(Cipher.DECRYPT_MODE, publicKey);  
148  
149         return cipher.doFinal(data);  
150     }  
151  
152     /** *//**
153      * 加密<br>
154      * 用公钥加密
155      *  
156      * @param data
157      * @param key
158      * @return
159      * @throws Exception
160      */
161     public static byte[] encryptByPublicKey(byte[] data, String key)  
162             throws Exception {  
163         // 对公钥解密  
164         byte[] keyBytes = decryptBASE64(key);  
165  
166         // 取得公钥  
167         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
168         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
169         Key publicKey = keyFactory.generatePublic(x509KeySpec);  
170  
171         // 对数据加密  
172         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
173         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
174  
175         return cipher.doFinal(data);  
176     }  
177  
178     /** *//**
179      * 加密<br>
180      * 用私钥加密
181      *  
182      * @param data
183      * @param key
184      * @return
185      * @throws Exception
186      */
187     public static byte[] encryptByPrivateKey(byte[] data, String key)  
188             throws Exception {  
189         // 对密钥解密  
190         byte[] keyBytes = decryptBASE64(key);  
191  
192         // 取得私钥  
193         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
194         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
195         Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
196  
197         // 对数据加密  
198         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
199         cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
200  
201         return cipher.doFinal(data);  
202     }  
203  
204     /** *//**
205      * 取得私钥
206      *  
207      * @param keyMap
208      * @return
209      * @throws Exception
210      */
211     public static String getPrivateKey(Map<String, Object> keyMap)  
212             throws Exception {  
213         Key key = (Key) keyMap.get(PRIVATE_KEY);  
214  
215         return encryptBASE64(key.getEncoded());  
216     }  
217  
218     /** *//**
219      * 取得公钥
220      *  
221      * @param keyMap
222      * @return
223      * @throws Exception
224      */
225     public static String getPublicKey(Map<String, Object> keyMap)  
226             throws Exception {  
227         Key key = (Key) keyMap.get(PUBLIC_KEY);  
228  
229         return encryptBASE64(key.getEncoded());  
230     }  
231  
232     /** *//**
233      * 初始化密钥
234      *  
235      * @return
236      * @throws Exception
237      */
238     public static Map<String, Object> initKey() throws Exception {  
239         KeyPairGenerator keyPairGen = KeyPairGenerator  
240                 .getInstance(KEY_ALGORITHM);  
241         keyPairGen.initialize(1024);  
242  
243         KeyPair keyPair = keyPairGen.generateKeyPair();  
244  
245         // 公钥  
246         RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  
247  
248         // 私钥  
249         RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();  
250  
251         Map<String, Object> keyMap = new HashMap<String, Object>(2);  
252  
253         keyMap.put(PUBLIC_KEY, publicKey);  
254         keyMap.put(PRIVATE_KEY, privateKey);  
255         return keyMap;  
256     }  
257 }


 

2.3 Junit进行测试。


 

01 package com.rsa;
02 import static org.junit.Assert.*;  
03  
04 import org.junit.Before;  
05 import org.junit.Test;  
06  
07 import java.util.Map;  
08  
09 /** *//**
10 *  
11 *   使用Junit4 来进行单元测试。
12 * @version 1.0
13 * @since 1.0
14 */
15 public class RSACoderTest {  
16     private String publicKey;  
17     private String privateKey;  
18  
19     @Before
20     public void setUp() throws Exception {  
21         Map<String, Object> keyMap = RSACoder.initKey();  
22  
23         publicKey = RSACoder.getPublicKey(keyMap);  
24         privateKey = RSACoder.getPrivateKey(keyMap);  
25         System.err.println("公钥: \n\r" + publicKey);  
26         System.err.println("私钥: \n\r" + privateKey);  
27     }  
28  
29     @Test
30     public void testPub2Pri() throws Exception {  
31         System.err.println("公钥加密——私钥解密");  
32         String inputStr = "abc";  
33         byte[] data = inputStr.getBytes();  
34  
35         byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);  
36  
37         byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,  
38                 privateKey);  
39  
40         String outputStr = new String(decodedData);  
41         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);  
42         assertEquals(inputStr, outputStr);  
43  
44     }  
45  
46     @Test
47     public void testPri2Pub() throws Exception {  
48         System.err.println("私钥加密——公钥解密");  
49         String inputStr = "sign";  
50         byte[] data = inputStr.getBytes();  
51  
52         byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);  
53  
54         byte[] decodedData = RSACoder.decryptByPublicKey(encodedData, publicKey);  
55  
56         String outputStr = new String(decodedData);  
57         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);  
58         assertEquals(inputStr, outputStr);    //使用Junit断言,加密前的原文与解密后的明文是否一致。
59  
60         System.err.println("私钥签名——公钥验证签名");  
61         // 产生签名   这里的encodedData可以与下面的encodedData同时换成new int[]{2,45}
62         String sign = RSACoder.sign(encodedData, privateKey); //数字签名只要公钥人拿到签名的sign对比
63         //,自己公钥通过同样的byte[]运算得到签名是否一致。是到致代表这个公钥就是对的,就是为现在发私钥人服务的。
64         System.err.println("签名:\r" + sign);  
65  
66         // 验证签名  
67         boolean status = RSACoder.verify(encodedData, publicKey, sign);  
68         System.err.println("状态:\r" + status);  
69         assertTrue(status);  
70  
71     }  
72  
73 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值