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

原创     发表于 2020-10-20 17:46     阅读 135  

用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

  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 
  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 
  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" οnclick="rsalogin();" />
  39. </td>
  40. </tr>
  41. </table>
  42. </form>
  43. </body>
  44. </html:html>


登陆校验loginCHK.jsp 
  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")%>]