这是我参与「第五届青训营 」伴学笔记创作活动的第17天
前几期讲了hash加密和hash破解的一些知识, 这期讲一下项目中常用的加密工具BCrypt
BCrypt
粗略了解原理
了解BCrypt, 首先要了解Hash加密和加盐, 可以去看我之前写的文章初步了解密码加密(二) | 青训营笔记, 里面简单的介绍了一下hash加密和加盐
BCrypt是一种基于Blowfish对称密钥加密分组密码算法设计出来的hash散列函数, 是一种可生成随机盐值的单向Hash加密算法, Hash结果中包含了上一轮Hash生成的盐值. 每次加密后的密文结果都不一样
下面是使用BCrypt生成的一个加密结果, BCrypt的加密结果主要可以分为Prefix, Cost Salt和Hashed Text四部分
$2a$04$QxuJ7Zt7l42ljBxmYKOfbemaZG8dO.qeAvwF3qxd6QZWsD2uJLcAW
其中,
$2a$ 是第一部分prefix, 代表BCrypt的版本
04$是第二部分Cost, 代表该hash结果进行了的轮数, 一般cost的值越大, 消耗的时间越长, 也就代表证该hash结果被破解的概率越小
QxuJ7Zt7l42ljBxmYKOfbe是第三部分Salt, 是由16字节(128 bit)的盐经过base 64编码得到的22字节大小的字符串, 我们不需要自己管理盐值.
maZG8dO.qeAvwF3qxd6QZWsD2uJLcAW是第四部分Hashed Text, 由24字节(96 bit)的hash值经过base 64编码得到, 长度为31个Byte
使用BCrypt时, 我们需要指定三个参数作为输入, 即cost, Salt和password, BCrypt将输入进得三个参数作为因子, 使用 expensiveBlowfishSetup(password,cost,salt), 在内部执行次blowfish算法, 再将其对OrpheanBeholderScryDoubt进行64次blowfish加密, 最后对加密结果使用base 64编码, 将其变为24位的Hashed text
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
cipherData := make([]byte, len(magicCipherData))
copy(cipherData, magicCipherData)
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
if err != nil {
return nil, err
}
for i := 0; i < 24; i += 8 {
for j := 0; j < 64; j++ {
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
}
}
// Bug compatibility with C bcrypt implementations. We only encode 23 of
// the 24 bytes encrypted.
hsh := base64Encode(cipherData[:maxCryptedHashSize])
return hsh, nil
}
浅浅地使用一下
我们可以直接导入别人封装好的包
import "golang.org/x/crypto/bcrypt"
接着可以浅浅的对密码加密一下, 其中passwdBytes长度不应超过72
hash, err := bcrypt.GenerateFromPassword(passwdBytes, bcrypt.MinCost)
另外就是核验密码, 如果密码无误, err值就为nil
err := bcrypt.CompareHashAndPassword(requestPasswordBytes, truePasswordBytes)
完整代码
package main
import (
"errors"
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
password := "12345678"
//密码加密
hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost)
err := errors.New("err为nil时停止循环")
for err != nil {
var cmdWord string
fmt.Println("请输入密码:...")
_, _ = fmt.Scan(&cmdWord)
//密码核验
if err = bcrypt.CompareHashAndPassword(hash, []byte(cmdWord)); err != nil {
fmt.Println("输入的密码不正确!", err)
} else {
fmt.Println("密码正确!")
}
}
}