mosquitto鉴权插件mosquitto-auth-plug的加密算法

mosquitto-auth-plug

PBKDF2加密


PBKDF2其实就是选择一种哈希算法,把密码加盐后经过多次迭代计算进行加密。当然肯定是不可逆的,可以防止碰撞破解,迭代次数越多越安全。

mosquitto-auth-plug使用了PBKDF2加密,编译后得到的np可执行程序可以进行加密:(下面假设密码为admin)

./np -p admin

得到加密密码:
PBKDF2$sha256$901$qiCmZmc2ZKEJkEaL$7Nw6lpv9G0IoEgxcTfEbx4ajtMQD3shC

返回的密码使用$符号分隔,最前为PBKDF2标识,默认使用sha256哈希算法,进行了901次迭代加密,然后是使用openssl生成随机数经过BASE64的字符串也就是盐,最后是真正PBKDF2生成的Key。

把这个密码存储到数据库以后,当用户使用用户名和密码登录上来时,auth-plug会根据用户名查询数据库得到密码,然后根据$分割标识的定义来进行加密密码,最后把加密后的密码跟数据库的密码进行对比就知道用户的密码是否正确。

使用golang计算auth-plug的密码


golang.org/x/crypto/pbkdf2 这个就是go的PBKDF2加密算法,golang.org被墙上不去,不过没关系,在github上有crypto的镜像:https://github.com/golang/crypto

PBKDF2调用方法:

func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte

通过查看auto-plug的np.c源码文件,可以知道是怎么生成密码的,其实就是先生成盐,调用openssl的RAND_bytes函数生成默认12字节随机数然后进行base64生成盐。
iter默认为901次,keyLen的长度为24,h哈希算法为sha256,调用Key方法后将返回的[]byte数组经过base64.StdEncoding.EncodeToString得到加密Key,最终按照上面写的规则使用$符号拼接得到最终的密码。

加密速度和密码长度优化


测试了一下使用默认iter = 901的golang版本加密,感觉速度太慢了,按这速度一台服务器想高并发根本不可能,反正一般系统的安全性并不要这么高,那么可以调节减少iter次数,其实就算iter设为零,PBKDF2也执行了两次sha256,加上16字节长度的salt,比一般网站的MD5安全多了,先凑合着用吧。
其实我想测试一下np.c的加密速度比golang的会快多少,如果差距大的话可以使用cgo写个替代,暂时还没空搞。

十几分钟后,还是忍不住修改了np.c程序简单测试了一下,同样iter=901,c版本比golang几乎快了一倍,汗!

不过当iter比较小比如iter=9时,两者差距不大。甚至当iter=0时,go版本更快一点,应该是c版本没有优化每次调用都重新分配内存,而go胜在有GC。
对于我的应用来说,因为采用较小的iter所以没必要用cgo优化了。

golang自带的crypto/rand太慢,使用了github.com/NebulousLabs/fastrand会快很多,生成12字节的随机串快一倍,如果生成的字节数大的话会更快,甚至快10倍以上。

另外最终用$拼接的密码长度也太长了,如果数据库用的是Redis的话太浪费内存,前面的3段可以大大缩短,甚至都不用,然后修改mosquitto-auth-plug的源码来配合优化减少长度。

如果不怕慢而是希望提高安全性,那么把iter设的更大值即可。
当然既然有源码,那么使用什么加密方法自己都可以随便改,如果要求高甚至可以改为安全性更高的bcrypt。
鱼和熊掌不可兼得,越安全的哈希加密肯定是速度越慢,因为暴力破解者也需要更多的时间来碰撞破解,安全性就是提高破解者的成本让其得不偿失。

猜你喜欢

转载自blog.csdn.net/gamereborn/article/details/80211352