Bootstrap

SpringSecurity加密和匹配过程分析

SpringSecurity加密和解密过程

demo
impo
rt 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();  
}
;