数据加密的几种方式

数据加密
  • 对称加密算法(AES,DES,3DES)

    对称加密指的是加密和解密时,使用的是同一秘钥,其中最优秀的对称加密算法是AES。
    不过对称加密也有不好的地方,即对秘钥的保存问题。

  • 非对称加密算法(RSA,ECC)
    非对称加密

    非对称加密指的是加密和解密的双方使用不同的秘钥,通过非对称加密算法生成一对秘钥
    一个公钥,一个私钥,公钥可对外暴露,私钥只对自己可见,任何想向我发信息的人,通过公钥对数据加密
    我收到后,使用私钥对密文进行解密。最优秀的非对称加密算法是RSA。

  • 单向Hash算法

    单向Hash算法也叫散列算法,单向指的是经过加密后的hash值并不能反向推算出明文,即不可逆
    目前对数据库的敏感信息的加密大多数使用这种方式

    • MD5/SHA-1加密
      通过对明文密码字段进行MD5加密,将计算出的16字节hash值保存在数据库中,但这种方式并不安全,因为单向hash算法有两个特点,
      1,从同一个密码进行单向哈希,得到的总是唯一确定的hash值
      2,计算速度快。随着技术进步,一秒钟能够完成数十亿次单向哈希计算
      攻击者可以将所有密码的常见组合进行单向哈希,使用hash值作为key,密码作为value,组成查询表
      拿到你的数据库中的hash值在表中查询,就能找出对应的明文密码了。

    • SHA2(SHA256,SHA512)
      也是和MD5差不多,只不过生成的hash字节长度更大而已,还是可以通过查询表,或者更高效的彩虹表
      找到对应的明文密码

    • SHA2+盐
      这是一种有效能避免查询表或彩虹表攻击的方式之一,盐指的是随机的字符串,在每一个用户的明文后
      加上随机生成的盐,再对该字符串进行哈希计算,将计算后的hash值保存在数据库中,
      这种方式也叫加盐,随机字符串盐也有要求

      1. 使用CSPRNG算法生成盐,而不是普通的随机数算法。
        CSPRNG是加密安全的,这意味着用它产生的随机数更加随机,且不可预测。
      2. 盐不能太短
        如果盐很短,那意味着密码+盐组成的字符串的长度和取值空间都有限。
        黑客完全可以为密码+盐的所有组合建立彩虹表。
      3. 盐不能重复使用
        如果所有用户的密码都使用同一个盐进行加密。那么不管盐有多复杂、多大的长度,
        黑客都可以很容易的使用这个固定盐重新建立彩虹表,破解你的所有用户的密码。

      但这种方式现在也不适用了,因为为了验证用户的明文是否正确,通常会在表中设置一个salt
      的字段用于存放盐,这就会导致一个问题,如果数据库被脱库,意味着明文+盐的hash值,和盐
      一起被泄露,攻击者还是可以以此建立彩虹表,依靠如今显卡恐怖的并行计算能力,依然能找出明文

    • Password Hash(PBKDF2、BCRYPT、SCRYPT,Argon2)
      也叫密码哈希,专业加密密码的hash算法,这类加密算法有个共同的特点,算法中都有个因子,
      用于指明计算密码摘要所需要的资源和时间,也就是计算强度。计算强度越大,
      攻击者建立彩虹表越困难,以至于不可继续。
      其中最优秀的当属Argon2算法,在2015年7月,Argon2算法被NIST认定为最好的密码hash算法。

PasswordEncoder接口的使用

PasswordEncoder是Spring Security提供的密码加密方式的接口定义,其实现类如下图:

  • 使用BCryptPasswordEncoder加密
    • 导入Spring Security依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
    • 注册BCryptPasswordEncoder组件

      @Bean
      public PasswordEncoder passwordEncoder(){
          return new BCryptPasswordEncoder();
      }
    • 加密解密

      //对password字段加密保存在数据库中
      user.setPassword(passwordEncoder.encode(user.getPassword()));
      userServiceImpl.save(user);
      
      //通过前端传递过来的id获取数据库中对象
      User userDataBase = userServiceImpl.getById(user.getId());
      boolean matches = passwordEncoder.matches(user.getPassword(),userDataBase.getPassword());
      /*
      	PasswordEncoder接口的encode方法将明文通过哈希计算成密文,
      	其中使用了加盐和慢哈希的方式增加攻击者破解难度
      	PasswordEncoder接口的matches方法,第一个参数是用户传递的密码明文,第二个参数是数据库
      	存储的hash值,两者进行比对,如果比对成功说明用户密码正确。
      */
  • 使用Argon2PasswordEncoder加密
    • 除了Security,导入Argon2所需的依赖

      <dependency>
          <groupId>org.bouncycastle</groupId>
          <artifactId>bcprov-jdk15on</artifactId>
          <version>1.64</version>
      </dependency>
    • 注册Argon2PasswordEncoder组件

      @Bean
      public PasswordEncoder passwordEncoder(){
          return new Argon2PasswordEncoder();
      }
    • 加密解密同BCryptPasswordEncoder