• 密码加密解密之路


    1.背景

    数据采集,客户需要把他们那边的数据库连接信息存到我们系统里,那我们系统就要尽可能的保证这部分数据安全,不被盗。

    2.我的思路

    1.需要加密的地方有两处,一个是新增的时候前端传给后端的时候,一个是存到数据库

    2.与前端交互时的加密方式肯定和存到数据库的加密方式不同(为了安全)

    3.接收前端传值,这块好说,跟前端约定一个加密方式,约定一个盐

    4.存到数据库怎么加密解密

    麻烦得点就在于存到数据库这里,

    我是针对每一条数据都会生成一个uuid,然后nacos配置文件中还有一个盐salt

    然后我把uuid和salt用特殊符号拼接之后,再进行一次加密形成最终盐 finalSalt

    用finalSalt对密码进行加密,把密文密码存到数据库

    如果用户对密码进行修改,那就重新生成一个uuid,再重新加密,存储密文

    之所以设计这么麻烦,就是增加解密得困难度,想要明文,只有同时拿到数据库数据,nacos配置,以及我的java代码,才能解密,尽可能保障密码得安全性

    5.密码可为空

    这一点我是纠结了一会,因为在产品设计上密码是可为空得,如果我不把密码返回给前端,那前端保存得时候,如果用户没修改密码,前端上传给我得也会是空,那这个时候我就不知道到底是用户把密码改为空了,还是说他根本没有动密码这个文本框

    所以我就把密文密码返回了,让他保存到时候再按照前后端约定加密传输给我,

    我接收到之后先按照前后端约定得解密,再去和数据里边密文对比,

    如果一致,说明没改,

    如果不一致,说明修改了,并且解密出来的是明文密码,我会对明文密码按照4中说的再进行加密

    但是把密文密码传给前端始终是个不太安全得事情

    我后来想到一点,前端是可以识别到,用户是否动过密码这个文本框得

    那我就不把密码返回给前端了

    可以让前端给我一个标识,true,就代表动过文本框,

    如果现在上传得密码为空,且动过文本框,那我就直接把密码字段保存为空即可。

    如果现在上传得密码为空,但是false,没动过文本框,那我就不更新密码这个字段

    如果现在上传密码不为空,那就不管true还是false了,密码肯定修改了,就先按照前后端解密再按照数据库加密即可

    3.加解密工具类

    这是针对数据库那一部分

    博客上是前后端约定的那一部分

    【精选】java前后端加密解密crypto-js_java crypto-CSDN博客

    1. import org.apache.commons.codec.binary.Base64;
    2. import org.springframework.util.StringUtils;
    3. import javax.crypto.*;
    4. import javax.crypto.spec.SecretKeySpec;
    5. import java.nio.charset.StandardCharsets;
    6. import java.security.*;
    7. /**
    8. * @Description: 加密解密
    9. * @Version 1.0
    10. */
    11. public class SecretUtil {
    12. /**
    13. * 用uuid加密
    14. *
    15. * @param UUID
    16. * @param salt 盐 从nacos配置中取值
    17. * @param password 明文密码
    18. * @return
    19. */
    20. public static String encryptUUID(String UUID, String salt, String password) {
    21. if (!StringUtils.hasText(UUID) || !StringUtils.hasText(salt) || !StringUtils.hasText(password)) {
    22. return null;
    23. }
    24. //先把uuid和salt再进行一次加密
    25. String concat = UUID.concat(salt);
    26. byte[] bytes = base64Encrypt(concat);
    27. //再对明文密码进行加密
    28. return encrypt(password, bytes);
    29. }
    30. /**
    31. * UUID解密
    32. *
    33. * @param UUID
    34. * @param salt 盐 从nacos配置中取值
    35. * @param password 密文密码
    36. * @return
    37. */
    38. public static String decryptUUID(String UUID, String salt, String password) {
    39. if (!StringUtils.hasText(UUID) || !StringUtils.hasText(salt) || !StringUtils.hasText(password)) {
    40. return null;
    41. }
    42. //先把uuid和salt再进行一次加密
    43. String concat = UUID.concat(salt);
    44. byte[] bytes = base64Encrypt(concat);
    45. //再对明文密码进行加密
    46. return decrypt(password, bytes);
    47. }
    48. /**
    49. * base64加密
    50. *
    51. * @param content 待加密内容
    52. * @return byte[]
    53. */
    54. public static byte[] base64Encrypt(final String content) {
    55. return java.util.Base64.getEncoder().encode(content.getBytes());
    56. }
    57. /**
    58. * base64解密
    59. *
    60. * @param encoderContent 已加密内容
    61. * @return byte[]
    62. */
    63. public static byte[] base64Decrypt(final byte[] encoderContent) {
    64. return java.util.Base64.getDecoder().decode(encoderContent);
    65. }
    66. /**
    67. * 加密
    68. *
    69. * @param data 待加密内容
    70. * @param bytes 盐的base64
    71. * @return
    72. */
    73. public static String encrypt(String data, byte[] bytes) {
    74. byte[] dataByte = data.getBytes(StandardCharsets.UTF_8);
    75. try {
    76. KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    77. SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    78. secureRandom.setSeed(bytes);
    79. //这个128要特别注意 对长度有要求的
    80. keyGenerator.init(128, secureRandom);
    81. SecretKey secretKey = keyGenerator.generateKey();
    82. Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    83. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    84. byte[] encrypted = cipher.doFinal(dataByte);
    85. // 加密
    86. return java.util.Base64.getEncoder().encodeToString(encrypted);
    87. } catch (Exception e) {
    88. throw new RuntimeException(e);
    89. }
    90. }
    91. /**
    92. * 解密
    93. *
    94. * @param data 待解密内容
    95. * @param bytes 盐的base64
    96. * @return
    97. */
    98. public static String decrypt(String data, byte[] bytes) {
    99. try {
    100. KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    101. SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    102. secureRandom.setSeed(bytes);
    103. //这个128要特别注意 对长度有要求的
    104. keyGenerator.init(128, secureRandom);
    105. SecretKey secretKey = keyGenerator.generateKey();
    106. Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    107. cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey.getEncoded(), "AES"));
    108. //采用base64算法进行转码,避免出现中文乱码
    109. byte[] encryptBytes = Base64.decodeBase64(data);
    110. byte[] decryptBytes = cipher.doFinal(encryptBytes);
    111. return new String(decryptBytes);
    112. } catch (Exception e) {
    113. throw new RuntimeException(e);
    114. }
    115. }
    116. }

    4.代码混淆

    这块可以不看,是中间调研,走过的一个弯路

    因为java的class文件可以反编译,所以我想让编译之后的代码变成01001那种无法识别的语言

    但是发现java代码混淆并不能达成这个效果

    但是毕竟走了一天弯路,还是记录下吧

    4.1先看效果

    4.2再上代码

    从这下载吧

    https://download.csdn.net/download/qq_35653822/88554123

    里边有个字典,可以实现把包名,类名,变量名混淆为0o00o这种方式

    5.不可逆算法

    目前接触到的,存登录密码是不可逆算法,用的是security自带的加密

    security提供了match方法,可以对比输入的密码和数据库中是否一致,返回true或者false,但是不提供解密方法

    除非管理员可以一键重置用户的密码,把用户密码重置成初始化默认密码

    存数据库里边的长这样,谁也看不出来存的是啥

     这种方法固然安全,但是对于我们1中说的需求,不适合这种算法,因为我们使用对方数据库的时候肯定要获取这个密码进行连接

    1. public static String irreversibleEncrypt(String password) {
    2. BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    3. //加密 每次调用加密方法 返回的结果都不同
    4. return passwordEncoder.encode(password);
    5. }
    6. /**
    7. * 对比
    8. * 不可逆算法 不能解密 只能对比
    9. *
    10. * @param oldPassword 未加密密码 如123456
    11. * @param requestPassword 加密后的密码
    12. * @return
    13. */
    14. public static boolean irreversibleMatch(String oldPassword, String requestPassword) {
    15. BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    16. //对比
    17. return passwordEncoder.matches(oldPassword, requestPassword);
    18. }

    6.不对称算法

    这个什么时候使用就看具体场景吧

    7.keystore

    Java密码术 - 存储密钥( Storing keys)_学习Java密码学|WIKI教程

    这个同事调研过,结论是不适合我们,我没仔细研究

  • 相关阅读:
    C. Number of Ways Codeforces Round 266 (Div. 2)
    文件上传漏洞靶场前十关
    猴子吃桃问题--c语言
    springboot整合cxf-rt-transports-http-jetty发布jax-rs服务
    在 RHEL or CentOS 7、8 中更改主机名的 4 种方法
    学习C++的周期计划书
    计算机网络
    全球名校AI课程库(8)| Berkeley伯克利 · 全栈深度学习训练营课程『Full Stack Deep Learnin』
    mysql内置功能(走开发的看)
    每日刷题记录 (一)
  • 原文地址:https://blog.csdn.net/qq_35653822/article/details/134501835