BCrypt로 인코딩을 해보면 같은 평문이라도 해싱 결과값이 매번 다른데,
어떻게 두 문자열의 값이 같다고 판단하는 걸까?
BCrypt Encode (암호화 과정)
BCrypt는 무작위로 생성되는 솔트라는 데이터를 해시 과정에서 비밀번호에 추가하여 해시 결과가 매번 달라지도록 만든다.
@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
}
String salt = getSalt();
return BCrypt.hashpw(rawPassword.toString(), salt);
}
getSalt 메서드를 사용하여 salt 값을 만들어낸다. 그리고 해당 salt 값을 사용하여 해시를 수행한다.
mathces 동작 방식
✔️ matches
평문의 비밀번호와 암호화된 비밀번호가 서로 일치하는지 확인하는 matches() 메서드 동작 방식 알아보기.
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
// ...
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
public static boolean checkpw(String plaintext, String hashed) {
byte[] passwordb = plaintext.getBytes(StandardCharsets.UTF_8);
return equalsNoEarlyReturn(hashed, hashpwforcheck(passwordb, hashed));
}
static boolean equalsNoEarlyReturn(String a, String b) {
return MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8), b.getBytes(StandardCharsets.UTF_8));
}
일치 여부를 최종적으로 리턴하는 equalsNoEarlyReturn까지의 과정에 있어서, 암호화할때 사용했던 hashpw 메서드를 여기서도 사용하는것을 찾아볼 수 있다.
private static String hashpwforcheck(byte[] passwordb, String salt) {
return hashpw(passwordb, salt, true);
}
✔️ hashpw() 메소드
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();
// 생략...
// 솔트 값 추출
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();
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("$");
encode_base64(saltb, saltb.length, rs);
encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
return rs.toString();
}
동작 방식
솔트 값 추출 : 암호화된 문자열에서 실제 솔트 값을 추출하고 이를 디코딩한다.
해싱 프로세스 : 실제 솔트값을 사용해 일치 여부를 확인할 평문 비밀번호의 해싱을 수행한다.
결과 해시 생성 : 적절한 접두사 및 라운드 그리고 해시 결과를 포함여 최종 해시 문자열을 구성한다.
요약
저장된 비밀번호에서 실제 솔트를 추출 후, 입력한 비밀번호에 대해 솔트를 사용하여 해싱 프로세스를 거친다. 최종적인 해시 결과를 비교하여 일치 여부를 확인하는 것이다.
'Programming > Java,Back-end' 카테고리의 다른 글
[Java] 익명 클래스와 람다(Lambda) 정리 (0) | 2024.10.29 |
---|---|
[Gradle] runtimeOnly, implementation, compileOnly, api 차이 (0) | 2024.10.26 |
[Java] 메인 메소드 존재 이유, 매개변수 String[] args에 대하여 (0) | 2024.10.26 |
ObjectMapper 싱글톤 빈으로 사용해도 될까? (1) | 2024.09.14 |
DAO와 Repository는 다른 개념일까?(차이를 구분할때의 장점) (2) | 2024.05.01 |