📌Golang📌常用包📌密码hash.txt
用于安全存储密码的hash算法有:pbkdf2、bcrypt、scrypt、argon2等。
推荐使用: pbkdf2 < bcrypt < scrypt < argon2(最好是argon2id)
"golang.org/x/crypto"官方扩展包下提供了对应算法。

========== ========== ========== ========== ==========

"golang.org/x/crypto/bcrypt"内置常量:
	MinCost     int = 4 
	MaxCost     int = 31
	DefaultCost int = 10

func GenerateFromPassword(password []byte, cost int) ([]byte, error)
从明文密码生成60字节长度hash值,password长度不能超过72字节,cost范围[4,31]。
cost值越大运算速度越慢,cost每增加1运算耗时大约翻一倍,一般取DefaultCost。
bcrypt是加盐的自适应hash算法,每次运行计算的密码值都不相同。

func Cost(hashedPassword []byte) (int, error)
返回用于创建给定哈希密码的cost值。

func CompareHashAndPassword(hashedPassword, password []byte) error
将哈希密码与其可能的明文密码进行比较,成功时返回nil,失败时返回error。

========== ========== ========== ========== ==========

"golang.org/x/crypto/pbkdf2"

func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte
入参:明文、随机盐值(建议最少8字节)、迭代次数(建议2^12)、返回密钥长度、hash函数。

========== ========== ========== ========== ==========

"golang.org/x/crypto/scrypt"

func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error)
password明文,salt随机盐值,N是CPU成本参数必须是大于1的2的幂,r*p必须小于2^30,keyLen为返回密钥长度。
推荐参数为N=2^15,r=8,p=1。参数N、r和p应随着内存延迟和CPU并行度的增加而增加。
可将N设置为100ms可导出密钥的2的最大幂,salt建议至少8字节。

========== ========== ========== ========== ==========

"golang.org/x/crypto/argon2"

func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte
time参数指定通过内存的次数必须大于0,memory参数指定以KiB为单位的内存大小,threads为并行参数必须大于0。
salt随机盐值,keyLen为返回密钥长度。Key建议参数为time=3,memory=2^15;IDKey建议参数为time=1,memory=2^16。

========== ========== ========== ========== ==========

package main

import (
	"crypto/rand"
	"crypto/sha1"
	"fmt"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/crypto/scrypt"
)

func main() {
	pwd := []byte("123456")
	salt := make([]byte, 16)
	if _, err := rand.Read(salt); err != nil {
		panic(err)
	}

	bts := pbkdf2.Key(pwd, salt, 1<<12, 32, sha1.New)
	ss, _ := scrypt.Key(pwd, salt, 1<<15, 8, 1, 32)
	a1 := argon2.Key(pwd, salt, 3, 1<<15, 4, 32)
	a2 := argon2.IDKey(pwd, salt, 1, 1<<16, 4, 32)
	fmt.Println(bts, ss, a1, a2) // 返回的字节数组和salt应该分别编码后保存,也可以合并保存到一个字段。

	hashed, _ := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost)
	fmt.Println(string(hashed), len(hashed)) // 密码长度60,已合并salt值无需另外保存。
	err := bcrypt.CompareHashAndPassword(hashed, pwd)
	fmt.Println(err == nil) // true
	err = bcrypt.CompareHashAndPassword(hashed, []byte("654321"))
	fmt.Println(err == nil) // false
}