当前位置:   article > 正文

RSA非对称加密(附工具类Util)_rsautil

rsautil


        之前写过一篇关于DES对称加密的帖子,感兴趣的小伙伴可以去看看:DES对称加密算法

今天主要聊聊什么是非对称加密,以及它是如何实现的。

一、非对称加密是什么?

非对称加密(asymmetric cryptography),也称为公开密钥加密(Public-key cryptography),是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥。顾名思义,公钥可以任意对外发布;而私钥必须由用户自行严格秘密保管,绝不透过任何途径向任何人提供,也不会透露给要通信的另一方,即使他被信任。

非对称加密的重要性质:

1.加密的双向性

加密具有双向性,即公钥和私钥中的任一个均可用作加密,此时另一个则用作解密。

使用其中一个密钥把明文加密后所得的密文,只能用相对应的另一个密钥才能解密得到原本的明文,甚至连最初用来加密的密钥也不能用作解密,这是非对称加密最重要的性质或者说特点。

2.公钥无法推导出私钥 

必须确保使用公钥无法推导出私钥,至少妄想使用公钥推导私钥必须在计算上是不可行的,否则安全性将不复存在。

虽然两个密钥在数学上相关,但如果知道了公钥,并不能凭此计算出私钥;因此公钥可以公开,任意向外发布;而私钥不公开,绝不通过任何途径向任何人提供。

在对称密码中,由于加密和解密的密钥是相同的,因此必须向接收者配送密钥。用于解密的密钥必须被配送给接收者,这一问题称为密钥配送问题。如果使用非对称加密,则无需向接收者配送用于解密的密钥,这样就解决了密钥配送的问题。

非对称加密中,密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密,接收者用解密密钥对密文进行解密。需理解公钥密码,清楚地分加密密钥和解密密钥是非常重要的。加密密钥是发送者加密时使用的,而解密密钥则是接收者解密时使用的。

3.加密密钥和解密密钥的区别

  • 发送者只需要加密密钥
  • 接收者只需要解密密钥
  • 解密密钥不可以被窃听者获取
  • 加密密钥被窃听者获取也没关系

也就是说,解密密钥从一开始就是由接收者自己保管的,因此只要将加密密钥发给发送者就可以解决密钥配送问题了,而根本不需要配送解密密钥。

非对称加密中,加密密钥一般是公开的。真是由于加密密钥可以任意公开,因此该密钥被称为公钥(pulickey)。相对地解密密钥是绝对不能公开的,这个密钥只能由你自己来使用,因此称为私钥(privatekey)。私钥不可以被别人知道,也不可以将它发送给别人。

公钥和私钥是"一一对应的",一对公钥和私钥统称为密钥对(keypair)。由公钥进行加密的密文,必须使用与该公钥配对的私钥才能解密。密钥对中的两个密钥之间具有非常密切的的关系(数学上的关系)。因此公钥和私钥不能分别单独生成。

PS:公钥密码的使用者需要生成一个包括公钥和私钥的密钥对,其中公钥会被发送给别人,而私钥则仅供自己使用。

二、非对称加密通信流程

举例:假设士兵A要给军官B发一条信息,A是发送者,B是接收者,黑客C想窃听A和B之间的通讯内容。

1.军官B生成一个包含公钥和私钥的密钥对(私钥由B自行妥善保管

2.军官B将自己的公钥发送给士兵A,表示B请A用这个公钥对消息进行加密并发送给他。但此时公钥被黑客C截获(没关系,无伤大雅)。

3.士兵A用军官B的公钥对消息进行加密(加密后的消息只有B的私钥才能够解密。虽然A拥有B的公钥,但用B的公钥是无法对密文进行解密的)

4.士兵A将密文发送给军官B(密文也被黑客C截获,不过也没关系,C虽然拥有B的公钥,但是B的公钥是无法进行解密的)

5.军官B用自己的私钥对密文进行解密,获得最终的明文,详细可参考下图: 

A和B之间传输的信息有两个,一个是B的公钥,一个是B公钥加密的密文,由于B的私钥没有出现在通信内容中,因此C无法对密文进行解密。

三、RSA非对称加密算法

RSA是一种非对称加密算法,它的名字由三位开发者。即RonRivest、AdiShamir和LeonardAdleman 的姓氏的首字母组成的(Rivest-Shamir-Leonard)。

1.RSA加密

RSA的加密工程可以用下来公式来表达,如下:

也就是说,RSA的密文是对代表明文的数字的E次方求modN的结果。换句话说,就是将明文自己做E次乘法,然后将其结果除以N求余数,这个余数就是密文。 

RSA的加密是求明文的E次方modN,因此只要知道E和N这两个数,任何人都可以完成加密的运算。所以说E和N是RSA加密的密钥。也就是说E和N的组合就是公钥。

有一个很容易引起误解的地方需要大家注意一一E和N这两个数并不是密钥对(公钥和私钥的密钥对)。E和N两个数才组成了一个公钥,因此我们一般会写成 “公钥是(E,N)” 或者 “公钥是{E, N}" 这样的形式,将E和N用括号括起来。

2.RSA解密

RSA的解密和加密一样简单,可以用下面的公式来表达:

也就是说,对表示密文的数字的D次方求modN就可以得到明文。换句话说,将密文自己做D次乘法,在对其结果除以N求余数,就可以得到明文。

这里所使用的数字N和加密时使用的数字N是相同的。数D和数N组合起来就是RSA的解密密钥,因此D和N的组合就是私钥。只有知道D和N两个数的人才能够完成解密的运算。

大家应该已经注意到,在RSA中,加密和解密的形式是相同的。加密是求 "E次方的mod N”,而解密则是求 "D次方的modN”,这真是太美妙了。

当然,D也并不是随便什么数都可以的,作为解密密钥的D,和数字E有着相当紧密的联系。否则,用E加密的结果可以用D来解密这样的机制是无法实现的。

PS:顺便说一句,D是解密〈Decryption)的首字母,N是数字(Number)的首字母

四、RSA工具类Util

RsaTestUtils.java
  1. package com.st.microservice.billcenter.infrastructure.util;
  2. import org.apache.commons.codec.binary.Base64;
  3. import javax.crypto.BadPaddingException;
  4. import javax.crypto.Cipher;
  5. import javax.crypto.IllegalBlockSizeException;
  6. import javax.crypto.NoSuchPaddingException;
  7. import java.nio.charset.StandardCharsets;
  8. import java.security.*;
  9. import java.security.interfaces.RSAPrivateKey;
  10. import java.security.interfaces.RSAPublicKey;
  11. import java.security.spec.InvalidKeySpecException;
  12. import java.security.spec.PKCS8EncodedKeySpec;
  13. import java.security.spec.X509EncodedKeySpec;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. /**
  17. * @description: RSA加密工具类
  18. * @author: admin
  19. */
  20. public class RsaTestUtils {
  21. public static void main(String[] args) {
  22. //生成公钥和私钥
  23. Map<Integer, String> keyMap = genKeyPair();
  24. //加密字符串
  25. String content = "我是原文啊!132456789";
  26. System.out.println("加密前内容:" + content);
  27. System.out.println("随机生成的公钥为:" + keyMap.get(0));
  28. System.out.println("随机生成的私钥为:" + keyMap.get(1));
  29. String messageEn = encrypt(content, keyMap.get(0));
  30. System.out.println("加密后的内容为:" + messageEn);
  31. String messageDe = decrypt(messageEn, keyMap.get(1));
  32. System.out.println("还原后的内容为:" + messageDe);
  33. }
  34. /**
  35. * 随机生成密钥对
  36. */
  37. public static Map<Integer, String> genKeyPair() {
  38. // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
  39. KeyPairGenerator keyPairGen = null;
  40. try {
  41. keyPairGen = KeyPairGenerator.getInstance("RSA");
  42. } catch (NoSuchAlgorithmException e) {
  43. e.printStackTrace();
  44. }
  45. // 初始化密钥对生成器,密钥大小为96-1024位
  46. assert keyPairGen != null;
  47. keyPairGen.initialize(1024, new SecureRandom());
  48. // 生成一个密钥对,保存在keyPair中
  49. KeyPair keyPair = keyPairGen.generateKeyPair();
  50. // 得到私钥
  51. RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
  52. // 得到公钥
  53. RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
  54. String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
  55. // 得到私钥字符串
  56. String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
  57. // 将公钥和私钥保存到Map
  58. Map<Integer, String> keyMap = new HashMap<>();
  59. // 0表示公钥
  60. keyMap.put(0, publicKeyString);
  61. // 1表示私钥
  62. keyMap.put(1, privateKeyString);
  63. return keyMap;
  64. }
  65. /**
  66. * RSA公钥加密
  67. *
  68. * @param str 加密字符串
  69. * @param publicKey 公钥
  70. * @return 密文
  71. */
  72. public static String encrypt(String str, String publicKey) {
  73. // base64编码的公钥
  74. byte[] decoded = Base64.decodeBase64(publicKey);
  75. RSAPublicKey pubKey;
  76. String outStr = null;
  77. try {
  78. pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
  79. Cipher cipher = Cipher.getInstance("RSA");
  80. cipher.init(Cipher.ENCRYPT_MODE, pubKey);
  81. outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
  82. } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException |
  83. NoSuchPaddingException | NoSuchAlgorithmException e) {
  84. e.printStackTrace();
  85. }
  86. // RSA加密
  87. return outStr;
  88. }
  89. /**
  90. * RSA私钥解密
  91. *
  92. * @param str 加密字符串
  93. * @param privateKey 私钥
  94. * @return 明文
  95. */
  96. public static String decrypt(String str, String privateKey) {
  97. //64位解码加密后的字符串
  98. byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
  99. //base64编码的私钥
  100. byte[] decoded = Base64.decodeBase64(privateKey);
  101. RSAPrivateKey priKey;
  102. //RSA解密
  103. Cipher cipher;
  104. String outStr = null;
  105. try {
  106. priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
  107. cipher = Cipher.getInstance("RSA");
  108. cipher.init(Cipher.DECRYPT_MODE, priKey);
  109. outStr = new String(cipher.doFinal(inputByte));
  110. } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException |
  111. IllegalBlockSizeException | InvalidKeyException e) {
  112. e.printStackTrace();
  113. }
  114. return outStr;
  115. }
  116. }

实例结果:

简单的应用场景: 先生成一对公私钥匙对,前端拿公钥加密,后端拿私钥解密。

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/698807
推荐阅读
相关标签
  

闽ICP备14008679号