112 lines
2.3 KiB
Go
112 lines
2.3 KiB
Go
package password
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/argon2"
|
|
)
|
|
|
|
// Argon2Crypto 实现 Argon2id 密码哈希算法
|
|
type Argon2Crypto struct {
|
|
// 参数可配置,默认使用推荐值
|
|
Memory uint32
|
|
Iterations uint32
|
|
Parallelism uint8
|
|
SaltLength uint32
|
|
KeyLength uint32
|
|
}
|
|
|
|
// NewArgon2Crypto 创建带默认参数的 Argon2 加密器
|
|
func NewArgon2Crypto() *Argon2Crypto {
|
|
return &Argon2Crypto{
|
|
Memory: 64 * 1024, // 64MB
|
|
Iterations: 3,
|
|
Parallelism: 2,
|
|
SaltLength: 16,
|
|
KeyLength: 32,
|
|
}
|
|
}
|
|
|
|
// Encrypt 实现密码加密
|
|
func (a *Argon2Crypto) Encrypt(password string) (string, error) {
|
|
// 生成随机盐值
|
|
salt := make([]byte, a.SaltLength)
|
|
if _, err := rand.Read(salt); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// 生成哈希
|
|
hash := argon2.IDKey(
|
|
[]byte(password),
|
|
salt,
|
|
a.Iterations,
|
|
a.Memory,
|
|
a.Parallelism,
|
|
a.KeyLength,
|
|
)
|
|
|
|
// 格式化输出(兼容标准格式)
|
|
return fmt.Sprintf(
|
|
"$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
|
|
argon2.Version,
|
|
a.Memory,
|
|
a.Iterations,
|
|
a.Parallelism,
|
|
base64.RawStdEncoding.EncodeToString(salt),
|
|
base64.RawStdEncoding.EncodeToString(hash),
|
|
), nil
|
|
}
|
|
|
|
// Verify 验证密码
|
|
func (a *Argon2Crypto) Verify(password, encrypted string) (bool, error) {
|
|
// 解析哈希字符串
|
|
parts := strings.Split(encrypted, "$")
|
|
if len(parts) != 6 {
|
|
return false, errors.New("无效的 Argon2 哈希格式")
|
|
}
|
|
|
|
// 解析参数
|
|
var version int
|
|
var memory, iterations uint32
|
|
var parallelism uint8
|
|
_, err := fmt.Sscanf(parts[2], "v=%d", &version)
|
|
if err != nil || version != argon2.Version {
|
|
return false, errors.New("不支持的 Argon2 版本")
|
|
}
|
|
|
|
_, err = fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &memory, &iterations, ¶llelism)
|
|
if err != nil {
|
|
return false, errors.New("无效的 Argon2 参数")
|
|
}
|
|
|
|
// 解码盐值和哈希
|
|
salt, err := base64.RawStdEncoding.DecodeString(parts[4])
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
decodedHash, err := base64.RawStdEncoding.DecodeString(parts[5])
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
keyLength := uint32(len(decodedHash))
|
|
|
|
// 使用相同参数生成新哈希
|
|
newHash := argon2.IDKey(
|
|
[]byte(password),
|
|
salt,
|
|
iterations,
|
|
memory,
|
|
parallelism,
|
|
keyLength,
|
|
)
|
|
|
|
// 安全比较
|
|
return subtle.ConstantTimeCompare(newHash, decodedHash) == 1, nil
|
|
}
|