Bcrypt加密技术

Bcrypt采用随机盐的方式,进行密码混淆。

基本使用

导入相关依赖:

导入相关依赖。

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>de.svenkubiak</groupId>
<artifactId>jBCrypt</artifactId>
<version>0.4.1</version>
</dependency>
</dependencies>

测试demo一:

1
2
3
4
5
6
7
8
9
10
11
12
private static void bcryptEncrypt() {

String passwd="lingwu";
String salt = BCrypt.gensalt();
System.out.println(salt);

String hashpw = BCrypt.hashpw(passwd,salt);
System.out.println(hashpw);

boolean checkpw = BCrypt.checkpw(passwd,hashpw);
System.out.println(checkpw);
}

生成结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[*]-------第1次
$2a$10$opX7XwCM4i8xP9eoOpUGsu
$2a$10$opX7XwCM4i8xP9eoOpUGsuQASW6n14n7j8jj1J5VJDSHPqb4GSbYO
true
[*]-------第2次
$2a$10$FHOFbQAkzsxC6fbgHVJlvO
$2a$10$FHOFbQAkzsxC6fbgHVJlvO17lW1lJZxW/0llcQjqbdTKkqoeD2fai
true
[*]-------第3次
$2a$10$oou339lXDWzTTq5ubIhxIe
$2a$10$oou339lXDWzTTq5ubIhxIecFPSPUaSDbhyV0BuHPIzoBOup8oASxS
true
[*]-------第4次
$2a$10$fiEfevC8yqXlURzxfVTNr.
$2a$10$fiEfevC8yqXlURzxfVTNr.XT7B.9KM6U4dmqRnGHi/T3Pw5/4ZPfS
true
[*]-------第5次
$2a$10$HE9PJBZ1/eC8YgsWhc.6o.
$2a$10$HE9PJBZ1/eC8YgsWhc.6o.E.Y6R6/qVZ0SvrBwNIy9lkXCPDk.SFO
true

hash格式说明:

$2a:申明Bcrypt算法

$10:轮循10次,默认为10,在区间4与31之间。

$1-22位,表示盐,最后31位表示密文。

原理部分:

疑惑一

同一密码,每次生成的hash都不一样,那么它是如何进行校验的?

打开源码,可以看到,从hash中取的saltsalt跟password进行hash;得到的结果传入的的hash进行比对,完成校验。

疑惑二

通过增加轮循次数来增加破暴破时间,和md5或sha-1多次效果是否等效?

因为bcrypt源自Blowfish块密码.进行多次迭代的目的是使密码处理变慢,尤其是对攻击者而言变慢,因为它会阻止大量的密码猜测.但是攻击者可能会使用普通系统不使用的硬件,例如programmable GPU,大大提高了适用于这种硬件的计算能力. Blowfish和bcrypt使用基于RAM的查找表(在处理过程中修改的表);这样的表对于通用CPU来说很容易处理,但是在GPU上却很麻烦.因此,bcrypt在某种程度上阻碍了攻击者使用GPU的处理能力。

疑惑三

增加轮循次数防暴力破解效果有多大?

直接看测试效果比较明显。轮循次数为10和20的时间花费情况:

测试demo二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class bcryptStudy {

public static void main(String[] args) {
int GENSALT_DEFAULT_LOG2_ROUNDS=0;

bcryptTime(GENSALT_DEFAULT_LOG2_ROUNDS=10);
bcryptTime(GENSALT_DEFAULT_LOG2_ROUNDS=20);
}

private static void bcryptTime(int GENSALT_DEFAULT_LOG2_ROUNDS) {
long start1 = System.currentTimeMillis();
//int GENSALT_DEFAULT_LOG2_ROUNDS=10;
bcryptEncryptN(GENSALT_DEFAULT_LOG2_ROUNDS);
long end1 = System.currentTimeMillis();
long time1=end1-start1;
System.out.println("round"+GENSALT_DEFAULT_LOG2_ROUNDS+" take time ---->"+time1+"ms");

}

private static void bcryptEncryptN(int GENSALT_DEFAULT_LOG2_ROUNDS) {
for (int i = 1; i <11; i++) {
//System.out.println("[*]-------第"+i+"次");
bcryptEncrypt(GENSALT_DEFAULT_LOG2_ROUNDS);
}
}
private static void bcryptEncrypt(int GENSALT_DEFAULT_LOG2_ROUNDS) {

String passwd="lingwu";
String salt = BCrypt.gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
//System.out.println(salt);

String hashpw = BCrypt.hashpw(passwd,salt);
System.out.println(hashpw);

boolean checkpw = BCrypt.checkpw(passwd,hashpw);
//System.out.println(checkpw);
}
}

生成结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$2a$10$SOi7S1vfmSaBVtIWZ/g7ku3aKuYYRDrnnJijaEPvIh3CojRb9/lVm
$2a$10$FltrFw0X/vPHZDRcT8GkkeXH.K11YhqLXgVbeyxXT4MTqnAIVC7hy
$2a$10$H.BVO4e1lNCo.0tsrEYUMu026/WNPz0zjs0IiDUw9WfEcKdrAqJj.
$2a$10$sQVmHDVy7NDJnPLnTt.mluS0JvzLwPoKOyuK4553mcetvQxKYwE0m
$2a$10$aYLoeXHGcVm8whGxxYR54uW4y9OLhj7Xsmz8qhP5WU1g5d0Db619O
$2a$10$I4yKJUrCi1ShLhXcS7CKheXHYbaehq.TFHYicEeY1QUDYSSTwnBVu
$2a$10$vVacHUEqVYWtJACgHCpRF.FO7slxOoAF/uXodvNhFyH2KzgEL1s2m
$2a$10$HlQ1K41HpkOGEG4/bVElUu.Tb/GhxaF..1e0e8mVXSZKxPHzhLwp.
$2a$10$HnyPPJFf6dFA3uvcWtEJs.dZDo56BW7.zh0uhXE1tbjaIdrrY6e4G
$2a$10$ePh6mibWzDA7mfVbzQ86u.rxfKxeTVHBvwX6X4KuOHalN.5a1PJP.
round10 take time ---->2145ms
$2a$20$ApGKCzS7dZnZ.PIl.qH.euy742iX0cWvTl4BgGkKdkkQaQ0po6TQ2
$2a$20$AWXsqxQxMgeIMbRIH1UaAeFhWAgvosIdcUBNl6Nj2xMwo4wGn/q..
$2a$20$LuRzJ7YURj1hgSTpDZYdxuye7q2Jx9xXAdRuZyVRyVcNyj5UYD.s.
$2a$20$EbZbgBK.lk4.s7u1yvSVFemLroAr4ZNcYO1xHjWB8XSJT5/gjT7d2
$2a$20$e/EAOn5s8nScMGePsMW5Gu5im2vdQ12dtWCzRrWTboKO9OC2f0v0i
$2a$20$TbZrn7MQPYMcEwlubn6aUe8hyGdi8J1sq5ryyWjAP/JqMtxoSH4QW
$2a$20$HYu2WsCRpwZBSE3wr6lQ2uJjR/hzepTi115o9aQjTrdlTaL2uD6Ny
$2a$20$ANxL2PwQ5507glLBwWwSG.07UJ1FUUpwkArEenwA.uKMt8zoE5Hcu
$2a$20$nJKznxmkzUOSTWBKlVl1X.PC2M8MRmvhQplYiuIVgW5Z19T0CaWwe
$2a$20$gPJQPXwLkhiosAqTypu57.mSBWUSJEuvH9pGz5XSeVBJYCnM9L.eK
round20 take time ---->2188643ms

发现轮循次数为10和20的时间花费相差大约1000倍,生成一个轮循次数为10的大约需要花费200ms,很慢(和md5比起来),生成一个轮循次数为20的大约需要花费200s,这。。。

注:轮循20次是真的慢。。。加密的速度也太慢了,在生成hash的过程中都想中断进程了,别说攻击者暴力跑了。。。

优势与劣势:

优:

  • 动态盐,每次随机22位字符;
  • 不用本地保存与盐相关的字段。
  • Bcrypt的加密时间非常慢,暴力破解需要枚举遍历所有可能结果时,增加了破解的难度。

劣势:

  • 效率低。

总结:

1.bcrypt做为一个跨平台的文件加密工具。在现有的spring中运用,足以看出他的地位。安全与性能需要找个平稀点,越慢的算法越安全。通过salt和const这两个值来减缓加密过程,ta的加密时间(百ms级)远远超过md5(大概1ms左右)。对于计算机来说,Bcrypt 的计算速度很慢,但是对于用户来说,这个过程不算慢。

2.服务端在用户校验时增加token或验证码,做好防止批量提交的准备,也可预防恶意用户进行压力负担,影响服务端性能。

3.轮循次数默认的10就挺好,没必要再加了。。。

4.推荐使用!!!