MANSEN 发表于 2019-5-10 11:30

SpringSecurity加密和匹配过程分析

SpringSecurity加密和匹配过程分析(原创同步发表于CSDN)
### SpringSecurity加密和解密过程

###### demo

```java
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class BCryptPasswordEncoderUtils {
private static BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//加密,密码
public static String encodePassword(String password){
return bCryptPasswordEncoder.encode(password);
}

public static void main(String[] args) {
String str = "123456";
String password = encodePassword(str);
System.out.println(password);
//进行密码对比第一个参数为未加密密码,第二个参数为加密后数据库中的密码
boolean b = bCryptPasswordEncoder.matches("123456", "$2a$10$wdEIrlCoJDLT/burZ0ARg.WHyVve9VlxAVZFPWzT7n1RqVUJhDi/S");
System.out.println(b);
}

}
```

###### 加密详解

```java
public static String encodePassword(String password){
    return bCryptPasswordEncoder.encode(password);
}
```

调用encode方法进行加密

```java
public String encode(CharSequence rawPassword) {
String salt;
if (strength > 0) {
if (random != null) {
salt = BCrypt.gensalt(version.getVersion(), strength, random);
} else {
salt = BCrypt.gensalt(version.getVersion(), strength);
}
} else {
salt = BCrypt.gensalt(version.getVersion());
}
return BCrypt.hashpw(rawPassword.toString(), salt);
}
```

salt:盐

strength:静态常量

random:SecureRandom加密的强随机数

BCrypt实现了OpenBSD-style的Blowfish密码的使用(Blowfish算法是一个64位分组及可变密钥长度的对称密钥分组密码算法).

第一次使用需要哈希加密密码

调用hashpw()方法(参考一)传入一个随机salt BCrypt.hashpw(rawPassword.toString(), salt)

gensalt()方法接收一个可选参数,决定hash计算的复杂度.

###### 密码对比

调用matches进行密码对比

检查明文密码是否与已存在的密码匹配

```java
public boolean matches(CharSequence rawPassword, String encodedPassword) {
    //验证密码是否为空
if (encodedPassword == null || encodedPassword.length() == 0) {
logger.warn("Empty encoded password");
return false;
}
    //验证密码是否是BCrypt类型
if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
logger.warn("Encoded password does not look like BCrypt");
return false;
}
    //调用checkpw判断
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
```

在调用checkpw()方法前先hash

```java
public static boolean checkpw(String plaintext, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
}
```

```java
public static String hashpw(String password, String salt) {
byte passwordb[];

try {
passwordb = password.getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new AssertionError("UTF-8 is not supported");
}

return hashpw(passwordb, salt);
}
```

总结:从将已hash的密码作为盐,获取真盐(初次加密的盐)加密明文密码(参考一),然后与加密密码进行对比

参考一:

```
public static String hashpw(byte passwordb[], String salt) {
BCrypt B;
String real_salt;
byte saltb[], hashed[];
char minor = (char) 0;
int rounds, off;
StringBuilder rs = new StringBuilder();

if (salt == null) {
throw new IllegalArgumentException("salt cannot be null");
}

int saltLength = salt.length();

if (saltLength < 28) {
throw new IllegalArgumentException("Invalid salt");
}

if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
throw new IllegalArgumentException ("Invalid salt version");
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;
}

// Extract number of rounds
if (salt.charAt(off + 2) > '$')
throw new IllegalArgumentException ("Missing salt rounds");
rounds = Integer.parseInt(salt.substring(off, off + 2));

real_salt = salt.substring(off + 3, off + 25);
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);

if (minor >= 'a') // add null terminator
passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);

B = new BCrypt();
hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 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();
}
```

骑狗的猴子 发表于 2019-5-10 12:25

写的不错,学习了

夏天因果 发表于 2019-5-28 11:16

写的很好。学到了
页: [1]
查看完整版本: SpringSecurity加密和匹配过程分析