(转)Go实现随机加盐密码认证

发布时间 2023-11-30 15:52:03作者: liujiacai

原文:https://juejin.cn/post/7068192471498358821

为什么要加密

人们往往有使用同一密码的习惯, 为了防止数据库意外泄露/破坏和出于保护用户隐私的目的, 不应在数据库里存入用户密码明文

实现

代码

 
scss
复制代码
package main

import (
	"fmt"
	"golang.org/x/crypto/bcrypt"
	"time"
)

func main() {
	password := []byte("thisIsPassWord")
	nowG := time.Now()
	hashedPassword, _ := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
	fmt.Println("加密后", string(hashedPassword), "耗时", time.Now().Sub(nowG))
	nowC := time.Now()
	err := bcrypt.CompareHashAndPassword(hashedPassword, password)
	fmt.Println("验证耗费时间", time.Now().Sub(nowC))
	fmt.Println(err)
}

// 结果
// 加密后 $2a$10$ESkb/bwSyISLgq1bOH0C2utXdb.hcH9oBQD1hUnfDOzm4bMKK6EX2 耗时 67.9985ms
// 验证耗费时间 66.0008ms
// <nil>  

将加密后的密码串存入数据表, 然后用户登录传递明文密码(也可以前段加密后端再解密成明文)进行验证

密码串解析

 
shell
复制代码
$2a$10$ESkb/bwSyISLgq1bOH0C2utXdb.hcH9oBQD1hUnfDOzm4bMKK6EX2
$ 为分隔符
2a bcrypt加密版本号
10 Cost值
ESkb/bwSyISLgq1bOH0C2utXdb 盐
hcH9oBQD1hUnfDOzm4bMKK6EX2 密码密文

优缺点

 
makefile
复制代码
优点:
    单向HASH,不可解密
    盐值随机(有效防止彩虹表碰撞)
    破解时间成本极高
缺点:
    效率较慢(相较于mad5/sha算法)

源码分析

 
go
复制代码
// GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version.
func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
	p, err := newFromPassword(password, cost)
	if err != nil {
		return nil, err
	}
	return p.Hash(), nil
}


func newFromPassword(password []byte, cost int) (*hashed, error) {
   if cost < MinCost {
      cost = DefaultCost
   }
   p := new(hashed)
   p.major = majorVersion
   p.minor = minorVersion

   err := checkCost(cost)
   if err != nil {
      return nil, err
   }
   p.cost = cost

   unencodedSalt := make([]byte, maxSaltSize)
   _, err = io.ReadFull(rand.Reader, unencodedSalt)
   if err != nil {
      return nil, err
   }

   p.salt = base64Encode(unencodedSalt)
   hash, err := bcrypt(password, p.cost, p.salt)
   if err != nil {
      return nil, err
   }
   p.hash = hash
   return p, err
}


// CompareHashAndPassword compares a bcrypt hashed password with its possible
// plaintext equivalent. Returns nil on success, or an error on failure.
func CompareHashAndPassword(hashedPassword, password []byte) error {
	p, err := newFromHash(hashedPassword)
	if err != nil {
		return err
	}

	otherHash, err := bcrypt(password, p.cost, p.salt)
	if err != nil {
		return err
	}

	otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
	if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
		return nil
	}

	return ErrMismatchedHashAndPassword
}

加密就是随机生成16位byte再转成base64当做salt, 用salt和cost进行加密

解密就是用已有的加密哈希的salt和cost对现有密码进行加密来确认密码密文是否一致