비밀번호를 평문으로 저장하지 않고 해시함수를 사용하여 암호화 후 저장하기 위해 bcrypt 알고리즘을 사용한다.

라이브러리 사용을 위해 메이븐 의존성을 추가하면 사용가능한 BCrypt 클래스가 추가된다.

<dependency>
    <groupId>org.mindrot</groupId>
    <artifactId>jbcrypt</artifactId>
    <version>0.4</version>
</dependency>

BCrypt 해시함수에 같은 plain text를 인자로 넣어도 자동 생성된(BCrypt.gensalt()) salt 값이 다르기 때문에 암호화된 해시값은 서로 다르다. (length만 같다.)

@Test
public void BCrypt해시함수값은_평문이_같아도_매번_다르다(){
    String plainText = "HelloWorld"; // 암호화할 평문

    String hashText = BCrypt.hashpw(plainText, BCrypt.gensalt());
    log.debug("hashText : {}", hashText);

    String hashText2 = BCrypt.hashpw(plainText, BCrypt.gensalt());
    log.debug("hashText2 : {}", hashText2);

    Assert.assertNotEquals(hashText, hashText2); // 암호화된 값은 다름
    Assert.assertEquals(hashText.length(), hashText2.length());
}
hashText : $2a$10$4tByHHhN5rP.rvz6gSFifu6M/tvhyRDfV1xKwfMbhpScexzMY/xxK
hashText2 : $2a$10$1xwmyqmslmblRNh6DdvnpuD7MoRd7E7ntOYE5aK36Y.g45g.6DtTe

같은 평문을 여러번 해시해보면 서로 다른 값들이 나오지만 해시값들을 평문과 비교해보면 모두 ‘같음’으로 체크된다. 사용자 로그인을 위한 비밀번호 체크시에도 사용자가 입력한 평문과 DB의 해시값을 비교한다.

@Test
public void BCrypt해시함수값이_달라도_평문과_비교가_가능하다(){
    String plainText = "HelloWorld";
    String hashText = BCrypt.hashpw(plainText, BCrypt.gensalt());
    String hashText2 = BCrypt.hashpw(plainText, BCrypt.gensalt());

    // hashText와 hashText2는 서로 다르지만 평문과 비교하면 모두 같다.
    Assert.assertTrue(BCrypt.checkpw(plainText, hashText));
    Assert.assertTrue(BCrypt.checkpw(plainText, hashText2));
}