RSA非对称加密算法解析:密钥、明文及密文长度的约定--以及使用RSA算法实现登录时的前后端的加解密

1、RSA算法基础

RSA算法是一种非对称加密算法,非对称即:加解密用的不是同一个秘钥,它有一对秘钥,分为公钥和私钥。公钥加密,一般是客户端进行处理;私钥解密,一般是后端处理。公钥要暴露给加密方使用,私钥则要藏起来,一般由服务器管理。

1.1关于秘钥长度

一般来说,我们默认使用或常用的秘钥长度值是1024bit位,即1024/8=128byte,目前主流可选值:1024、2048、3072、4096,最小好像是512位,但如果老大就要256位的,也是有解决办法的,参考本文最底部。在使用RSA加密的过程中,显然秘钥长度越长,加密的强度也就越强,同时程序计算的时间也会变长。秘钥长度增加一倍,密钥对生成的时间就增加16倍,公钥加密操作时长增加4倍,私钥解密操作时长增加8倍,所以秘钥长度视情况而定,不宜太大,否则效率低下。

1.2关于明文长度

一般来说,如果我们“定长定量自己可控可理解”的加密(比如下面加密代码),则可以视明文长度=秘钥bit长度值/8-11,比如我下面使用的秘钥长度是1024bit,那么她一次可加密的明文长度就是:1024/8-11=117byte,所以当明文较长时或不确定时,就需要进行分块加密,而每一块就是117byte。

1.3关于密文长度

分段加密后,每一块明文加密后的密文长度=密钥的长度,比如我下面使用的秘钥是 1024bit,那么在此条件下生成的每一块的密文长度1024bit/8=128Byte

所以在加密和解密的过程中都需要判断明文或密文的长度,然后分块进行。RSA加解密对内容的长度是有限制的,如果加密数据过大会抛出如下异常:

Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes

2、java实现RSA加解密以及密钥对的生成:

  1. package com.util;
  2. import java.io.ByteArrayOutputStream;
  3. import java.security.Key;
  4. import java.security.KeyFactory;
  5. import java.security.KeyPair;
  6. import java.security.KeyPairGenerator;
  7. import java.security.interfaces.RSAPrivateKey;
  8. import java.security.interfaces.RSAPublicKey;
  9. import java.security.spec.PKCS8EncodedKeySpec;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. import javax.crypto.Cipher;
  13. /**
  14. * SRA算法加解密工具
  15. * @author aigov
  16. */
  17. public class RSAEncryptUtil {
  18. /**加密算法RSA*/
  19. public static final String KEY_ALGORITHM = "RSA";
  20. /**获取公钥的key*/
  21. private static final String PUBLIC_KEY = "RSAPublicKey";
  22. /**获取私钥的key*/
  23. private static final String PRIVATE_KEY = "RSAPrivateKey";
  24. /**RSA最大加密明文大小*/
  25. private static final int MAX_ENCRYPT_BLOCK = 117;
  26. /**RSA最大解密密文大小*/
  27. private static final int MAX_DECRYPT_BLOCK = 128;
  28. /**
  29. * 生成密钥对(公钥和私钥)
  30. * @return base64格式的公私钥
  31. */
  32. public static Map<String, String> genKeyPair() {
  33. //map存放密钥对
  34. Map<String, String> keyMap = null ;
  35. try{
  36. //秘钥对生成器
  37. KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
  38. //初始化秘钥大小为1024位
  39. keyPairGen.initialize(1024);
  40. //生成密钥对
  41. KeyPair keyPair = keyPairGen.generateKeyPair();
  42. RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
  43. RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
  44. //公钥转base64编码
  45. byte[] pk = publicKey.getEncoded();
  46. String publicKeyB64 = Base64Utils.encode(pk);
  47. //私钥转base64编码
  48. byte[] privPk = privateKey.getEncoded();
  49. String privateKeyB64 = Base64Utils.encode(privPk);
  50. keyMap = new HashMap<String, String>(2);
  51. keyMap.put(PUBLIC_KEY, publicKeyB64);
  52. keyMap.put(PRIVATE_KEY, privateKeyB64);
  53. }catch(Exception e){
  54. e.getStackTrace();
  55. }
  56. return keyMap;
  57. }
  58. /**
  59. * 私钥解密
  60. * @param encryptedData 已加密数据
  61. * @param privateKey 私钥(BASE64编码)
  62. * @return
  63. * @throws Exception
  64. */
  65. public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
  66. //base64解密私钥
  67. byte[] keyBytes = Base64Utils.decode(privateKey);
  68. //PKCS8EncodedKeySpec类继承EncodedKeySpec类,以PKCS#8标准的编码格式来表示*私钥*。
  69. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
  70. //实例化秘钥工厂
  71. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  72. //生成私钥
  73. Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
  74. //实例化加密对象
  75. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
  76. cipher.init(Cipher.DECRYPT_MODE, privateK);
  77. int inputLen = encryptedData.length;
  78. ByteArrayOutputStream out = new ByteArrayOutputStream();
  79. int offSet = 0;
  80. byte[] cache;
  81. int i = 0;
  82. // 分段解密
  83. while (inputLen - offSet > 0) {
  84. if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
  85. cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
  86. } else {
  87. cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
  88. }
  89. out.write(cache, 0, cache.length);
  90. i++;
  91. offSet = i * MAX_DECRYPT_BLOCK;
  92. }
  93. byte[] decryptedData = out.toByteArray();
  94. out.close();
  95. return decryptedData;
  96. }
  97. /**
  98. * 私钥解密
  99. * @param data 已加密数据(BASE64字符)
  100. * @param PRIVATEKEY 私钥(BASE64编码)
  101. * @return
  102. */
  103. public static String decryptDataOnJava(String data, String PRIVATEKEY) {
  104. String temp = "";
  105. try {
  106. byte[] rs = Base64Utils.decode(data);
  107. temp = new String(RSAEncryptUtil.decryptByPrivateKey(rs, PRIVATEKEY));
  108. } catch (Exception e) {
  109. e.printStackTrace();
  110. }
  111. return temp;
  112. }
  113. /**
  114. * 公钥加密--一般前端做加密
  115. * @param data 明文
  116. * @param publicKey 公钥(BASE64编码)
  117. * @return
  118. * @throws Exception
  119. */
  120. /**
  121. public static byte[] encryptByPublicKey(byte[] data, String publicKey)throws Exception {
  122. byte[] keyBytes = Base64Utils.decode(publicKey);
  123. //X509EncodedKeySpec类继承EncodedKeySpec类,以X.509标准的编码格式来表示*公钥*。
  124. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
  125. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  126. Key publicK = keyFactory.generatePublic(x509KeySpec);
  127. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
  128. cipher.init(Cipher.ENCRYPT_MODE, publicK);
  129. int inputLen = data.length;
  130. ByteArrayOutputStream out = new ByteArrayOutputStream();
  131. int offSet = 0;
  132. byte[] cache;
  133. int i = 0;
  134. // 对数据分段加密
  135. while (inputLen - offSet > 0) {
  136. if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
  137. cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
  138. } else {
  139. cache = cipher.doFinal(data, offSet, inputLen - offSet);
  140. }
  141. out.write(cache, 0, cache.length);
  142. i++;
  143. offSet = i * MAX_ENCRYPT_BLOCK;
  144. }
  145. byte[] encryptedData = out.toByteArray();
  146. out.close();
  147. return encryptedData;
  148. }
  149. **/
  150. }

3、controller调用

  1. //登录
  2. public void toLogin() {
  3. //缓存秘钥
  4. Map<String, String> KeyPair = RSAEncryptUtil.genKeyPair();
  5. setAttr("publicKeys", KeyPair.get("RSAPublicKey"));
  6. setSessionAttr("privateKeys", KeyPair.get("RSAPrivateKey"));
  7. render("");
  8. }
  9. //私钥解密解密
  10. String privateKey = getSessionAttr("privateKeys");
  11. String loginPwd = RSAEncryptUtil.decryptDataOnJava(loginPwd, privateKey);
  12. //拿解密后的密码做其它业务判断:验证登录信息、、、、

4、前端加密

引入

  1. //加密
  2. var encrypt = new JSEncrypt();
  3. var publicKey = '${publicKeys}';// 公钥
  4. var pwdYw = $('#user_password').val(); // 密码原文
  5. encrypt.setPublicKey(publicKey);
  6. var pwdMw = encrypt.encrypt(pwdYw);// 密码密文

5、补充

密钥越长越安全,但是老大就要用256位的密钥的话,参考下面:

  1. .....
  2. .....
  3. KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
  4. final int KEY_SIZE = 256;
  5. keyPairGen.initialize(KEY_SIZE, new SecureRandom());
  6. KeyPair keyPair = keyPairGen.generateKeyPair();
  7. .....
  8. .....