java加解密之RSA使用

主页 > 新闻资讯 > 软件开发资讯 > Java编程Java软件开发 >

java加解密之RSA使用

最近为了分析一段请求流,不得不去研究一下RSA加密。

首先,强调一点:密钥的“钥”读“yue”,不是“yao”,额。。。

网上关于RSA的原理一抓一大把的,这里只是简单说说我的理解:

1. 两个足够大的互质数p, q;

2. 用于模运算的模 n=p*q;

3. 公钥KU(e, n)中的e满足 1<e< (p-1)(q-1),且与(p-1)(q-1)互质;

4. 密钥KR(d, n)中的d满足  d*e % (p-1)(q-1)= 1,%是取余运算。

因为公钥是公开的,所以我知道了e和n,那么根据2,3,4式子的关系,我们只要从n的值推出p, q的值则可计算出d的值,也就能找到密钥。

然而,关键就在这里, n=p*q,如果两个互质数p和q足够大,那么根据目前的计算机技术和其他工具,至今也没能从n分解出p和q,这是数学上的一个难题,也正是这个难题成为了RSA加密至今被广泛使用的原因。换句话说,只要密钥长度n足够大(一般1024足矣),基本上不可能从公钥信息推出私钥信息。

 

好了,这里作为研究的随笔,记录一下java如何使用,以下主要有三种方法,基本大同小异,只是获取公钥私钥的途径不一样就是了:

方法一:

利用KeyPairGenerator直接生成公钥和密钥,一般私钥保留给服务端,公钥交给客户端。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class RSACryptography {  
  2.       
  3.     public static String data="hello world";  
  4.   
  5.     public static void main(String[] args) throws Exception {  
  6.         // TODO Auto-generated method stub  
  7.           
  8.         KeyPair keyPair=genKeyPair(1024);  
  9.           
  10.         //获取公钥,并以base64格式打印出来  
  11.         PublicKey publicKey=keyPair.getPublic();          
  12.         System.out.println("公钥:"+new String(Base64.getEncoder().encode(publicKey.getEncoded())));  
  13.           
  14.         //获取私钥,并以base64格式打印出来  
  15.         PrivateKey privateKey=keyPair.getPrivate();       
  16.         System.out.println("私钥:"+new String(Base64.getEncoder().encode(privateKey.getEncoded())));  
  17.           
  18.         //公钥加密  
  19.         byte[] encryptedBytes=encrypt(data.getBytes(), publicKey);    
  20.         System.out.println("加密后:"+new String(encryptedBytes));  
  21.           
  22.         //私钥解密  
  23.         byte[] decryptedBytes=decrypt(encryptedBytes, privateKey);        
  24.         System.out.println("解密后:"+new String(decryptedBytes));  
  25.     }  
  26.       
  27.     //生成密钥对  
  28.     public static KeyPair genKeyPair(int keyLength) throws Exception{  
  29.         KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance("RSA");  
  30.         keyPairGenerator.initialize(1024);        
  31.         return keyPairGenerator.generateKeyPair();  
  32.     }  
  33.       
  34.     //公钥加密  
  35.     public static byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception{  
  36.         Cipher cipher=Cipher.getInstance("RSA");//java默认"RSA"="RSA/ECB/PKCS1Padding"  
  37.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  38.         return cipher.doFinal(content);  
  39.     }  
  40.       
  41.     //私钥解密  
  42.     public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception{  
  43.         Cipher cipher=Cipher.getInstance("RSA");  
  44.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  45.         return cipher.doFinal(content);  
  46.     }  
  47.   
  48. }  

 

运行结果:

方法二:

实际上,方法一只是用来生成密钥就OK了,生成的密钥需要保存到本地文件中,所以一般不会在客户端调用KeyPairGenerator进行密钥的生成操作。

这里,我们可以将方法一得到的密钥保存到文件,下次我们直接读取就可以了。我假设以String的形式保存在文件内,那么接下来直接使用读取到的String生成密钥即可。

当然,你也可以使用openssl来生成也可以,不过我觉得麻烦就不弄了。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class RSACryptography {  
  2.       
  3.     public static String data="hello world";  
  4.     public static String publicKeyString="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCISLP98M/56HexX/9FDM8iuIEQozy6kn2JMcbZS5/BhJ+U4PZIChJfggYlWnd8NWn4BYr2kxxyO8Qgvc8rpRZCkN0OSLqLgZGmNvoSlDw80UXq90ZsVHDTOHuSFHw8Bv//B4evUNJBB8g9tpVxr6P5EJ6FMoR/kY2dVFQCQM4+5QIDAQAB";  
  5.     public static String privateKeyString="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIhIs/3wz/nod7Ff/0UMzyK4gRCjPLqSfYkxxtlLn8GEn5Tg9kgKEl+CBiVad3w1afgFivaTHHI7xCC9zyulFkKQ3Q5IuouBkaY2+hKUPDzRRer3RmxUcNM4e5IUfDwG//8Hh69Q0kEHyD22lXGvo/kQnoUyhH+RjZ1UVAJAzj7lAgMBAAECgYAVh26vsggY0Yl/Asw/qztZn837w93HF3cvYiaokxLErl/LVBJz5OtsHQ09f2IaxBFedfmy5CB9R0W/aly851JxrI8WAkx2W2FNllzhha01fmlNlOSumoiRF++JcbsAjDcrcIiR8eSVNuB6ymBCrx/FqhdX3+t/VUbSAFXYT9tsgQJBALsHurnovZS1qjCTl6pkNS0V5qio88SzYP7lzgq0eYGlvfupdlLX8/MrSdi4DherMTcutUcaTzgQU20uAI0EMyECQQC6il1Kdkw8Peeb0JZMHbs+cMCsbGATiAt4pfo1b/i9/BO0QnRgDqYcjt3J9Ux22dPYbDpDtMjMRNrAKFb4BJdFAkBMrdWTZOVc88IL2mcC98SJcII5wdL3YSeyOZto7icmzUH/zLFzM5CTsLq8/HDiqVArNJ4jwZia/q6Fg6e8KO2hAkB0EK1VLF/ox7e5GkK533Hmuu8XGWN6I5bHnbYd06qYQyTbbtHMBrFSaY4UH91Qwd3u9gAWqoCZoGnfT/o03V5lAkBqq8jZd2lHifey+9cf1hsHD5WQbjJKPPIb57CK08hn7vUlX5ePJ02Q8AhdZKETaW+EsqJWpNgsu5wPqsy2UynO";  
  6.       
  7.     public static void main(String[] args) throws Exception {  
  8.         // TODO Auto-generated method stub  
  9.           
  10.           
  11.         //获取公钥   
  12.         PublicKey publicKey=getPublicKey(publicKeyString);  
  13.           
  14.         //获取私钥   
  15.         PrivateKey privateKey=getPrivateKey(privateKeyString);        
  16.           
  17.         //公钥加密  
  18.         byte[] encryptedBytes=encrypt(data.getBytes(), publicKey);    
  19.         System.out.println("加密后:"+new String(encryptedBytes));  
  20.           
  21.         //私钥解密  
  22.         byte[] decryptedBytes=decrypt(encryptedBytes, privateKey);        
  23.         System.out.println("解密后:"+new String(decryptedBytes));  
  24.     }  
  25.       
  26.     //将base64编码后的公钥字符串转成PublicKey实例  
  27.     public static PublicKey getPublicKey(String publicKey) throws Exception{  
  28.         byte[ ] keyBytes=Base64.getDecoder().decode(publicKey.getBytes());  
  29.         X509EncodedKeySpec keySpec=new X509EncodedKeySpec(keyBytes);  
  30.         KeyFactory keyFactory=KeyFactory.getInstance("RSA");  
  31.         return keyFactory.generatePublic(keySpec);    
  32.     }  
  33.       
  34.     //将base64编码后的私钥字符串转成PrivateKey实例  
  35.     public static PrivateKey getPrivateKey(String privateKey) throws Exception{  
  36.         byte[ ] keyBytes=Base64.getDecoder().decode(privateKey.getBytes());  
  37.         PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(keyBytes);  
  38.         KeyFactory keyFactory=KeyFactory.getInstance("RSA");  
  39.         return keyFactory.generatePrivate(keySpec);  
  40.     }  
  41.       
  42.     //公钥加密  
  43.     public static byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception{  
  44.         Cipher cipher=Cipher.getInstance("RSA");//java默认"RSA"="RSA/ECB/PKCS1Padding"  
  45.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  46.         return cipher.doFinal(content);  
  47.     }  
  48.       
  49.     //私钥解密  
  50.     public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception{  
  51.         Cipher cipher=Cipher.getInstance("RSA");  
  52.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  53.         return cipher.doFinal(content);  
  54.     }  
  55.   
  56. }  
运行结果:

 

方法三:

除了保存密钥字符串之外,其他的做法一般是只保存 模n(modulus),公钥和私钥的e和d(exponent)。

其中,n, e, d可以这样获取到,获取到后可以保存到本地文件中。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. //获取公钥  
  2. RSAPublicKey publicKey=(RSAPublicKey) getPublicKey(publicKeyString);  
  3. BigInteger modulus1=publicKey.getModulus();  
  4. BigInteger exponent1=publicKey.getPublicExponent();  
  5.       
  6. //获取私钥  
  7. RSAPrivateKey privateKey=(RSAPrivateKey) getPrivateKey(privateKeyString);         
  8. BigInteger modulus2=privateKey.getModulus();  
  9. BigInteger exponent2=privateKey..getPrivateExponent();  

这里,假设我已经从文件中读取到了modulus和exponent:

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class RSACryptography {  
  2.       
  3.     public static String data="hello world";  
  4.     public static String modulusString="95701876885335270857822974167577168764621211406341574477817778908798408856077334510496515211568839843884498881589280440763139683446418982307428928523091367233376499779842840789220784202847513854967218444344438545354682865713417516385450114501727182277555013890267914809715178404671863643421619292274848317157";  
  5.     public static String publicExponentString="65537";  
  6.     public static String privateExponentString="15118200884902819158506511612629910252530988627643229329521452996670429328272100404155979400725883072214721713247384231857130859555987849975263007110480563992945828011871526769689381461965107692102011772019212674436519765580328720044447875477151172925640047963361834004267745612848169871802590337012858580097";  
  7.       
  8.     public static void main(String[] args) throws Exception {  
  9.         // TODO Auto-generated method stub        
  10.           
  11.         //由n和e获取公钥  
  12.         PublicKey publicKey=getPublicKey(modulusString, publicExponentString);  
  13.           
  14.         //由n和d获取私钥  
  15.         PrivateKey privateKey=getPrivateKey(modulusString, privateExponentString);  
  16.           
  17.         //公钥加密  
  18.         byte[] encryptedBytes=encrypt(data.getBytes(), publicKey);    
  19.         System.out.println("加密后:"+new String(encryptedBytes));  
  20.           
  21.         //私钥解密  
  22.         byte[] decryptedBytes=decrypt(encryptedBytes, privateKey);        
  23.         System.out.println("解密后:"+new String(decryptedBytes));  
  24.     }  
  25.       
  26.     //将base64编码后的公钥字符串转成PublicKey实例  
  27.     public static PublicKey getPublicKey(String modulusStr, String exponentStr) throws Exception{  
  28.         BigInteger modulus=new BigInteger(modulusStr);  
  29.         BigInteger exponent=new BigInteger(exponentStr);  
  30.         RSAPublicKeySpec publicKeySpec=new RSAPublicKeySpec(modulus, exponent);  
  31.         KeyFactory keyFactory=KeyFactory.getInstance("RSA");  
  32.         return keyFactory.generatePublic(publicKeySpec);  
  33.     }  
  34.       
  35.     //将base64编码后的私钥字符串转成PrivateKey实例  
  36.     public static PrivateKey getPrivateKey(String modulusStr, String exponentStr) throws Exception{  
  37.         BigInteger modulus=new BigInteger(modulusStr);  
  38.         BigInteger exponent=new BigInteger(exponentStr);  
  39.         RSAPrivateKeySpec privateKeySpec=new RSAPrivateKeySpec(modulus, exponent);  
  40.         KeyFactory keyFactory=KeyFactory.getInstance("RSA");  
  41.         return keyFactory.generatePrivate(privateKeySpec);  
  42.     }  
  43.       
  44.     //公钥加密  
  45.     public static byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception{  
  46.         Cipher cipher=Cipher.getInstance("RSA");//java默认"RSA"="RSA/ECB/PKCS1Padding"  
  47.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  48.         return cipher.doFinal(content);  
  49.     }  
  50.       
  51.     //私钥解密  
  52.     public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception{  
  53.         Cipher cipher=Cipher.getInstance("RSA");  
  54.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  55.         return cipher.doFinal(content);  
  56.     }  
  57.   
  58. }  
运行结果:

 



这里三种方式总结起来也就是

1,.KeyPairGenerator获取key;

2. String获取key;

3. modulus和exponent获取key。

--------------------后来,我发现,数据太长抛异常了,好吧---------------------------

然而,当加密的数据太长的时候则需要分组加密,不然数据过长会抛异常,如“Encryt data is too much”,或者“data length is longer than 127”等。

上面三个方法使用的key的n值(modulus)是1024bit的,也就是128byte,根据RSA加密规则,加密1 byte字节的数据,需要12 byte,即其他11byte可能用于记录其他信息什么的,这里我就不清楚了,而1024bit长度的key则最多可以加密128-11=117byte的数据,所以,对于超过117byte的数据,我们需要以117byte为一组进行数据分割。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class RSACryptography {  
  2.       
  3.     public static String data="hello world";  
  4.     public static String modulusString="95701876885335270857822974167577168764621211406341574477817778908798408856077334510496515211568839843884498881589280440763139683446418982307428928523091367233376499779842840789220784202847513854967218444344438545354682865713417516385450114501727182277555013890267914809715178404671863643421619292274848317157";  
  5.     public static String publicExponentString="65537";  
  6.     public static String privateExponentString="15118200884902819158506511612629910252530988627643229329521452996670429328272100404155979400725883072214721713247384231857130859555987849975263007110480563992945828011871526769689381461965107692102011772019212674436519765580328720044447875477151172925640047963361834004267745612848169871802590337012858580097";  
  7.       
  8.     public static void main(String[] args) throws Exception {  
  9.         // TODO Auto-generated method stub        
  10.           
  11.         //由n和e获取公钥  
  12.         PublicKey publicKey=getPublicKey(modulusString, publicExponentString);  
  13.           
  14.         //由n和d获取私钥  
  15.         PrivateKey privateKey=getPrivateKey(modulusString, privateExponentString);  
  16.           
  17.         //公钥加密  
  18.         String encrypted=encrypt(data, publicKey);    
  19.         System.out.println("加密后:"+encrypted);     
  20.           
  21.         //私钥解密  
  22.         String decrypted=decrypt(encrypted,  privateKey);         
  23.         System.out.println("解密后:"+new String(decrypted));  
  24.     }  
  25.       
  26.     //将base64编码后的公钥字符串转成PublicKey实例  
  27.     public static PublicKey getPublicKey(String modulusStr, String exponentStr) throws Exception{  
  28.         BigInteger modulus=new BigInteger(modulusStr);  
  29.         BigInteger exponent=new BigInteger(exponentStr);  
  30.         RSAPublicKeySpec publicKeySpec=new RSAPublicKeySpec(modulus, exponent);  
  31.         KeyFactory keyFactory=KeyFactory.getInstance("RSA");  
  32.         return keyFactory.generatePublic(publicKeySpec);  
  33.     }  
  34.       
  35.     //将base64编码后的私钥字符串转成PrivateKey实例  
  36.     public static PrivateKey getPrivateKey(String modulusStr, String exponentStr) throws Exception{  
  37.         BigInteger modulus=new BigInteger(modulusStr);  
  38.         BigInteger exponent=new BigInteger(exponentStr);  
  39.         RSAPrivateKeySpec privateKeySpec=new RSAPrivateKeySpec(modulus, exponent);  
  40.         KeyFactory keyFactory=KeyFactory.getInstance("RSA");  
  41.         return keyFactory.generatePrivate(privateKeySpec);  
  42.     }  
  43.       
  44.     //公钥加密,并转换成十六进制字符串打印出来  
  45.     public static String encrypt(String content, PublicKey publicKey) throws Exception{  
  46.         Cipher cipher=Cipher.getInstance("RSA");//java默认"RSA"="RSA/ECB/PKCS1Padding"  
  47.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  48.           
  49.         int splitLength=((RSAPublicKey)publicKey).getModulus().bitLength()/8-11;  
  50.         byte[][] arrays=splitBytes(content.getBytes(), splitLength);  
  51.         StringBuffer sb=new StringBuffer();  
  52.         for(byte[] array : arrays){           
  53.             sb.append(bytesToHexString(cipher.doFinal(array)));  
  54.         }  
  55.         return sb.toString();  
  56.     }  
  57.       
  58.     //私钥解密,并转换成十六进制字符串打印出来  
  59.     public static String decrypt(String content, PrivateKey privateKey) throws Exception{  
  60.         Cipher cipher=Cipher.getInstance("RSA");  
  61.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  62.           
  63.         int splitLength=((RSAPrivateKey)privateKey).getModulus().bitLength()/8;  
  64.         byte[] contentBytes=hexString2Bytes(content);  
  65.         byte[][] arrays=splitBytes(contentBytes, splitLength);  
  66.         StringBuffer sb=new StringBuffer();  
  67.         for(byte[] array : arrays){   
  68.             sb.append(new String(cipher.doFinal(array)));  
  69.         }  
  70.         return sb.toString();  
  71.     }  
  72.       
  73.     //拆分byte数组  
  74.     public static byte[][] splitBytes(byte[] bytes, int splitLength){  
  75.         int x; //商,数据拆分的组数,余数不为0时+1  
  76.         int y; //余数  
  77.         y=bytes.length%splitLength;  
  78.         if(y!=0){  
  79.             x=bytes.length/splitLength+1;  
  80.         }else{  
  81.             x=bytes.length/splitLength;  
  82.         }  
  83.         byte[][] arrays=new byte[x][];  
  84.         byte[] array;  
  85.         for(int i=0; i<x; i++){  
  86.               
  87.             if(i==x-1 && bytes.length%splitLength!=0){  
  88.                 array=new byte[bytes.length%splitLength];  
  89.                 System.arraycopy(bytes, i*splitLength, array, 0, bytes.length%splitLength);  
  90.             }else{  
  91.                 array=new byte[splitLength];  
  92.                 System.arraycopy(bytes, i*splitLength, array, 0, splitLength);  
  93.             }  
  94.             arrays[i]=array;  
  95.         }  
  96.         return arrays;  
  97.     }  
  98.       
  99.     //byte数组转十六进制字符串  
  100.     public static String bytesToHexString(byte[] bytes) {  
  101.         StringBuffer sb = new StringBuffer(bytes.length);  
  102.         String sTemp;  
  103.         for (int i = 0; i < bytes.length; i++) {  
  104.             sTemp = Integer.toHexString(0xFF & bytes[i]);  
  105.             if (sTemp.length() < 2)  
  106.                 sb.append(0);  
  107.             sb.append(sTemp.toUpperCase());  
  108.         }  
  109.         return sb.toString();  
  110.     }  
  111.       
  112.     //十六进制字符串转byte数组  
  113.     public static byte[] hexString2Bytes(String hex) {  
  114.         int len = (hex.length() / 2);  
  115.         hex=hex.toUpperCase();  
  116.         byte[] result = new byte[len];  
  117.         char[] achar = hex.toCharArray();  
  118.         for (int i = 0; i < len; i++) {  
  119.             int pos = i * 2;  
  120.             result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));  
  121.         }  
  122.         return result;  
  123.     }  
  124.       
  125.     private static byte toByte(char c) {  
  126.         byte b = (byte"0123456789ABCDEF".indexOf(c);  
  127.         return b;  
  128.     }  
  129.   
  130. }  

运行结果就不贴出来了。

 

最后,有一点必须强调,因为中间磨了我不少时间。

中间加密后,如果要打印出来,必须以十六进制或者BCD码的形式打印,不能new String(byte[])后,再从这个String里getbytes(),也不要用base64,不然会破坏原数据。

比如,举个例子:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. byte[ ] bytes=new byte[ ]{108, -5611134, -67};  
  2. byte[ ] newBytes=new String(bytes).getBytes();  
  3. StringBuffer sb=new StringBuffer();  
  4. for(int i=0; i<newBytes.length; i++){  
  5.     sb.append(newBytes[i]+"|");  
  6. }  
  7. System.out.println(sb.toString());  
将一个byte数组new String后再getbytes出来后,看看运行结果:

 


最后一个byte由-67变为了63,这个务必注意啊~



上一篇:Java集合---ArrayList的实现原理   下一篇: FusionCharts,双折线图和双柱状图