1. 程式人生 > >用javascript與java進行RSA加密與解密

用javascript與java進行RSA加密與解密

這幾天一直做安全登入,網上查了好多資料,不盡如意。

具體實現思路如下:

1。服務端生成公鑰與私鑰,儲存。

2。客戶端在請求到登入頁面後,隨機生成一字串。

3。後此隨機字串作為金鑰加密密碼,再用從服務端獲取到的公鑰加密生成的隨機字串。

4。將此兩段密文傳入服務端,服務端用私鑰解出隨機字串,再用此私鑰解出加密的密文。

這其中有一個關鍵是解決服務端的公鑰,傳入客戶端,客戶端用此公鑰加密字串後,後又能在服務端用私鑰解出。

此文即為實現此步而作。

加密演算法為RSA:

1。服務端的RSA  java實現。

Java程式碼  收藏程式碼
  1. /** 
  2.  *  
  3.  */
  4. package com.sunsoft.struts.util;  
  5. import java.io.ByteArrayOutputStream;  
  6. import java.io.FileInputStream;  
  7. import java.io.FileOutputStream;  
  8. import java.io.ObjectInputStream;  
  9. import java.io.ObjectOutputStream;  
  10. import java.math.BigInteger;  
  11. import java.security.KeyFactory;  
  12. import java.security.KeyPair;  
  13. import java.security.KeyPairGenerator;  
  14. import java.security.NoSuchAlgorithmException;  
  15. import java.security.PrivateKey;  
  16. import java.security.PublicKey;  
  17. import java.security.SecureRandom;  
  18. import java.security.interfaces.RSAPrivateKey;  
  19. import java.security.interfaces.RSAPublicKey;  
  20. import java.security.spec.InvalidKeySpecException;  
  21. import
     java.security.spec.RSAPrivateKeySpec;  
  22. import java.security.spec.RSAPublicKeySpec;  
  23. import javax.crypto.Cipher;  
  24. /** 
  25.  * RSA 工具類。提供加密,解密,生成金鑰對等方法。 
  26.  * 需要到http://www.bouncycastle.org下載bcprov-jdk14-123.jar。 
  27.  *  
  28.  */
  29. publicclass RSAUtil {  
  30.     /** 
  31.      * * 生成金鑰對 * 
  32.      *  
  33.      * @return KeyPair * 
  34.      * @throws EncryptException 
  35.      */
  36.     publicstatic KeyPair generateKeyPair() throws Exception {  
  37.         try {  
  38.             KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",  
  39.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  40.             finalint KEY_SIZE = 1024;// 沒什麼好說的了,這個值關係到塊加密的大小,可以更改,但是不要太大,否則效率會低
  41.             keyPairGen.initialize(KEY_SIZE, new SecureRandom());  
  42.             KeyPair keyPair = keyPairGen.generateKeyPair();  
  43.             saveKeyPair(keyPair);  
  44.             return keyPair;  
  45.         } catch (Exception e) {  
  46.             thrownew Exception(e.getMessage());  
  47.         }  
  48.     }  
  49.     publicstatic KeyPair getKeyPair()throws Exception{  
  50.         FileInputStream fis = new FileInputStream("C:/RSAKey.txt");  
  51.          ObjectInputStream oos = new ObjectInputStream(fis);  
  52.          KeyPair kp= (KeyPair) oos.readObject();  
  53.          oos.close();  
  54.          fis.close();  
  55.          return kp;  
  56.     }  
  57.     publicstaticvoid saveKeyPair(KeyPair kp)throws Exception{  
  58.          FileOutputStream fos = new FileOutputStream("C:/RSAKey.txt");  
  59.          ObjectOutputStream oos = new ObjectOutputStream(fos);  
  60.          //生成金鑰
  61.          oos.writeObject(kp);  
  62.          oos.close();  
  63.          fos.close();  
  64.     }  
  65.     /** 
  66.      * * 生成公鑰 * 
  67.      *  
  68.      * @param modulus * 
  69.      * @param publicExponent * 
  70.      * @return RSAPublicKey * 
  71.      * @throws Exception 
  72.      */
  73.     publicstatic RSAPublicKey generateRSAPublicKey(byte[] modulus,  
  74.             byte[] publicExponent) throws Exception {  
  75.         KeyFactory keyFac = null;  
  76.         try {  
  77.             keyFac = KeyFactory.getInstance("RSA",  
  78.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  79.         } catch (NoSuchAlgorithmException ex) {  
  80.             thrownew Exception(ex.getMessage());  
  81.         }  
  82.         RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(  
  83.                 modulus), new BigInteger(publicExponent));  
  84.         try {  
  85.             return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);  
  86.         } catch (InvalidKeySpecException ex) {  
  87.             thrownew Exception(ex.getMessage());  
  88.         }  
  89.     }  
  90.     /** 
  91.      * * 生成私鑰 * 
  92.      *  
  93.      * @param modulus * 
  94.      * @param privateExponent * 
  95.      * @return RSAPrivateKey * 
  96.      * @throws Exception 
  97.      */
  98.     publicstatic RSAPrivateKey generateRSAPrivateKey(byte[] modulus,  
  99.             byte[] privateExponent) throws Exception {  
  100.         KeyFactory keyFac = null;  
  101.         try {  
  102.             keyFac = KeyFactory.getInstance("RSA",  
  103.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  104.         } catch (NoSuchAlgorithmException ex) {  
  105.             thrownew Exception(ex.getMessage());  
  106.         }  
  107.         RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(  
  108.                 modulus), new BigInteger(privateExponent));  
  109.         try {  
  110.             return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);  
  111.         } catch (InvalidKeySpecException ex) {  
  112.             thrownew Exception(ex.getMessage());  
  113.         }  
  114.     }  
  115.     /** 
  116.      * * 加密 * 
  117.      *  
  118.      * @param key 
  119.      *            加密的金鑰 * 
  120.      * @param data 
  121.      *            待加密的明文資料 * 
  122.      * @return 加密後的資料 * 
  123.      * @throws Exception 
  124.      */
  125.     publicstaticbyte[] encrypt(PublicKey pk, byte[] data) throws Exception {  
  126.         try {  
  127.             Cipher cipher = Cipher.getInstance("RSA",  
  128.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  129.             cipher.init(Cipher.ENCRYPT_MODE, pk);  
  130.             int blockSize = cipher.getBlockSize();// 獲得加密塊大小,如:加密前資料為128個byte,而key_size=1024
  131.             // 加密塊大小為127
  132.             // byte,加密後為128個byte;因此共有2個加密塊,第一個127
  133.             // byte第二個為1個byte
  134.             int outputSize = cipher.getOutputSize(data.length);// 獲得加密塊加密後塊大小
  135.             int leavedSize = data.length % blockSize;  
  136.             int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
  137.                     : data.length / blockSize;  
  138.             byte[] raw = newbyte[outputSize * blocksSize];  
  139.             int i = 0;  
  140.             while (data.length - i * blockSize > 0) {  
  141.                 if (data.length - i * blockSize > blockSize)  
  142.                     cipher.doFinal(data, i * blockSize, blockSize, raw, i  
  143.                             * outputSize);  
  144.                 else
  145.                     cipher.doFinal(data, i * blockSize, data.length - i  
  146.                             * blockSize, raw, i * outputSize);  
  147.                 // 這裡面doUpdate方法不可用,檢視原始碼後發現每次doUpdate後並沒有什麼實際動作除了把byte[]放到
  148.                 // ByteArrayOutputStream中,而最後doFinal的時候才將所有的byte[]進行加密,可是到了此時加密塊大小很可能已經超出了
  149.                 // OutputSize所以只好用dofinal方法。
  150.                 i++;  
  151.             }  
  152.             return raw;  
  153.         } catch (Exception e) {  
  154.             thrownew Exception(e.getMessage());  
  155.         }  
  156.     }  
  157.     /** 
  158.      * * 解密 * 
  159.      *  
  160.      * @param key 
  161.      *            解密的金鑰 * 
  162.      * @param raw 
  163.      *            已經加密的資料 * 
  164.      * @return 解密後的明文 * 
  165.      * @throws Exception 
  166.      */
  167.     publicstaticbyte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {  
  168.         try {  
  169.             Cipher cipher = Cipher.getInstance("RSA",  
  170.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  171.             cipher.init(cipher.DECRYPT_MODE, pk);  
  172.             int blockSize = cipher.getBlockSize();  
  173.             ByteArrayOutputStream bout = new ByteArrayOutputStream(64);  
  174.             int j = 0;  
  175.             while (raw.length - j * blockSize > 0) {  
  176.                 bout.write(cipher.doFinal(raw, j * blockSize, blockSize));  
  177.                 j++;  
  178.             }  
  179.             return bout.toByteArray();  
  180.         } catch (Exception e) {  
  181.             thrownew Exception(e.getMessage());  
  182.         }  
  183.     }  
  184.     /** 
  185.      * * * 
  186.      *  
  187.      * @param args * 
  188.      * @throws Exception 
  189.      */
  190.     publicstaticvoid main(String[] args) throws Exception {  
  191.         RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair().getPublic();  
  192.         String test = "hello world";  
  193.         byte[] en_test = encrypt(getKeyPair().getPublic(),test.getBytes());  
  194.         byte[] de_test = decrypt(getKeyPair().getPrivate(),en_test);  
  195.         System.out.println(new String(de_test));  
  196.     }  
  197. }  

 2.測試頁面:

IndexAction.java

Java程式碼  收藏程式碼
  1. /* 
  2.  * Generated by MyEclipse Struts 
  3.  * Template path: templates/java/JavaClass.vtl 
  4.  */
  5. package com.sunsoft.struts.action;  
  6. import java.security.interfaces.RSAPrivateKey;  
  7. import java.security.interfaces.RSAPublicKey;  
  8. import javax.servlet.http.HttpServletRequest;  
  9. import javax.servlet.http.HttpServletResponse;  
  10. import org.apache.struts.action.Action;  
  11. import org.apache.struts.action.ActionForm;  
  12. import org.apache.struts.action.ActionForward;  
  13. import org.apache.struts.action.ActionMapping;  
  14. import com.sunsoft.struts.util.RSAUtil;  
  15. /**  
  16.  * MyEclipse Struts 
  17.  * Creation date: 06-28-2008 
  18.  *  
  19.  * XDoclet definition: 
  20.  * @struts.action validate="true" 
  21.  */
  22. publicclass IndexAction extends Action {  
  23.     /* 
  24.      * Generated Methods 
  25.      */
  26.     /**  
  27.      * Method execute 
  28.      * @param mapping 
  29.      * @param form 
  30.      * @param request 
  31.      * @param response 
  32.      * @return ActionForward 
  33.      */
  34.     public ActionForward execute(ActionMapping mapping, ActionForm form,  
  35.             HttpServletRequest request, HttpServletResponse response)throws Exception {  
  36.         RSAPublicKey rsap = (RSAPublicKey) RSAUtil.getKeyPair().getPublic();  
  37.         String module = rsap.getModulus().toString(16);  
  38.         String empoent = rsap.getPublicExponent().toString(16);  
  39.         System.out.println("module");  
  40.         System.out.println(module);  
  41.         System.out.println("empoent");  
  42.         System.out.println(empoent);  
  43.         request.setAttribute("m", module);  
  44.         request.setAttribute("e", empoent);  
  45.         return mapping.findForward("login");  
  46.     }  
  47. }  

 通過此action進入登入頁面,並傳入公鑰的 Modulus 與PublicExponent的hex編碼形式。

3。登入頁面 login.jsp

Html程式碼  收藏程式碼
  1. <%@ page language="java"pageEncoding="GBK"%>
  2. <%@ taglib uri="http://struts.apache.org/tags-bean"prefix="bean" %>
  3. <%@ taglib uri="http://struts.apache.org/tags-html"prefix="html" %>
  4. <%@ taglib uri="http://struts.apache.org/tags-logic"prefix="logic" %>
  5. <%@ taglib uri="http://struts.apache.org/tags-tiles"prefix="tiles" %>
  6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  7. <html:htmllang="true">
  8.   <head>
  9.     <html:base/>
  10.     <title>login</title>
  11.     <metahttp-equiv="pragma"content="no-cache">
  12.     <metahttp-equiv="cache-control"content="no-cache">
  13.     <metahttp-equiv="expires"content="0">
  14.     <metahttp-equiv="keywords"content="keyword1,keyword2,keyword3">
  15.     <metahttp-equiv="description"content="This is my page">
  16.     <!-- 
  17.     <link rel="stylesheet" type="text/css" href="styles.css"> 
  18.     -->
  19. <scripttype="text/javascript"src="js/RSA.js"></script>
  20. <scripttype="text/javascript"src="js/BigInt.js"></script>
  21. <scripttype="text/javascript"src="js/Barrett.js"></script>
  22. <scripttype="text/javascript">
  23. function rsalogin()  
  24. {  
  25.    bodyRSA();  
  26.    var result = encryptedString(key, document.getElementById("pwd").value);  
  27.    //alert(result);  
  28.    loginForm.action="login.do?result="+result;  
  29.    loginForm.submit();  
  30. }  
  31. var key ;  
  32. function bodyRSA()  
  33. {  
  34.     setMaxDigits(130);  
  35.     key = new RSAKeyPair("10001","","8c1cd09a04ed01aafe70dc84c5f32ae23a16fe8fc8898aba6797c5a9c708720de4f08dbf086af429fc51c0636208f56de20a8ab5686affd9bdfb643ae1e90d5617155c4867eef06b0884ba8ecd187907c7069ae3eed4f0155eeca6573411864035ae803ad8fd91a0cc479f27e41b19c13465ab30f3cfbfd14de56f49cbd09481");   
  36. }  
  37. </script>
  38.   </head>
  39.   <body>
  40.     <html:formaction="login"method="post"focus="username">
  41.       <tableborder="0">
  42.         <tr>
  43.           <td>Login:</td>
  44.           <td><html:textproperty="username"/></td>
  45.         </tr>
  46.         <tr>
  47.           <td>Password:</td>
  48.           <td><html:passwordproperty="password"styleId="pwd"/></td>
  49.         </tr>
  50.         <tr>
  51.           <tdcolspan="2"align="center"><inputtype="button"value="SUBMIT"onclick="rsalogin();"/></td>
  52.         </tr>
  53.       </table>
  54.     </html:form>
  55.   </body>
  56. </html:html>

 3.點選登入後,呼叫LoginAction.java

Java程式碼  收藏程式碼
  1. /* 
  2.  * Generated by MyEclipse Struts 
  3.  * Template path: templates/java/JavaClass.vtl 
  4.  */
  5. package com.sunsoft.struts.action;  
  6. import java.math.BigInteger;  
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpServletResponse;  
  9. import org.apache.struts.action.Action;  
  10. import org.apache.struts.action.ActionForm;  
  11. import org.apache.struts.action.ActionForward;  
  12. import org.apache.struts.action.ActionMapping;  
  13. import com.sunsoft.struts.util.RSAUtil;  
  14. /**  
  15.  * MyEclipse Struts 
  16.  * Creation date: 06-28-2008 
  17.  *  
  18.  * XDoclet definition: 
  19.  * @struts.action path="/login" name="loginForm" input="/login.jsp" scope="request" validate="true" 
  20.  * @struts.action-forward name="error" path="/error.jsp" 
  21.  * @struts.action-forward name="success" path="/success.jsp" 
  22.  */
  23. publicclass LoginAction extends Action {  
  24.     /* 
  25.      * Generated Methods 
  26.      */
  27.     /**  
  28.      * Method execute 
  29.      * @param mapping 
  30.      * @param form 
  31.      * @param request 
  32.      * @param response 
  33.      * @return ActionForward 
  34.      */
  35.     public ActionForward execute(ActionMapping mapping, ActionForm form,  
  36.             HttpServletRequest request, HttpServletResponse response) throws Exception{  
  37.         //LoginForm loginForm = (LoginForm) form;
  38.         String result = request.getParameter("result");  
  39.         System.out.println("原文加密後為:");  
  40.         System.out.println(result);  
  41.         byte[] en_result = new BigInteger(result, 16).toByteArray();  
  42.         System.out.println("轉成byte[]"+new String(en_result));  
  43.         byte[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_result);  
  44.         System.out.println("還原密文:");  
  45.         System.out.println(new String(de_result));  
  46.         StringBuffer sb = new StringBuffer();  
  47.         sb.append(new String(de_result));  
  48.         System.out.println(sb.reverse().toString());  
  49.         return mapping.findForward("success");  
  50.     }  
  51. }  

 因為發現解出的明文是倒序的,後面就用StringBuffer的reverse()來轉換了一下。

4。login.jsp所呼叫的js

  • 描述: login.jsp所呼叫的javascript,有:RSA.jsBigInt.jsBarrett.js
原文地址:http://sunxboy.iteye.com/blog/209156