Files
go-utils/password/pbkdf2.go
2025-06-14 23:28:15 +08:00

151 lines
3.5 KiB
Go

package password
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"hash"
"strconv"
"strings"
)
// PBKDF2Crypto 实现 PBKDF2-HMAC 密码哈希算法
type PBKDF2Crypto struct {
// 可配置参数,默认使用推荐值
Iterations int
KeyLength int
Hash func() hash.Hash
HashName string
}
// NewPBKDF2Crypto 创建带默认参数的 PBKDF2 加密器 (SHA256)
func NewPBKDF2Crypto() *PBKDF2Crypto {
return &PBKDF2Crypto{
Iterations: 310000, // NIST 推荐最小值
KeyLength: 32, // 256-bit
Hash: sha256.New,
HashName: "sha256",
}
}
// NewPBKDF2WithSHA512 创建使用 SHA512 的 PBKDF2 加密器
func NewPBKDF2WithSHA512() *PBKDF2Crypto {
return &PBKDF2Crypto{
Iterations: 600000, // SHA512 需要更多迭代
KeyLength: 64, // 512-bit
Hash: sha512.New,
HashName: "sha512",
}
}
// Encrypt 实现密码加密
func (p *PBKDF2Crypto) Encrypt(password string) (string, error) {
// 生成随机盐值 (16 bytes 推荐最小值)
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
return "", err
}
// 生成密钥
key := pbkdf2Key([]byte(password), salt, p.Iterations, p.KeyLength, p.Hash)
// 格式: pbkdf2:<hash>:<iterations>:<base64-salt>:<base64-key>
return fmt.Sprintf(
"pbkdf2:%s:%d:%s:%s",
p.HashName,
p.Iterations,
base64.RawStdEncoding.EncodeToString(salt),
base64.RawStdEncoding.EncodeToString(key),
), nil
}
// Verify 验证密码
func (p *PBKDF2Crypto) Verify(password, encrypted string) (bool, error) {
// 解析哈希字符串
parts := strings.Split(encrypted, ":")
if len(parts) != 5 || parts[0] != "pbkdf2" {
return false, errors.New("无效的 PBKDF2 哈希格式")
}
// 解析参数
hashName := parts[1]
iterations, err := strconv.Atoi(parts[2])
if err != nil {
return false, errors.New("无效的迭代次数")
}
salt, err := base64.RawStdEncoding.DecodeString(parts[3])
if err != nil {
return false, err
}
expectedKey, err := base64.RawStdEncoding.DecodeString(parts[4])
if err != nil {
return false, err
}
// 根据哈希名称选择哈希函数
hashFunc, ok := getHashFunction(hashName)
if !ok {
return false, fmt.Errorf("不支持的哈希算法: %s", hashName)
}
// 生成新密钥
keyLength := len(expectedKey)
newKey := pbkdf2Key([]byte(password), salt, iterations, keyLength, hashFunc)
// 安全比较
return hmac.Equal(newKey, expectedKey), nil
}
// pbkdf2Key 实现 PBKDF2 核心算法
func pbkdf2Key(password, salt []byte, iterations, keyLength int, hashFunc func() hash.Hash) []byte {
prf := hmac.New(hashFunc, password)
hashLength := prf.Size()
blockCount := (keyLength + hashLength - 1) / hashLength
output := make([]byte, 0, blockCount*hashLength)
for i := 1; i <= blockCount; i++ {
// U1 = PRF(password, salt || INT(i))
prf.Reset()
prf.Write(salt)
binary.BigEndian.PutUint32(make([]byte, 4), uint32(i))
prf.Write([]byte{byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i)})
u := prf.Sum(nil)
// F = U1 ⊕ U2 ⊕ ... ⊕ U_iterations
f := make([]byte, len(u))
copy(f, u)
for j := 1; j < iterations; j++ {
prf.Reset()
prf.Write(u)
u = prf.Sum(nil)
for k := 0; k < len(f); k++ {
f[k] ^= u[k]
}
}
output = append(output, f...)
}
return output[:keyLength]
}
// getHashFunction 根据名称获取哈希函数
func getHashFunction(name string) (func() hash.Hash, bool) {
switch name {
case "sha256":
return sha256.New, true
case "sha512":
return sha512.New, true
default:
return nil, false
}
}