当前位置:   article > 正文

密码加密方式_可逆加密

可逆加密

密码加密方式

密码加密方式是指通过使用特定的算法将原始密码转换成一个(在理想情况下)无法还原的不可读形式,以确保即使数据被泄露,密码也不会轻易地被恶意用户获取。在计算机安全中,这个过程通常称为散列(Hashing)。

密码在存储时通常会使用散列函数处理。散列函数能够接收任意长度的输入(比如一个密码),并产生一个固定长度的输出(即散列值或哈希值)。这个过程应该是单向的,也就是说,从散列值获得原始输入应该是不可能的或极其困难的。

1 可逆加密算法

加密后, 密文可以反向解密得明文原文;

1). 对称加密

在这里插入图片描述

指加密和解密使用相同密钥的加密算法。

优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。
缺点: 没有非对称加密安全。

常见的对称加密算法:DES、3DES、DESX、Blowfish、RC4、RC5、RC6和AES

说白了加密和解密都使用同一个秘钥处理;

2). 非对称加密

在这里插入图片描述

指加密和解密使用不同密钥的加密算法,也称为公私钥加密。假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密。

加密和解密:

  • 私钥加密,持有私钥或公钥才可以解密
  • 公钥加密,持有私钥才可解密

优点: 非对称加密与对称加密相比,其安全性更好;
缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密

2 不可逆加密算法

一旦加密就不能反向解密得到密码原文 。通常用于密码数据加密。

常见的不可逆加密算法有: MD5 、SHA、HMAC

1).MD5

MD5是比较常见的加密算法,广泛的应用于软件开发中的密码加密,通过MD5生成的密文,是无法解密得到明文密码的。但是现在在大数据背景下,很多的网站通过大数据可以将简单的MD5加密的密码破解。

网址: https://www.cmd5.com/

可以在用户注册时,限制用户输入密码的长度及复杂度,从而增加破解难度。

2). Bcrypt

用户表的密码通常使用 MD5 等不可逆算法加密后存储,为防止彩虹表破解,会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的 salt(盐值)加密。 特定字符串是程序代码中固定的,salt 是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。

BCrypt 算法将 salt 随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理 salt 问题。

在SecurityConfig配置类配置密码加密匹配器:

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
	return new BCryptPasswordEncoder();
}
  • 1
  • 2
  • 3
  • 4

加密密码:

@Autoware
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Test
public void test01(){
  for (int i = 0; i < 5; i++) {
	System.out.println(bCryptPasswordEncoder.encode("123456"));
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

得到结果:

$2a$10$C6YynRFeJsSy7D/kg3d30OWnuwko7KQIEK5JrX0mWND.vuz2TqwpK
$2a$10$aSJfxH2oBtopFMbkMJ.PQ.sbSBXJH9g.9bv1mCyte/BtcU9VTs7lG
$2a$10$nVoB.eV5Uhc9FNUC36Pn0OosGh7aKlp7Sjfxaiml8NCSJ6PX1q6.m
$2a$10$2RM3mRNjz1LoZ5eeLdj.Hu15vlWIIj2zJC09vwTevBlIi5rjJStam
$2a$10$c2sZT/LtM1ExWfZjO0yIPeTGSqMSlX7oi.SvliMbeZpT9Y4qIBDue
  • 1
  • 2
  • 3
  • 4
  • 5

验证密码:

boolean matches = 
   bCryptPasswordEncoder.matches("123456", "$2a$10$c2sZT/LtM1ExWfZjO0yIPeTGSqMSlX7oi.SvliMbeZpT9Y4qIBDue");
System.out.println(matches);//返回值为true, 则代表验证通过; 反之, 验证不通过
  • 1
  • 2
  • 3

注意:此时重新启动security_demo测试工程,security底层会自动调用PasswordEncoder类型bean进行密码校验处理;

3 BcryptPasswordEncode原理介绍

SpringSecurity提供了实现PasswordEncoder接口的密码加密工具类:BcryptPasswordEncoder,该类基于Bcrypt强哈希算法来加密密码,更加安全;

Bcrypt强哈希方法每次加密相同的明文得到的密文结果都不一样,这样即使数据库泄露,黑客也很难破解密码;

1)Bcrypt加密

一般在注册用户时,Bcrypt使用SHA-256加密算法+随机盐值+秘钥(明文密码)进行加密处理,得到的密文存入数据库中;

    @Test
    public void testPwd(){
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String enPwd1 = passwordEncoder.encode("123456");
        System.out.println("enPwd1:"+enPwd1);
        String enPwd2 = passwordEncoder.encode("123456");
        System.out.println("enPwd2:"+enPwd2);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

相同的明文加密得到的密文各不相同:

enPwd1:$2a$10$yZJPn5tlBVSW3Udt926HcO8AiyzBKcLi4gKaJrVNG1Yw6LlmYCZhm
enPwd2:$2a$10$H4P8eWCGM.kpy8/.ayYfruoJbFMyi4lnIxjBYP2yB7d6yqiQS8NTi
  • 1
  • 2

进入encode方法:

public String encode(CharSequence rawPassword) {
    if (rawPassword == null) {
        throw new IllegalArgumentException("rawPassword cannot be null");
    } else {
        String salt = this.getSalt();
        return BCrypt.hashpw(rawPassword.toString(), salt);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

生成加密的代码

public static String hashpw(String password, String salt) {
        byte[] passwordb = password.getBytes(StandardCharsets.UTF_8);
        return hashpw(passwordb, salt);
    }

    public static String hashpw(byte[] passwordb, String salt) {
        char minor = 0;
        StringBuilder rs = new StringBuilder();
        if (salt == null) {
            throw new IllegalArgumentException("salt cannot be null");
        } else {
            int saltLength = salt.length();
            if (saltLength < 28) {
                throw new IllegalArgumentException("Invalid salt");
            } else if (salt.charAt(0) == '$' && salt.charAt(1) == '2') {
                byte off;
                if (salt.charAt(2) == '$') {
                    off = 3;
                } else {
                    minor = salt.charAt(2);
                    if (minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b' || salt.charAt(3) != '$') {
                        throw new IllegalArgumentException("Invalid salt revision");
                    }

                    off = 4;
                }

                if (salt.charAt(off + 2) > '$') {
                    throw new IllegalArgumentException("Missing salt rounds");
                } else if (off == 4 && saltLength < 29) {
                    throw new IllegalArgumentException("Invalid salt");
                } else {
                    int rounds = Integer.parseInt(salt.substring(off, off + 2));
                    String real_salt = salt.substring(off + 3, off + 25);
                    byte[] saltb = decode_base64(real_salt, 16);
                    if (minor >= 'a') {
                        passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
                    }

                    BCrypt B = new BCrypt();
                    byte[] hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 65536 : 0);
                    rs.append("$2");
                    if (minor >= 'a') {
                        rs.append(minor);
                    }

                    rs.append("$");
                    if (rounds < 10) {
                        rs.append("0");
                    }

                    rs.append(rounds);
                    rs.append("$");
                    encode_base64(saltb, saltb.length, rs);
                    encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
                    return rs.toString();
                }
            } else {
                throw new IllegalArgumentException("Invalid salt version");
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

在这里插入图片描述

我们发现明文密码加密时,Bcrypt底层生成一个随机盐值参与加密运算,同时也会将盐值和加密后的密文一并形成最终密文;

2)Bcrypt密码匹配

一般在用户认证登录时,业务方法首先根据用户信息获取对应的密文信息,然后将明文和密码经过matches方法获取匹配结果为true则表示密码一致;

Bcrypt底层是先根据密文获取生成的盐值,然后再将盐值与明文加密得到密文,这个密文如果与从数据库获取的密文一致,则说明输入的名称是正确的。

而且根据下面的分析,可以发现,解密的过程中会根据传递过来的明文和密文经过一系列计算获取real_salt真实的盐值————>真实的盐值salt盐值的某一部分。而具体多少位是有效的需要根据你的明文进行判断得到,然后根据真实的盐值再将明文加密后与salt组合后获取密文。最后与密文信息进行匹配,这样大大提高了安全性。

    @Test
    public void testMatcher() {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String enPwd="$2a$10$yZJPn5tlBVSW3Udt926HcO8AiyzBKcLi4gKaJrVNG1Yw6LlmYCZhm";
        boolean isSuccess = passwordEncoder.matches("123456", enPwd);
        System.out.println(isSuccess);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public boolean matches(CharSequence rawPassword, String encodedPassword) {
        if (rawPassword == null) {
            throw new IllegalArgumentException("rawPassword cannot be null");
        } else if (encodedPassword != null && encodedPassword.length() != 0) {
            if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
                this.logger.warn("Encoded password does not look like BCrypt");
                return false;
            } else {
                //这里拿到了
                return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
            }
        } else {
            this.logger.warn("Empty encoded password");
            return false;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

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

闽ICP备14008679号