关于使用JS前台加密、JAVA后台解密的RSA实现,RSA加密和签名

时间:2022-02-18 03:58:27

需求环境:

西安项目中,客户要求保护用户的密码信息,不允许在http中传递明文的密码信息。

 

实现:

用RSA非对称加密方式实现。后台生成rsa密钥对,然后在登陆页面设置rsa公钥,提交时用公钥加密密码,生成的密文传到后台,用私钥解密,获取密码明文。

这样客户端只需要知道rsa加密方式和公钥,前台不知道私钥是无法解密的,此解决方案还是相对比较安全的。

附件是参照网友资料的Java+JS的实现,放在这里供大家下载。访问方式/RSA/login.jsp。

需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar文件。

因为后台要转换成bigint,所以对明文长度有些限制:

总长度不超过126(1汉字长度为9),如下两个字符串:

阿送大法散得阿送大法散得阿送
1232132131231231232131232K1232132131231231232131232K1232132131231231232131232K1232132131231231232131232K1234567890123456789012

 

 

RSA速度  
 * 由于进行的都是大数计算,使得RSA最快的情况也比DES慢上100倍,无论 是软件还是硬件实现。  
 * 速度一直是RSA的缺陷。一般来说只用于少量数据 加密。

 

 

Util.java

 

Java代码   关于使用JS前台加密、JAVA后台解密的RSA实现,RSA加密和签名
  1. package RSA;  
  2.   
  3. /** 
  4.  *  
  5.  */  
  6.   
  7. import java.io.ByteArrayOutputStream;  
  8. import java.io.FileInputStream;  
  9. import java.io.FileOutputStream;  
  10. import java.io.ObjectInputStream;  
  11. import java.io.ObjectOutputStream;  
  12. import java.math.BigInteger;  
  13. import java.security.KeyFactory;  
  14. import java.security.KeyPair;  
  15. import java.security.KeyPairGenerator;  
  16. import java.security.NoSuchAlgorithmException;  
  17. import java.security.PrivateKey;  
  18. import java.security.PublicKey;  
  19. import java.security.SecureRandom;  
  20. import java.security.interfaces.RSAPrivateKey;  
  21. import java.security.interfaces.RSAPublicKey;  
  22. import java.security.spec.InvalidKeySpecException;  
  23. import java.security.spec.RSAPrivateKeySpec;  
  24. import java.security.spec.RSAPublicKeySpec;  
  25.   
  26. import javax.crypto.Cipher;  
  27.   
  28. /** 
  29.  * RSA 工具类。提供加密,解密,生成密钥对等方法。 
  30.  * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。 
  31.  *  
  32.  */  
  33. public class RSAUtil {  
  34.       
  35.     private static String RSAKeyStore = "C:/RSAKey.txt";  
  36.     /** 
  37.      * * 生成密钥对 * 
  38.      *  
  39.      * @return KeyPair * 
  40.      * @throws EncryptException 
  41.      */  
  42.     public static KeyPair generateKeyPair() throws Exception {  
  43.         try {  
  44.             KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",  
  45.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  46.             final int KEY_SIZE = 1024;// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低  
  47.             keyPairGen.initialize(KEY_SIZE, new SecureRandom());  
  48.             KeyPair keyPair = keyPairGen.generateKeyPair();  
  49.               
  50.             System.out.println(keyPair.getPrivate());  
  51.             System.out.println(keyPair.getPublic());  
  52.               
  53.             saveKeyPair(keyPair);  
  54.             return keyPair;  
  55.         } catch (Exception e) {  
  56.             throw new Exception(e.getMessage());  
  57.         }  
  58.     }  
  59.   
  60.     public static KeyPair getKeyPair() throws Exception {  
  61.         FileInputStream fis = new FileInputStream(RSAKeyStore);  
  62.         ObjectInputStream oos = new ObjectInputStream(fis);  
  63.         KeyPair kp = (KeyPair) oos.readObject();  
  64.         oos.close();  
  65.         fis.close();  
  66.         return kp;  
  67.     }  
  68.   
  69.     public static void saveKeyPair(KeyPair kp) throws Exception {  
  70.   
  71.         FileOutputStream fos = new FileOutputStream(RSAKeyStore);  
  72.         ObjectOutputStream oos = new ObjectOutputStream(fos);  
  73.         // 生成密钥  
  74.         oos.writeObject(kp);  
  75.         oos.close();  
  76.         fos.close();  
  77.     }  
  78.   
  79.     /** 
  80.      * * 生成公钥 * 
  81.      *  
  82.      * @param modulus * 
  83.      * @param publicExponent * 
  84.      * @return RSAPublicKey * 
  85.      * @throws Exception 
  86.      */  
  87.     public static RSAPublicKey generateRSAPublicKey(byte[] modulus,  
  88.             byte[] publicExponent) throws Exception {  
  89.         KeyFactory keyFac = null;  
  90.         try {  
  91.             keyFac = KeyFactory.getInstance("RSA",  
  92.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  93.         } catch (NoSuchAlgorithmException ex) {  
  94.             throw new Exception(ex.getMessage());  
  95.         }  
  96.   
  97.         RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(  
  98.                 modulus), new BigInteger(publicExponent));  
  99.         try {  
  100.             return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);  
  101.         } catch (InvalidKeySpecException ex) {  
  102.             throw new Exception(ex.getMessage());  
  103.         }  
  104.     }  
  105.   
  106.     /** 
  107.      * * 生成私钥 * 
  108.      *  
  109.      * @param modulus * 
  110.      * @param privateExponent * 
  111.      * @return RSAPrivateKey * 
  112.      * @throws Exception 
  113.      */  
  114.     public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,  
  115.             byte[] privateExponent) throws Exception {  
  116.         KeyFactory keyFac = null;  
  117.         try {  
  118.             keyFac = KeyFactory.getInstance("RSA",  
  119.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  120.         } catch (NoSuchAlgorithmException ex) {  
  121.             throw new Exception(ex.getMessage());  
  122.         }  
  123.   
  124.         RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(  
  125.                 modulus), new BigInteger(privateExponent));  
  126.         try {  
  127.             return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);  
  128.         } catch (InvalidKeySpecException ex) {  
  129.             throw new Exception(ex.getMessage());  
  130.         }  
  131.     }  
  132.   
  133.     /** 
  134.      * * 加密 * 
  135.      *  
  136.      * @param key 
  137.      *            加密的密钥 * 
  138.      * @param data 
  139.      *            待加密的明文数据 * 
  140.      * @return 加密后的数据 * 
  141.      * @throws Exception 
  142.      */  
  143.     public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {  
  144.         try {  
  145.             Cipher cipher = Cipher.getInstance("RSA",  
  146.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  147.             cipher.init(Cipher.ENCRYPT_MODE, pk);  
  148.             int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024  
  149.             // 加密块大小为127  
  150.             // byte,加密后为128个byte;因此共有2个加密块,第一个127  
  151.             // byte第二个为1个byte  
  152.             int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小  
  153.             int leavedSize = data.length % blockSize;  
  154.             int blocksSize = leavedSize != 0 ? data.length / blockSize + 1  
  155.                     : data.length / blockSize;  
  156.             byte[] raw = new byte[outputSize * blocksSize];  
  157.             int i = 0;  
  158.             while (data.length - i * blockSize > 0) {  
  159.                 if (data.length - i * blockSize > blockSize)  
  160.                     cipher.doFinal(data, i * blockSize, blockSize, raw, i  
  161.                             * outputSize);  
  162.                 else  
  163.                     cipher.doFinal(data, i * blockSize, data.length - i  
  164.                             * blockSize, raw, i * outputSize);  
  165.                 // 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到  
  166.                 // ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了  
  167.                 // OutputSize所以只好用dofinal方法。  
  168.   
  169.                 i++;  
  170.             }  
  171.             return raw;  
  172.         } catch (Exception e) {  
  173.             throw new Exception(e.getMessage());  
  174.         }  
  175.     }  
  176.   
  177.     /** 
  178.      * * 解密 * 
  179.      *  
  180.      * @param key 
  181.      *            解密的密钥 * 
  182.      * @param raw 
  183.      *            已经加密的数据 * 
  184.      * @return 解密后的明文 * 
  185.      * @throws Exception 
  186.      */  
  187.     public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {  
  188.         try {  
  189.             Cipher cipher = Cipher.getInstance("RSA",  
  190.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  
  191.             cipher.init(cipher.DECRYPT_MODE, pk);  
  192.             int blockSize = cipher.getBlockSize();  
  193.             ByteArrayOutputStream bout = new ByteArrayOutputStream(64);  
  194.             int j = 0;  
  195.   
  196.             while (raw.length - j * blockSize > 0) {  
  197.                 bout.write(cipher.doFinal(raw, j * blockSize, blockSize));  
  198.                 j++;  
  199.             }  
  200.             return bout.toByteArray();  
  201.         } catch (Exception e) {  
  202.             throw new Exception(e.getMessage());  
  203.         }  
  204.     }  
  205.   
  206.     /** 
  207.      * * * 
  208.      *  
  209.      * @param args * 
  210.      * @throws Exception 
  211.      */  
  212.     public static void main(String[] args) throws Exception {  
  213.         RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair().getPublic();  
  214.         String test = "hello world";  
  215.         byte[] en_test = encrypt(getKeyPair().getPublic(), test.getBytes());  
  216.         byte[] de_test = decrypt(getKeyPair().getPrivate(), en_test);  
  217.         System.out.println(new String(de_test));  
  218.     }  
  219. }  
 

LoginAction.java

Java代码   关于使用JS前台加密、JAVA后台解密的RSA实现,RSA加密和签名
  1. package RSA;  
  2.   
  3. //login  
  4. /* 
  5.  * Generated by MyEclipse Struts 
  6.  * Template path: templates/java/JavaClass.vtl 
  7.  */  
  8. import java.math.BigInteger;  
  9. import java.net.URLDecoder;  
  10. import java.net.URLEncoder;  
  11.   
  12. import javax.servlet.http.HttpServletRequest;  
  13. import javax.servlet.http.HttpServletResponse;  
  14.   
  15. import RSA.RSAUtil;  
  16.   
  17. /** 
  18.  * MyEclipse Struts Creation date: 06-28-2008 
  19.  *  
  20.  * XDoclet definition: 
  21.  *  
  22.  * @struts.action path="/login" name="loginForm" input="/login.jsp" 
  23.  *                scope="request" validate="true" 
  24.  * @struts.action-forward name="error" path="/error.jsp" 
  25.  * @struts.action-forward name="success" path="/success.jsp" 
  26.  */  
  27. public class LoginAction {  
  28.     /* 
  29.      * Generated Methods 
  30.      */  
  31.   
  32.     /** 
  33.      * Method execute 
  34.      *  
  35.      * @param mapping 
  36.      * @param form 
  37.      * @param request 
  38.      * @param response 
  39.      * @return ActionForward 
  40.      */  
  41.     public boolean execute(HttpServletRequest request,  
  42.             HttpServletResponse response) throws Exception {  
  43.         String pwd ;  
  44.         String result = request.getParameter("result");  
  45.         System.out.println("原文加密后为:");  
  46.         System.out.println(result);  
  47.         byte[] en_result = new BigInteger(result, 16).toByteArray();  
  48.         //System.out.println("转成byte[]" + new String(en_result));  
  49.         byte[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),  
  50.                 en_result);  
  51.         System.out.println("还原密文:");  
  52.         System.out.println(new String(de_result));  
  53.         StringBuffer sb = new StringBuffer();  
  54.         sb.append(new String(de_result));  
  55.         pwd = sb.reverse().toString();  
  56.         System.out.println(sb);  
  57.         System.out.println("=================================");  
  58.         pwd = URLDecoder.decode(pwd,"UTF-8");//  
  59.         System.out.println(pwd);  
  60.         request.setAttribute("pwd", pwd);  
  61.         return true;  
  62.     }  
  63. }  

 

登陆login.jsp

Html代码   关于使用JS前台加密、JAVA后台解密的RSA实现,RSA加密和签名
  1. <%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>  
  2.   
  3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  4. <html:html lang="true">  
  5. <head>  
  6.     <title>login</title>  
  7.   
  8.     <script type="text/javascript" src="js/RSA.js"></script>  
  9.     <script type="text/javascript" src="js/BigInt.js"></script>  
  10.     <script type="text/javascript" src="js/Barrett.js"></script>  
  11.       
  12.     <script type="text/javascript">  
  13. function rsalogin(){  
  14.     var thisPwd = document.getElementById("password").value;  
  15.     bodyRSA();  
  16.     var result = encryptedString(key, encodeURIComponent(thisPwd));  
  17.     //alert(encodeURIComponent(thisPwd)+"\r\n"+result);  
  18.     loginForm.action="loginCHK.jsp?result="+result;  
  19.     loginForm.submit();  
  20. }  
  21. var key ;  
  22. function bodyRSA(){  
  23.     setMaxDigits(130);  
  24.     key = new RSAKeyPair("10001","","8246a46f44fc4d961e139fd70f4787d272d374532f4d2d9b7cbaad6a15a8c1301319aa6b3f30413b859351c71938aec516fa7147b69168b195e81df46b6bed7950cf3a1c719d42175f73d7c97a85d7d20a9e83688b92f05b3059bb2ff75cd7190a042cd2db97ebc2ab4da366f2a7085556ed613b5a39c9fdd2bb2595d1dc23b5");   
  25. }  
  26. </script>  
  27. </head>  
  28.   
  29. <body>  
  30.     <form method="post" name="loginForm" target=_blank>  
  31.         <table border="0">  
  32.             <tr>  
  33.                 <td>  
  34.                     Password:  
  35.                 </td>  
  36.                 <td>  
  37.                     <input type='text' name="password" id=password style='width:400px' value="my passwd"/>  
  38.                 </td>  
  39.             </tr>  
  40.             <tr>  
  41.                 <td colspan="2" align="center">  
  42.                     <input type="button" value="SUBMIT" onclick="rsalogin();" />  
  43.                 </td>  
  44.             </tr>  
  45.         </table>  
  46.     </form>  
  47. </body>  
  48. </html:html>  
 

登陆校验loginCHK.jsp

Html代码   关于使用JS前台加密、JAVA后台解密的RSA实现,RSA加密和签名
  1. <%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>  
  2.   
  3. <jsp:directive.page import="RSA.LoginAction"/>  
  4.   
  5. <%  
  6. LoginAction la = new LoginAction();  
  7. la.execute(request ,response);  
  8. %>  
  9. pwd is [<%=request.getAttribute("pwd")%>]  

 

 

================================================

 

另外,通过RSA还可以实现数字签名 :用私钥签名、公钥验证即可。具体可以看代码:

Java代码   关于使用JS前台加密、JAVA后台解密的RSA实现,RSA加密和签名
  1. package encode;  
  2.   
  3. import java.security.KeyPair;  
  4. import java.security.KeyPairGenerator;  
  5. import java.security.Signature;  
  6.   
  7. public class DigitalSignature2Example {  
  8.     public static void main(String[] args) {  
  9.         args = new String[] { "中国" };  
  10.           
  11.         if (args.length != 1) {  
  12.             System.err.println("Usage:java DigitalSignature2Example ");  
  13.             System.exit(1);  
  14.         }  
  15.   
  16.         try {  
  17.             byte[] plainText = args[0].getBytes("UTF-8");  
  18.             System.out.println("\nStart generating RSA key.");  
  19.             // 生成RSA密钥对  
  20.             KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");  
  21.             // 初始化密钥长度  
  22.             keyGen.initialize(1024);  
  23.             // 生成密钥对  
  24.             KeyPair pair = keyGen.generateKeyPair();  
  25.             System.out.println("\nFinish generating RSA key.");  
  26.             // 使用私钥签名  
  27.             Signature sig = Signature.getInstance("SHA1WithRSA");  
  28.             // 用指定的私钥进行初始化  
  29.             sig.initSign(pair.getPrivate());  
  30.             // 添加要签名的信息  
  31.             sig.update(plainText);  
  32.             // 返回签名的字节数组  
  33.             byte[] signature = sig.sign();  
  34.             System.out.println(sig.getProvider().getInfo());  
  35.             System.out.println("\nSignature: ");  
  36.             System.out.println(new String(signature, "UTF-8"));  
  37.   
  38.             // 使用公钥验证  
  39.             System.out.println("\nStart signature verification.");  
  40.             sig.initVerify(pair.getPublic());  
  41.             // 添加要验证的信息  
  42.             sig.update(plainText);  
  43.             if (sig.verify(signature)) {  
  44.                 System.out.println("Signature verificated.");  
  45.             } else {  
  46.                 System.out.println("Signature failed.");  
  47.             }  
  48.         } catch (Exception e) {  
  49.             e.printStackTrace();  
  50.         }  
  51.     }  
  52. }