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


========== ========== crypto/hkdf ========== ==========

func Key(h func() hash.Hash, secret, salt []byte, info string, keyLength int) ([]byte, error)
入参:hash函数、明文、随机盐值、上下文信息、密钥长度。salt和info可为空。


========== ========== crypto/pbkdf2 ========== ==========

func Key(h func() hash.Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) 
入参:hash函数、明文、随机盐值(建议最少8字节)、迭代次数(建议2^12)、密钥长度(范围[1,(2^32-1)*h.Size()])。


========== ========== 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/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/hkdf"
	"crypto/pbkdf2"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"fmt"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/scrypt"
)

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

	kk, _ := hkdf.Key(sha256.New, pwd, salt, "", 32)
	bts, _ := pbkdf2.Key(sha1.New, string(pwd), salt, 1<<12, 32)
	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(kk, 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
}