密码加密解密之路

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博客

java 复制代码
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;

/**
 * @Description: 加密解密
 * @Version 1.0
 */
public class SecretUtil {

    
    
    /**
     * 用uuid加密
     *
     * @param UUID
     * @param salt     盐 从nacos配置中取值
     * @param password 明文密码
     * @return
     */
    public static String encryptUUID(String UUID, String salt, String password) {
        if (!StringUtils.hasText(UUID) || !StringUtils.hasText(salt) || !StringUtils.hasText(password)) {
            return null;
        }
        //先把uuid和salt再进行一次加密
        String concat = UUID.concat(salt);
        byte[] bytes = base64Encrypt(concat);
        //再对明文密码进行加密
        return encrypt(password, bytes);
    }

    /**
     * UUID解密
     *
     * @param UUID
     * @param salt     盐 从nacos配置中取值
     * @param password 密文密码
     * @return
     */
    public static String decryptUUID(String UUID, String salt, String password) {
        if (!StringUtils.hasText(UUID) || !StringUtils.hasText(salt) || !StringUtils.hasText(password)) {
            return null;
        }
        //先把uuid和salt再进行一次加密
        String concat = UUID.concat(salt);
        byte[] bytes = base64Encrypt(concat);
        //再对明文密码进行加密
        return decrypt(password, bytes);
    }

    /**
     * base64加密
     *
     * @param content 待加密内容
     * @return byte[]
     */
    public static byte[] base64Encrypt(final String content) {
        return java.util.Base64.getEncoder().encode(content.getBytes());
    }

    /**
     * base64解密
     *
     * @param encoderContent 已加密内容
     * @return byte[]
     */
    public static byte[] base64Decrypt(final byte[] encoderContent) {
        return java.util.Base64.getDecoder().decode(encoderContent);
    }

    /**
     * 加密
     *
     * @param data  待加密内容
     * @param bytes 盐的base64
     * @return
     */
    public static String encrypt(String data, byte[] bytes) {
        byte[] dataByte = data.getBytes(StandardCharsets.UTF_8);
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(bytes);
            //这个128要特别注意  对长度有要求的
            keyGenerator.init(128, secureRandom);
            SecretKey secretKey = keyGenerator.generateKey();
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] encrypted = cipher.doFinal(dataByte);
            // 加密
            return java.util.Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 解密
     *
     * @param data  待解密内容
     * @param bytes 盐的base64
     * @return
     */
    public static String decrypt(String data, byte[] bytes) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(bytes);
            //这个128要特别注意  对长度有要求的
            keyGenerator.init(128, secureRandom);
            SecretKey secretKey = keyGenerator.generateKey();
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey.getEncoded(), "AES"));
            //采用base64算法进行转码,避免出现中文乱码
            byte[] encryptBytes = Base64.decodeBase64(data);
            byte[] decryptBytes = cipher.doFinal(encryptBytes);
            return new String(decryptBytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}

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中说的需求,不适合这种算法,因为我们使用对方数据库的时候肯定要获取这个密码进行连接

java 复制代码
    public static String irreversibleEncrypt(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        //加密  每次调用加密方法 返回的结果都不同
        return passwordEncoder.encode(password);
    }

    /**
     * 对比
     * 不可逆算法 不能解密 只能对比
     *
     * @param oldPassword     未加密密码 如123456
     * @param requestPassword 加密后的密码
     * @return
     */
    public static boolean irreversibleMatch(String oldPassword, String requestPassword) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        //对比
        return passwordEncoder.matches(oldPassword, requestPassword);
    }

6.不对称算法

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

7.keystore

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

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

相关推荐
前端不太难1 天前
没有文档模型,HarmonyOS PC 应用会发生什么?
华为·状态模式·harmonyos
GatiArt雷1 天前
Libvio.link网站反爬机制深度剖析与合规爬虫策略研究
状态模式
Go_Zezhou1 天前
render快速部署网站和常见问题解决
运维·服务器·开发语言·python·github·状态模式
共享家95271 天前
搭建 AI 聊天机器人:”我的人生我做主“
前端·javascript·css·python·pycharm·html·状态模式
We1ky2 天前
从零到一:我的javascript记忆翻转卡牌游戏诞生记
状态模式
Elieal2 天前
Spring MVC 全局异常处理实战
spring·mvc·状态模式
Elieal2 天前
统一 JSON 格式,JacksonObjectMapper 定制 Spring Boot JSON 转换规则
spring boot·json·状态模式
前端不太难2 天前
HarmonyOS PC 应用,先做文档模型
华为·状态模式·harmonyos
前端不太难2 天前
HarmonyOS 走向 PC,应用模型正在重构
重构·状态模式·harmonyos
进击的小头3 天前
行为型模式:状态模式——嵌入式状态管理的优雅解决方案
c语言·状态模式