初步了解密码加密(五) | 青训营笔记

106 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第17天

前几期讲了hash加密和hash破解的一些知识, 这期讲一下项目中常用的加密工具BCrypt

BCrypt

粗略了解原理

了解BCrypt, 首先要了解Hash加密和加盐, 可以去看我之前写的文章初步了解密码加密(二) | 青训营笔记, 里面简单的介绍了一下hash加密和加盐

BCrypt是一种基于Blowfish对称密钥加密分组密码算法设计出来的hash散列函数, 是一种可生成随机盐值的单向Hash加密算法, Hash结果中包含了上一轮Hash生成的盐值. 每次加密后的密文结果都不一样

下面是使用BCrypt生成的一个加密结果, BCrypt的加密结果主要可以分为Prefix, Cost SaltHashed Text四部分

$2a$04$QxuJ7Zt7l42ljBxmYKOfbemaZG8dO.qeAvwF3qxd6QZWsD2uJLcAW

其中,

$2a$ 是第一部分prefix, 代表BCrypt的版本

04$是第二部分Cost, 代表该hash结果进行了242^4的轮数, 一般cost的值越大, 消耗的时间越长, 也就代表证该hash结果被破解的概率越小

QxuJ7Zt7l42ljBxmYKOfbe是第三部分Salt, 是由16字节(128 bit)的盐经过base 64编码得到的22字节大小的字符串, 我们不需要自己管理盐值.

maZG8dO.qeAvwF3qxd6QZWsD2uJLcAW是第四部分Hashed Text, 由24字节(96 bit)的hash值经过base 64编码得到, 长度为31个Byte

使用BCrypt时, 我们需要指定三个参数作为输入, 即cost, Saltpassword, BCrypt将输入进得三个参数作为因子, 使用 expensiveBlowfishSetup(password,cost,salt), 在内部执行2cost2^{cost}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("密码正确!")
		}
	}
}

image-20230223215122159