当前位置:   article > 正文

分布式环境下使用RSA算法实现登录密码的加密传输_rsaservice

rsaservice

目录

效果

RSA介绍

实现思路

服务端实现

​RSAService:RSA算法的相关操作

RedisService:公钥和密钥的存储和获取

获取公钥的接口

客户端使用公钥加密

服务端使用私钥解密


效果

RSA介绍

        RSA是一种非对称加密算法。
        非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方;甲方再用自己私钥对加密后的信息进行解密。甲方想要回复乙方时正好相反,使用乙方的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密。

        在我们Web环境中,甲方即为我们的服务端,乙方即为客户端。服务端需要生成一个公钥和一个私钥,私钥保管在服务端不能泄露,公钥可以公开给所有客户端。

在这里插入图片描述

实现思路

  1. 服务端在启动时,生成一个公钥和一个私钥。由于是分布式环境,需要将公钥和私钥存储于Redis,并设置过期时间。公钥和私钥的被动 “续期”,参见代码实现。
  2. 为了公开向客户端公开公钥,服务端需要提供一个获取公钥的接口,该接口从Redis中获取公钥返回给客户端。
  3. 登录页面(客户端)通过公钥将登录密码加密,通过http请求服务端
  4. 服务端拿到加密后的密码(密文),从Redis中获取私钥解密,得到原始密码。将原始密码以注册时相同的加密规则加密,与数据库中存储的密码比对,即可判断密码是否正确

服务端实现

下面的所有实现代码均在:https://github.com/passerbyYSQ/forum

​RSAService:RSA算法的相关操作

  1. /**
  2. * @author passerbyYSQ
  3. * @create 2022-09-25 16:42
  4. */
  5. public interface RSAService {
  6. KeyPair generateKeyPair();
  7. String getPublicKey();
  8. String getPrivateKey();
  9. String decryptByPrivateKey(String encryptedText);
  10. String encryptByPublicKey(String rawText);
  11. }
  1. /**
  2. * @author passerbyYSQ
  3. * @create 2022-09-25 15:36
  4. */
  5. @Service
  6. @Slf4j
  7. public class RSAServiceImpl implements RSAService, ApplicationListener<ContextRefreshedEvent> {
  8. @Resource
  9. private RedisService redisService;
  10. @Override
  11. public void onApplicationEvent(ContextRefreshedEvent event) {
  12. // 对于web应用会出现父子容器,这样就会触发两次
  13. if (event.getApplicationContext().getParent() == null) {
  14. redisService.saveRSAKeyPair();
  15. log.info("成功将RSA的KeyPair缓存至Redis");
  16. }
  17. }
  18. @Override
  19. public KeyPair generateKeyPair() {
  20. try {
  21. KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
  22. generator.initialize(1024, new SecureRandom());
  23. return generator.generateKeyPair();
  24. } catch (NoSuchAlgorithmException e) {
  25. e.printStackTrace();
  26. return null;
  27. }
  28. }
  29. public String getPublicKey() {
  30. KeyPair keyPair = redisService.getRSAKeyPair();
  31. byte[] publicBytes = keyPair.getPublic().getEncoded();
  32. byte[] base64Bytes = Base64.getEncoder().encode(publicBytes);
  33. return new String(base64Bytes);
  34. }
  35. public String getPrivateKey() {
  36. KeyPair keyPair = redisService.getRSAKeyPair();
  37. byte[] privateBytes = keyPair.getPrivate().getEncoded();
  38. byte[] base64Bytes = Base64.getEncoder().encode(privateBytes);
  39. return new String(base64Bytes);
  40. }
  41. public String decryptByPrivateKey(String encryptedText) {
  42. if (ObjectUtils.isEmpty(encryptedText)) {
  43. return null;
  44. }
  45. try {
  46. KeyPair keyPair = redisService.getRSAKeyPair();
  47. byte[] decodedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
  48. Cipher cipher = Cipher.getInstance("RSA");
  49. cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
  50. byte[] decryptedBytes = cipher.doFinal(decodedBytes);
  51. return new String(decryptedBytes);
  52. } catch (Exception e) {
  53. e.printStackTrace();
  54. return null;
  55. }
  56. }
  57. public String encryptByPublicKey(String rawText) {
  58. if (ObjectUtils.isEmpty(rawText)) {
  59. return null;
  60. }
  61. try {
  62. KeyPair keyPair = redisService.getRSAKeyPair();
  63. Cipher cipher = Cipher.getInstance("RSA");
  64. cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
  65. byte[] encryptedBytes = cipher.doFinal(rawText.getBytes());
  66. byte[] base64Bytes = Base64.getEncoder().encode(encryptedBytes);
  67. return new String(base64Bytes);
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. return null;
  71. }
  72. }
  73. }

RedisService:公钥和密钥的存储和获取

  1. /**
  2. * @author passerbyYSQ
  3. * @create 2021-06-02 13:18
  4. */
  5. public interface RedisService {
  6. void saveRSAKeyPair();
  7. KeyPair getRSAKeyPair();
  8. }
  1. /**
  2. * @author passerbyYSQ
  3. * @create 2021-06-02 13:21
  4. */
  5. @Slf4j
  6. @Service
  7. public class RedisServiceImpl implements RedisService {
  8. @Resource
  9. private RedisTemplate<String, Object> redisTemplate;
  10. @Resource
  11. private RSAService rsaService;
  12. @Override
  13. public void saveRSAKeyPair() {
  14. String key = String.format(Constant.REDIS_KEY_KEY_PAIR, "RSA");
  15. KeyPair keyPair = rsaService.generateKeyPair();
  16. if (!ObjectUtils.isEmpty(keyPair)) {
  17. redisTemplate.opsForValue().setIfAbsent(key, keyPair, Constant.DURATION_KEY_PAIR);
  18. }
  19. }
  20. @Override
  21. public KeyPair getRSAKeyPair() {
  22. String key = String.format(Constant.REDIS_KEY_KEY_PAIR, "RSA");
  23. KeyPair keyPair = (KeyPair) redisTemplate.opsForValue().get(key);
  24. if (ObjectUtils.isEmpty(keyPair)) {
  25. saveRSAKeyPair(); // KeyPair过期,则重新续费
  26. }
  27. // 重新获取KeyPair,因为如果并发续费,成功续上的有可能不是本机生成的KeyPair
  28. return (KeyPair) redisTemplate.opsForValue().get(key);
  29. }
  30. }

获取公钥的接口

        Controller中新增一个方法,调用RedisService中的RsaService的getPublicKey()方法获取公钥。此处略,在项目中我是直接通过模板引擎,将公钥植入到登录页面的。如下图,代码仅供参考,思路是一样的。

客户端使用公钥加密

        前端使用的加密类库是 jsencrypt(https://github.com/travist/jsencrypt),js文件下载:https://github.com/travist/jsencrypt/tree/master/bin

        使用比较简单,参见上面截图

服务端使用私钥解密

        服务端调用 RsaService的decryptByPrivateKey()方法解密即可获得明文。下面代码截图,仅供参考,思路是一样的。

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

闽ICP备14008679号