SpringSecurity加密和解密过程
demo
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);
}
}
加密详解
public static String encodePassword(String password){
return bCryptPasswordEncoder.encode(password);
}
调用encode方法进行加密
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进行密码对比
检查明文密码是否与已存在的密码匹配
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
public static boolean checkpw(String plaintext, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
}
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();
}