采用MD5或SHA-2家族的不可逆哈希函数混淆密码,由于它们被设计用来快速的计算数据的摘要值,通常是不可靠的。随着计算机性能的提高和GPU并行运算优化等手段可以越来越快的计算,降低了暴力破解的难度。
BCrypt是一种专门的密码散列函数,在1999年首次发布,各种语言都已有相应实现。它通过引入一个工作因子(work factor)以对抗摩尔定律,随着计算能力的提高,也可以相应的加大这个因子提高暴破成本。而且由于计算时间较慢和难以让GPU有效的计算,可以使安全级别大大提高。因此被称为 A Future-Adaptable Password Scheme

BCrypt 格式

加密字符串一般类似于$2a$10$7QDYxuYJBCEKu1q8IMHYg.3lq6OTiA5seEjtnYGOccsC0MkLtvJrS,可以分为四个部分

$<id>$<cost>$<salt><digest>
  • <id> 表示哈希算法和格式, 一到两个字符
  • <cost> 04 ~ 31, 工作因子,要重复迭代2**cost次,两个字符
  • <salt> 128位表示的盐,使用特殊的Base64编码为22个字符
  • <digest> 最终的结果值,184位编码为31个字符

3(美元符号) + 1 or 2 + 2 + 22 + 34 = 59 or 60

所以总长比较固定,数据库可以采用CHAR(60),且无需其他字段存储盐

使用示例

示例中用到的BCryptSpring Security提供

public class PasswordHashTest {

    private final String plainPasswd = "1234@abcd";

    @Test
    public void normalTest() {
        String hash1 = BCrypt.hashpw(plainPasswd, BCrypt.gensalt());
        Assert.assertTrue(BCrypt.checkpw(plainPasswd, hash1));
    }

    @Test
    public void strongerTest() {
        // 通常这个测试会跑比较久,计算能力过剩可改为BCrypt.gensalt(31)
        String hash2 = BCrypt.hashpw(plainPasswd, BCrypt.gensalt(14));
        Assert.assertTrue(BCrypt.checkpw(plainPasswd, hash2));
    }
}

See Also