一切福田,不離方寸,從心而覓,感無不通。

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

需求环境:

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

LoginAction.java

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

 

登陆login.jsp

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

登陆校验loginCHK.jsp

Html代码
  1. <%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
  2. <jsp:directive.page import="RSA.LoginAction"/>
  3. <%
  4. LoginAction la = new LoginAction();
  5. la.execute(request ,response);
  6. %>
  7. pwd is [<%=request.getAttribute("pwd")%>]

 

 

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

 

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

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

 

 

 

  • RSA (1.4 MB)
  • 描述: js和JAVA实现RSA的demo

from:http://sosuny.iteye.com/blog/793327