Spring Security를 사용해서 Password를 저장하고 인증 할 때 사용 되는 BCryptPasswordEncoder는 Password를 hash값으로 만들 때 salt와 key strech 방법을 사용한다.
비밀번호를 저장하던 인증을 하던 결국 hashpw()메서드를 통해 hashing를 한다.
private static String hashpw(byte passwordb[], String salt, boolean for_check) {
BCrypt B;
String real_salt;
byte saltb[], hashed[];
char minor = (char) 0;
int rounds, off;
StringBuilder rs = new StringBuilder();
// getSalt() or hash값에 있는 salt는 버전+key strech 값 + salt가 합쳐진 문자열이다.
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");
}
if (off == 4 && saltLength < 29) {
throw new IllegalArgumentException("Invalid salt");
}
// salt에 있는 key strech 값을 가져와 몇번 hashing할지 정한다.
rounds = Integer.parseInt(salt.substring(off, off + 2));
// salt에 있는 버전, key strech 값을 제외한 진짜 salt값을 뽑는다.
real_salt = salt.substring(off + 3, off + 25);
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
if (minor >= 'a') {
passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
}
B = new BCrypt();
// 진짜 salt값과 key streach를 이용해서 hash값을 구함.
hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0, for_check);
rs.append("$2");
if (minor >= 'a') {
rs.append(minor);
}
rs.append("$");
if (rounds < 10) {
rs.append("0");
}
rs.append(rounds);
rs.append("$");
// salt 값 + 해쉬 값을 base64로 인코딩해서 합친다.
encode_base64(saltb, saltb.length, rs);
encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
return rs.toString();
}
위 코드에서 중요한건 주석을 단 부분으로
한 줄평 : hash된 값은 복호화를 할 수 없어서 encoding이든 match든 같은 해시 함수를 사용 할 수 밖에 없다.