RSA加密解密,签名验签简述 + 完美工具类RSAUtils

你的名字 2022-12-31 12:29 662阅读 0赞

原创博文,欢迎转载,转载时请务必附上博文链接,感谢您的尊重。

系列文章目录

RSA+AES数据传输的加密解密【篇】,项目实战(专题汇总):

  1. AES 加密解密简述 + 完美工具类 AESUtils
  2. RSA 加密解密,签名验签简述 + 完美工具类 RSAUtils
  3. RSA + AES 加密原理,一线大厂主流的加密手段
  4. RSA 与 AES 加密效率对比,用调研事实说明问题,用算法原理解决疑惑
  5. RSA + AES 混合加密策略,真实项目案例,一线大厂的主流HTTP加密交互方式(正在赶稿)

一、RSA简介

RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

20201229193613333.jpg

RSA是被研究得最广泛的公钥算法,从提出到现在已近三十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。该能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。

RSA算法属于非对称加密算法,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。简单的说是“公钥加密,私钥解密;私钥加密,公钥解密”。

二、参数解释

下面几行是生成 RSA 秘钥对的标准核心代码,我们通过它分析下参数对结果的影响。

  1. // keygen类
  2. KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
  3. // 随机种子
  4. keygen.initialize(2048, new SecureRandom());
  5. // 生成一个密钥对,保存在keyPair中
  6. KeyPair keys = keygen.genKeyPair();
  7. PublicKey publicKey = keys.getPublic();
  8. PrivateKey privateKey = keys.getPrivate();

1. 秘钥长度

RSA密钥是(公钥+模值)、(私钥+模值)分组分发的,单独给对方一个公钥或私钥是没有任何用处,所以我们说的“密钥”其实是它们两者中的其中一组。

而“密钥长度”一般只是指模值的位长度,目前主流可选值:1024、2048、3072、4096… 没有上限,多大都可以使用。但出于安全问题的考虑,低于1024bit的密钥不建议使用。

2. 秘钥种子

初始化keygen时:

  • 如果不指定seed(如上范例所示),而是直接 New 一个 SecureRandom 的随机对象,那生成的公私钥也是随机的;
  • 如果指定seed(如下引用所示),那么 secureRandom 结果是一样的,所以生成的公私钥也永远不会变。

    String seed = “$%^*%^()(ED47d784sde78”;
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.setSeed(seed.getBytes());
    keygen.initialize(2048, secureRandom);

种子 seed 干嘛用?答:非常有用,而且生产中建议使用。

做测试每次都随机无所谓,但是在生产中很必要。

设想我们 new SecureRandom() 随机生成一对RSA秘钥,公钥都已经发给调用方了,结果我的私钥弄丢了,怎么办?只能再生成一对,然后逐一通知调用方进行更换,很尴尬吧。但是,如果我们使用种子seed生成,然后安全的保存了种子,以后就再不怕把公钥、私钥弄丢了。

二、RSAUtils

工具类 RSAUtils,包含:

  1. 生成 RSA 密钥对,包含随机、固定的方法;
  2. RSA 加密与解密,普通方法;
  3. RSA 加密与解密,限制明文大小的方法;
  4. SIGN 签名与验签的方法;

代码:RSAUtils.java

  1. import java.nio.charset.StandardCharsets;
  2. import java.security.Key;
  3. import java.security.KeyFactory;
  4. import java.security.KeyPair;
  5. import java.security.KeyPairGenerator;
  6. import java.security.PrivateKey;
  7. import java.security.PublicKey;
  8. import java.security.SecureRandom;
  9. import java.security.Signature;
  10. import java.security.interfaces.RSAPrivateKey;
  11. import java.security.interfaces.RSAPublicKey;
  12. import java.security.spec.PKCS8EncodedKeySpec;
  13. import java.security.spec.X509EncodedKeySpec;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. import javax.crypto.Cipher;
  17. import com.alibaba.fastjson.JSON;
  18. import org.apache.commons.codec.binary.Base64;
  19. import org.apache.commons.lang3.ArrayUtils;
  20. import org.slf4j.Logger;
  21. import org.slf4j.LoggerFactory;
  22. /**
  23. * 功能:SHA256withRSA 工具类
  24. * 说明:
  25. * @author Mr.tjm
  26. * @date 2020-5-20 11:25
  27. */
  28. @SuppressWarnings("all")
  29. public class RSAUtils {
  30. private static final Logger logger = LoggerFactory.getLogger(RSAUtils.class);
  31. // MAX_DECRYPT_BLOCK应等于密钥长度/8(1byte=8bit),所以当密钥位数为2048时,最大解密长度应为256.
  32. // 128 对应 1024,256对应2048
  33. private static final int KEYSIZE = 2048;
  34. // RSA最大加密明文大小
  35. private static final int MAX_ENCRYPT_BLOCK = 117;
  36. // RSA最大解密密文大小
  37. private static final int MAX_DECRYPT_BLOCK = 128;
  38. // 不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名
  39. private static final String KEY_ALGORITHM = "RSA";
  40. private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
  41. // 默认种子
  42. public static final String DEFAULT_SEED = "$%^*%^()(ED47d784sde78";
  43. // 编码格式
  44. private static final String CODE_FORMATE_UTF8 = "UTF-8";
  45. // - - - - - - - - - - - - - - - - - - - - RSA 生成秘钥对 - - - - - - - - - - - - - - - - - - - - //
  46. /**
  47. * 生成密钥对:Base64 转码的字符串
  48. * @param key
  49. * @return
  50. */
  51. public static Map<String, String> initKeyBase64Str() throws Exception {
  52. Map<String, String> map = new HashMap<>(2);
  53. Map<String, Key> keyMap = initKey();
  54. PublicKey publicKey = (PublicKey) keyMap.get("PublicKey");
  55. PrivateKey privateKey = (PrivateKey) keyMap.get("PrivateKey");
  56. map.put("PublicKey", new String(Base64.encodeBase64(publicKey.getEncoded())));
  57. map.put("PrivateKey", new String(Base64.encodeBase64(privateKey.getEncoded())));
  58. logger.info("生成密钥 = " + JSON.toJSONString(map));
  59. return map;
  60. }
  61. /**
  62. * 生成默认密钥
  63. *
  64. * @return 密钥对象
  65. */
  66. public static Map<String, Key> initKey() throws Exception {
  67. return initKey(DEFAULT_SEED);
  68. }
  69. /**
  70. * 生成密钥对:若seed为null,那么结果是随机的;若seed不为null且固定,那么结果也是固定的;
  71. *
  72. * @param seed 种子
  73. * @return 密钥对象
  74. */
  75. public static Map<String, Key> initKey(String seed) throws Exception {
  76. KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
  77. // 如果指定seed,那么secureRandom结果是一样的,所以生成的公私钥也永远不会变
  78. SecureRandom secureRandom = new SecureRandom();
  79. secureRandom.setSeed(seed.getBytes());
  80. // Modulus size must range from 512 to 1024 and be a multiple of 64
  81. keygen.initialize(KEYSIZE, secureRandom);
  82. // 生成一个密钥对,保存在keyPair中
  83. KeyPair keys = keygen.genKeyPair();
  84. PublicKey publicKey = keys.getPublic();
  85. PrivateKey privateKey = keys.getPrivate();
  86. // 将公钥和私钥保存到Map
  87. Map<String, Key> map = new HashMap<>(2);
  88. map.put("PublicKey", publicKey);
  89. map.put("PrivateKey", privateKey);
  90. logger.info("生成密钥 = " + JSON.toJSONString(map));
  91. return map;
  92. }
  93. // - - - - - - - - - - - - - - - - - - - - RSA 加密、解密 - - - - - - - - - - - - - - - - - - - - //
  94. /**
  95. * 获取公钥 PublicKey 信息
  96. * @param 公钥
  97. * @return
  98. */
  99. public static PublicKey getPublicKey(String pubKeyStr) throws Exception{
  100. byte[] publicKeys = Base64.decodeBase64(pubKeyStr);
  101. X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeys);
  102. KeyFactory mykeyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  103. PublicKey publicKey = mykeyFactory.generatePublic(publicKeySpec);
  104. logger.info("传入的公钥为:【" + pubKeyStr + "】,转义后的公钥为:【" + publicKey + "】");
  105. return publicKey;
  106. }
  107. /**
  108. * 公钥加密,指定 RSA 方式的 PublicKey 对象
  109. *
  110. * @param str 加密字符串
  111. * @param publicKey 公钥
  112. * @return
  113. */
  114. public static String encrypt(String str, String publicKey) throws Exception{
  115. // base64编码的公钥
  116. byte[] decoded = Base64.decodeBase64(publicKey);
  117. RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
  118. // RSA加密
  119. Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  120. cipher.init(Cipher.ENCRYPT_MODE, pubKey);
  121. String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(CODE_FORMATE_UTF8)));
  122. return outStr;
  123. }
  124. /**
  125. * 公钥加密,任意 PublicKey 对象
  126. *
  127. * @param publicKey
  128. * @param encryptData
  129. * @param encode
  130. */
  131. public static String encrypt(PublicKey publicKey, String encryptData, String encode) throws Exception {
  132. if (publicKey == null) {
  133. throw new Exception("加密公钥为空,请设置。");
  134. }
  135. try {
  136. final Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  137. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  138. byte[] output = cipher.doFinal(encryptData.getBytes(encode));
  139. return Base64.encodeBase64String(output);
  140. } catch (Exception e) {
  141. logger.info("加密异常:"+e.getMessage());
  142. return null;
  143. }
  144. }
  145. /**
  146. * 私钥解密,指定 RSA 方式的 PrivateKey 对象
  147. *
  148. * @param str 加密字符串
  149. * @param privateKey 私钥
  150. * @return
  151. */
  152. public static String decrypt(String str, String privateKey) throws Exception{
  153. // 64位解码加密后的字符串
  154. byte[] inputByte = Base64.decodeBase64(str.getBytes(CODE_FORMATE_UTF8));
  155. // base64编码的私钥
  156. byte[] decoded = Base64.decodeBase64(privateKey);
  157. RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
  158. // RSA解密
  159. Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  160. cipher.init(Cipher.DECRYPT_MODE, priKey);
  161. String outStr = new String(cipher.doFinal(inputByte));
  162. return outStr;
  163. }
  164. /**
  165. * RSA 公钥加密,【限制长度】
  166. *
  167. * @param str 加密字符串
  168. * @param publicKey 公钥
  169. * @return 密文
  170. */
  171. public static String encryptByPublicKey(String str, String publicKey) throws Exception {
  172. // base64编码的公钥
  173. byte[] keyBytes = decryptBASE64(publicKey);
  174. RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(keyBytes));
  175. // RSA加密
  176. Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  177. cipher.init(Cipher.ENCRYPT_MODE, pubKey);
  178. byte[] data = str.getBytes("UTF-8");
  179. // 加密时超过117字节就报错。为此采用分段加密的办法来加密
  180. byte[] enBytes = null;
  181. for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
  182. // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码
  183. byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
  184. enBytes = ArrayUtils.addAll(enBytes, doFinal);
  185. }
  186. String outStr = encryptBASE64(enBytes);
  187. return outStr;
  188. }
  189. /**
  190. * RSA 私钥解密,【限制长度】
  191. *
  192. * @param encryStr 加密字符串
  193. * @param privateKey 私钥
  194. * @return 明文
  195. */
  196. public static String decryptByPrivateKey(String encryStr, String privateKey) throws Exception {
  197. // base64编码的私钥
  198. byte[] decoded = decryptBASE64(privateKey);
  199. RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
  200. // RSA解密
  201. Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  202. cipher.init(Cipher.DECRYPT_MODE, priKey);
  203. // 64位解码加密后的字符串
  204. byte[] data = decryptBASE64(encryStr);
  205. // 解密时超过128字节报错。为此采用分段解密的办法来解密
  206. StringBuilder sb = new StringBuilder();
  207. for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) {
  208. byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK));
  209. sb.append(new String(doFinal));
  210. }
  211. return sb.toString();
  212. }
  213. /**
  214. * BASE64Encoder 加密
  215. *
  216. * @param data 要加密的数据
  217. * @return 加密后的字符串
  218. */
  219. private static String encryptBASE64(byte[] data) {
  220. return new String(Base64.encodeBase64(data));
  221. }
  222. /**
  223. * BASE64Encoder 解密
  224. *
  225. * @param data 要解密的数据
  226. * @return 解密后的字节
  227. */
  228. private static byte[] decryptBASE64(String data) {
  229. return Base64.decodeBase64(data);
  230. }
  231. // - - - - - - - - - - - - - - - - - - - - SIGN 签名,验签 - - - - - - - - - - - - - - - - - - - - //
  232. /**
  233. * 加签:生成报文签名
  234. *
  235. * @param content 报文内容
  236. * @param privateKey 私钥
  237. * @param encode 编码
  238. * @return
  239. */
  240. public static String doSign(String content, String privateKey, String encode) {
  241. try {
  242. String unsign = Base64.encodeBase64String(content.getBytes(StandardCharsets.UTF_8));
  243. byte[] privateKeys = Base64.decodeBase64(privateKey.getBytes());
  244. PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeys);
  245. KeyFactory mykeyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  246. PrivateKey psbcPrivateKey = mykeyFactory.generatePrivate(privateKeySpec);
  247. Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
  248. signature.initSign(psbcPrivateKey);
  249. signature.update(unsign.getBytes(encode));
  250. byte[] signed = signature.sign();
  251. return Base64.encodeBase64String(signed);
  252. } catch (Exception e) {
  253. logger.error("生成报文签名出现异常");
  254. }
  255. return null;
  256. }
  257. /**
  258. * 验证:验证签名信息
  259. *
  260. * @param content 签名报文
  261. * @param signed 签名信息
  262. * @param publicKey 公钥
  263. * @param encode 编码格式
  264. * @return
  265. */
  266. public static boolean doCheck(String content, String signed, PublicKey publicKey, String encode) {
  267. try {
  268. // 解密之前先把content明文,进行base64转码
  269. String unsigned = Base64.encodeBase64String(content.getBytes(encode));
  270. Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
  271. signature.initVerify(publicKey);
  272. signature.update(unsigned.getBytes(encode));
  273. boolean bverify = signature.verify(Base64.decodeBase64(signed));
  274. return bverify;
  275. } catch (Exception e) {
  276. logger.error("报文验证签名出现异常");
  277. }
  278. return false;
  279. }
  280. }

三、秘钥生成网站

如果只是想随机生成一对秘钥用于测试,也可以使用现有网站,笔者推荐一个:https://www.bejson.com/enc/rsa/

《BEJSON —— RSA 公钥私钥,加密解密在线测试》:

2020122920462180.png


我是IT无知君,您的点赞、评论和关注,是我创作的动力源泉。
学无止境,气有浩然,让我们一起加油,天涯未远,江湖有缘再见!!

发表评论

表情:
评论列表 (有 0 条评论,662人围观)

还没有评论,来说两句吧...

相关阅读

    相关 加密解密 签名

    几个基本概念: 加密:发送方利用接收方的公钥对要发送的明文进行加密。 解密:接受方利用自己的私钥进行解密。 公钥和私钥配对的,用公钥加密的文件,只有对应的私钥才能解密。当