feat: password.
This commit is contained in:
14
password/README.md
Normal file
14
password/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 密码加解密
|
||||
|
||||
算法列表
|
||||
|
||||
| 算法名 | 特点 | 用途 |
|
||||
|-----------------------------------------------|-------------------------------|------------------------|
|
||||
| Bcrypt | 基于 Blowfish 算法,内置盐值,支持调整计算成本。 | 密码存储,防止暴力破解。 |
|
||||
| Argon2 | 内存硬性哈希算法,支持多种参数调整(内存、时间、并行度)。 | 码存储,适合高安全性需求。 |
|
||||
| PBKDF2 | 基于 HMAC 的密钥派生函数,支持多种哈希算法。 | 密码存储,兼容性好。 |
|
||||
| SHA-256/SHA-512 | 快速单向哈希算法,无内置盐值。 | 数据完整性校验,需手动添加盐值用于密码存储。 |
|
||||
| AES (Advanced Encryption Standard) | 对称加密算法,支持 128/192/256 位密钥。 | 数据加密传输或存储。 |
|
||||
| RSA | 非对称加密算法,基于大整数分解难题。 | 数据加密、数字签名。 |
|
||||
| ECDSA/ECDH (椭圆曲线算法) | 基于椭圆曲线的非对称加密,密钥更短但安全性高。 | 数据完整性和认证。 |
|
||||
| HMAC (Hash-based Message Authentication Code) | 基于哈希算法的消息认证码。 | 数据完整性和认证。 |
|
||||
111
password/argon2.go
Normal file
111
password/argon2.go
Normal file
@@ -0,0 +1,111 @@
|
||||
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
|
||||
}
|
||||
41
password/argon2_test.go
Normal file
41
password/argon2_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArgon2Crypto_EncryptAndVerify(t *testing.T) {
|
||||
crypto := NewArgon2Crypto()
|
||||
|
||||
// 测试加密
|
||||
password := "securepassword"
|
||||
encrypted, err := crypto.Encrypt(password)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
// 测试验证成功
|
||||
isValid, err := crypto.Verify(password, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证未通过,密码应匹配")
|
||||
}
|
||||
|
||||
// 测试验证失败
|
||||
isValid, err = crypto.Verify("wrongpassword", encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if isValid {
|
||||
t.Fatal("验证通过,但密码不应匹配")
|
||||
}
|
||||
}
|
||||
34
password/bcrypt.go
Normal file
34
password/bcrypt.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type BCryptCrypto struct{}
|
||||
|
||||
func NewBCryptCrypto() *BCryptCrypto {
|
||||
return &BCryptCrypto{}
|
||||
}
|
||||
|
||||
// Encrypt 使用 bcrypt 加密密码,返回加密后的字符串和空盐值
|
||||
func (b *BCryptCrypto) Encrypt(password string) (encrypted string, err error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(hash), nil
|
||||
}
|
||||
|
||||
// Verify 验证密码是否匹配加密后的字符串
|
||||
func (b *BCryptCrypto) Verify(password, encrypted string) (bool, error) {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(encrypted), []byte(password))
|
||||
if err != nil {
|
||||
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
41
password/bcrypt_test.go
Normal file
41
password/bcrypt_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBCryptCrypto_EncryptAndVerify(t *testing.T) {
|
||||
crypto := NewBCryptCrypto()
|
||||
|
||||
// 测试加密
|
||||
password := "securepassword"
|
||||
encrypted, err := crypto.Encrypt(password)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
// 测试验证成功
|
||||
isValid, err := crypto.Verify(password, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证未通过,密码应匹配")
|
||||
}
|
||||
|
||||
// 测试验证失败
|
||||
isValid, err = crypto.Verify("wrongpassword", encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if isValid {
|
||||
t.Fatal("验证通过,但密码不应匹配")
|
||||
}
|
||||
}
|
||||
142
password/ecdsa_ecdh.go
Normal file
142
password/ecdsa_ecdh.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ECDSACrypto 实现基于 ECDSA 的加密和验证
|
||||
type ECDSACrypto struct {
|
||||
privateKey *ecdsa.PrivateKey
|
||||
publicKey *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// NewECDSACrypto 创建一个新的 ECDSACrypto 实例
|
||||
func NewECDSACrypto() (*ECDSACrypto, error) {
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ECDSACrypto{
|
||||
privateKey: privateKey,
|
||||
publicKey: &privateKey.PublicKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt 使用 ECDSA 对消息进行签名
|
||||
func (e *ECDSACrypto) Encrypt(plainPassword string) (string, error) {
|
||||
if plainPassword == "" {
|
||||
return "", errors.New("密码不能为空")
|
||||
}
|
||||
|
||||
hash := sha256.Sum256([]byte(plainPassword))
|
||||
r, s, err := ecdsa.Sign(rand.Reader, e.privateKey, hash[:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signature := r.String() + "$" + s.String()
|
||||
return "ecdsa$" + signature, nil
|
||||
}
|
||||
|
||||
// Verify 验证消息的签名是否有效
|
||||
func (e *ECDSACrypto) Verify(plainPassword, encrypted string) (bool, error) {
|
||||
if plainPassword == "" || encrypted == "" {
|
||||
return false, errors.New("密码或加密字符串不能为空")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(encrypted, "$", 3)
|
||||
if len(parts) != 3 || parts[0] != "ecdsa" {
|
||||
return false, errors.New("加密字符串格式无效")
|
||||
}
|
||||
|
||||
r := new(big.Int)
|
||||
s := new(big.Int)
|
||||
r.SetString(parts[1], 10)
|
||||
s.SetString(parts[2], 10)
|
||||
|
||||
hash := sha256.Sum256([]byte(plainPassword))
|
||||
return ecdsa.Verify(e.publicKey, hash[:], r, s), nil
|
||||
}
|
||||
|
||||
// ECDHCrypto 实现基于 ECDH 的密钥交换
|
||||
type ECDHCrypto struct {
|
||||
privateKey *ecdsa.PrivateKey
|
||||
publicKey *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// NewECDHCrypto 创建一个新的 ECDHCrypto 实例
|
||||
func NewECDHCrypto() (*ECDHCrypto, error) {
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ECDHCrypto{
|
||||
privateKey: privateKey,
|
||||
publicKey: &privateKey.PublicKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt 返回公钥作为加密结果
|
||||
func (e *ECDHCrypto) Encrypt(plainPassword string) (string, error) {
|
||||
if plainPassword == "" {
|
||||
return "", errors.New("密码不能为空")
|
||||
}
|
||||
|
||||
publicKeyBytes := elliptic.Marshal(e.privateKey.Curve, e.publicKey.X, e.publicKey.Y)
|
||||
return "ecdh$" + base64.StdEncoding.EncodeToString(publicKeyBytes), nil
|
||||
}
|
||||
|
||||
// Verify 验证共享密钥是否一致
|
||||
func (e *ECDHCrypto) Verify(plainPassword, encrypted string) (bool, error) {
|
||||
if plainPassword == "" || encrypted == "" {
|
||||
return false, errors.New("密码或加密字符串不能为空")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(encrypted, "$", 2)
|
||||
if len(parts) != 2 || parts[0] != "ecdh" {
|
||||
return false, errors.New("加密字符串格式无效")
|
||||
}
|
||||
|
||||
publicKeyBytes, err := base64.StdEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
x, y := elliptic.Unmarshal(e.privateKey.Curve, publicKeyBytes)
|
||||
if x == nil || y == nil {
|
||||
return false, errors.New("无效的公钥")
|
||||
}
|
||||
|
||||
sharedX, _ := e.privateKey.Curve.ScalarMult(x, y, e.privateKey.D.Bytes())
|
||||
expectedHash := sha256.Sum256(sharedX.Bytes())
|
||||
actualHash := sha256.Sum256([]byte(plainPassword))
|
||||
|
||||
return expectedHash == actualHash, nil
|
||||
}
|
||||
|
||||
func (e *ECDHCrypto) DeriveSharedSecret(publicKey string) ([]byte, error) {
|
||||
// 解码对方的公钥
|
||||
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 反序列化公钥
|
||||
x, y := elliptic.Unmarshal(e.privateKey.Curve, publicKeyBytes)
|
||||
if x == nil || y == nil {
|
||||
return nil, errors.New("无效的公钥")
|
||||
}
|
||||
|
||||
// 计算共享密钥
|
||||
sharedX, _ := e.privateKey.Curve.ScalarMult(x, y, e.privateKey.D.Bytes())
|
||||
|
||||
// 返回共享密钥的字节表示
|
||||
return sharedX.Bytes(), nil
|
||||
}
|
||||
60
password/ecdsa_ecdh_test.go
Normal file
60
password/ecdsa_ecdh_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestECDSACrypto_EncryptAndVerify(t *testing.T) {
|
||||
crypto, err := NewECDSACrypto()
|
||||
if err != nil {
|
||||
t.Fatalf("创建 ECDSACrypto 实例失败: %v", err)
|
||||
}
|
||||
|
||||
message := "test message"
|
||||
|
||||
// 签名消息
|
||||
encrypted, err := crypto.Encrypt(message)
|
||||
if err != nil {
|
||||
t.Fatalf("签名失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
isValid, err := crypto.Verify(message, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("签名验证未通过")
|
||||
}
|
||||
}
|
||||
|
||||
func TestECDHCrypto_EncryptAndVerify(t *testing.T) {
|
||||
crypto1, err := NewECDHCrypto()
|
||||
if err != nil {
|
||||
t.Fatalf("创建 ECDHCrypto 实例1失败: %v", err)
|
||||
}
|
||||
|
||||
crypto2, err := NewECDHCrypto()
|
||||
if err != nil {
|
||||
t.Fatalf("创建 ECDHCrypto 实例2失败: %v", err)
|
||||
}
|
||||
|
||||
message := "test message"
|
||||
|
||||
// 获取公钥
|
||||
encrypted, err := crypto1.Encrypt(message)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证共享密钥
|
||||
isValid, err := crypto2.Verify(message, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("共享密钥验证未通过")
|
||||
}
|
||||
}
|
||||
11
password/go.mod
Normal file
11
password/go.mod
Normal file
@@ -0,0 +1,11 @@
|
||||
module github.com/tx7do/go-utils/password
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.2
|
||||
|
||||
require golang.org/x/crypto v0.39.0
|
||||
|
||||
require golang.org/x/sys v0.33.0 // indirect
|
||||
|
||||
replace github.com/tx7do/go-utils => ../
|
||||
6
password/go.sum
Normal file
6
password/go.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
50
password/hmac.go
Normal file
50
password/hmac.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// HMACCrypto 实现基于 HMAC 的加密和验证
|
||||
type HMACCrypto struct {
|
||||
secretKey []byte
|
||||
}
|
||||
|
||||
// NewHMACCrypto 创建一个新的 HMACCrypto 实例
|
||||
func NewHMACCrypto(secretKey string) *HMACCrypto {
|
||||
return &HMACCrypto{
|
||||
secretKey: []byte(secretKey),
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt 使用 HMAC-SHA256 对数据进行加密
|
||||
func (h *HMACCrypto) Encrypt(data string) (string, error) {
|
||||
if data == "" {
|
||||
return "", errors.New("数据不能为空")
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, h.secretKey)
|
||||
_, err := mac.Write([]byte(data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash := mac.Sum(nil)
|
||||
return hex.EncodeToString(hash), nil
|
||||
}
|
||||
|
||||
// Verify 验证数据的 HMAC 值是否匹配
|
||||
func (h *HMACCrypto) Verify(data, encrypted string) (bool, error) {
|
||||
if data == "" || encrypted == "" {
|
||||
return false, errors.New("数据或加密字符串不能为空")
|
||||
}
|
||||
|
||||
expectedHash, err := h.Encrypt(data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return hmac.Equal([]byte(expectedHash), []byte(encrypted)), nil
|
||||
}
|
||||
43
password/hmac_test.go
Normal file
43
password/hmac_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHMACCrypto_EncryptAndVerify(t *testing.T) {
|
||||
secretKey := "mysecretkey"
|
||||
crypto := NewHMACCrypto(secretKey)
|
||||
|
||||
data := "testdata"
|
||||
|
||||
// 测试加密
|
||||
encrypted, err := crypto.Encrypt(data)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
// 测试验证
|
||||
isValid, err := crypto.Verify(data, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证结果不匹配")
|
||||
}
|
||||
|
||||
// 测试验证失败的情况
|
||||
isValid, err = crypto.Verify("wrongdata", encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if isValid {
|
||||
t.Fatal("验证结果错误,预期验证失败")
|
||||
}
|
||||
}
|
||||
29
password/interface.go
Normal file
29
password/interface.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Crypto 密码加解密接口
|
||||
type Crypto interface {
|
||||
// Encrypt 加密密码,返回加密后的字符串(包含算法标识和盐值)
|
||||
Encrypt(plainPassword string) (encrypted string, err error)
|
||||
|
||||
// Verify 验证密码是否匹配
|
||||
Verify(plainPassword, encrypted string) (bool, error)
|
||||
}
|
||||
|
||||
func CreateCrypto(algorithm string) (Crypto, error) {
|
||||
algorithm = strings.ToLower(algorithm)
|
||||
switch algorithm {
|
||||
case "bcrypt":
|
||||
return NewBCryptCrypto(), nil
|
||||
case "pbkdf2":
|
||||
return NewPBKDF2Crypto(), nil
|
||||
case "argon2":
|
||||
return NewArgon2Crypto(), nil
|
||||
default:
|
||||
return nil, errors.New("不支持的加密算法")
|
||||
}
|
||||
}
|
||||
150
password/pbkdf2.go
Normal file
150
password/pbkdf2.go
Normal file
@@ -0,0 +1,150 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
77
password/pbkdf2_test.go
Normal file
77
password/pbkdf2_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPBKDF2Crypto_EncryptAndVerify(t *testing.T) {
|
||||
crypto := NewPBKDF2Crypto()
|
||||
|
||||
// 测试加密
|
||||
password := "securepassword"
|
||||
encrypted, err := crypto.Encrypt(password)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
// 测试验证成功
|
||||
isValid, err := crypto.Verify(password, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证未通过,密码应匹配")
|
||||
}
|
||||
|
||||
// 测试验证失败
|
||||
isValid, err = crypto.Verify("wrongpassword", encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if isValid {
|
||||
t.Fatal("验证通过,但密码不应匹配")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPBKDF2WithSHA512_EncryptAndVerify(t *testing.T) {
|
||||
crypto := NewPBKDF2WithSHA512()
|
||||
|
||||
// 测试加密
|
||||
password := "securepassword"
|
||||
encrypted, err := crypto.Encrypt(password)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
// 测试验证成功
|
||||
isValid, err := crypto.Verify(password, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证未通过,密码应匹配")
|
||||
}
|
||||
|
||||
// 测试验证失败
|
||||
isValid, err = crypto.Verify("wrongpassword", encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if isValid {
|
||||
t.Fatal("验证通过,但密码不应匹配")
|
||||
}
|
||||
}
|
||||
73
password/rsa.go
Normal file
73
password/rsa.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
)
|
||||
|
||||
// RSACrypto 实现 RSA 加密和解密
|
||||
type RSACrypto struct {
|
||||
privateKey *rsa.PrivateKey
|
||||
publicKey *rsa.PublicKey
|
||||
}
|
||||
|
||||
// NewRSACrypto 创建一个新的 RSACrypto 实例
|
||||
func NewRSACrypto(keySize int) (*RSACrypto, error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RSACrypto{
|
||||
privateKey: privateKey,
|
||||
publicKey: &privateKey.PublicKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt 使用公钥加密数据
|
||||
func (r *RSACrypto) Encrypt(data string) (string, error) {
|
||||
encryptedBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, r.publicKey, []byte(data), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(encryptedBytes), nil
|
||||
}
|
||||
|
||||
// Decrypt 使用私钥解密数据
|
||||
func (r *RSACrypto) Decrypt(encryptedData string) (string, error) {
|
||||
decodedData, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, r.privateKey, decodedData, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(decryptedBytes), nil
|
||||
}
|
||||
|
||||
// ExportPrivateKey 导出私钥为 PEM 格式
|
||||
func (r *RSACrypto) ExportPrivateKey() (string, error) {
|
||||
privateKeyBytes := x509.MarshalPKCS1PrivateKey(r.privateKey)
|
||||
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: privateKeyBytes,
|
||||
})
|
||||
return string(privateKeyPEM), nil
|
||||
}
|
||||
|
||||
// ExportPublicKey 导出公钥为 PEM 格式
|
||||
func (r *RSACrypto) ExportPublicKey() (string, error) {
|
||||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(r.publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
publicKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PUBLIC KEY",
|
||||
Bytes: publicKeyBytes,
|
||||
})
|
||||
return string(publicKeyPEM), nil
|
||||
}
|
||||
63
password/rsa_test.go
Normal file
63
password/rsa_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRSACrypto_EncryptAndDecrypt(t *testing.T) {
|
||||
// 创建 RSACrypto 实例
|
||||
crypto, err := NewRSACrypto(2048)
|
||||
if err != nil {
|
||||
t.Fatalf("创建 RSACrypto 实例失败: %v", err)
|
||||
}
|
||||
|
||||
// 测试加密
|
||||
originalData := "securedata"
|
||||
encryptedData, err := crypto.Encrypt(originalData)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encryptedData == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encryptedData)
|
||||
|
||||
// 测试解密
|
||||
decryptedData, err := crypto.Decrypt(encryptedData)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
if decryptedData != originalData {
|
||||
t.Fatalf("解密结果不匹配,期望: %s,实际: %s", originalData, decryptedData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSACrypto_ExportKeys(t *testing.T) {
|
||||
// 创建 RSACrypto 实例
|
||||
crypto, err := NewRSACrypto(2048)
|
||||
if err != nil {
|
||||
t.Fatalf("创建 RSACrypto 实例失败: %v", err)
|
||||
}
|
||||
|
||||
// 测试导出私钥
|
||||
privateKey, err := crypto.ExportPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("导出私钥失败: %v", err)
|
||||
}
|
||||
|
||||
if privateKey == "" {
|
||||
t.Fatal("导出的私钥为空")
|
||||
}
|
||||
|
||||
// 测试导出公钥
|
||||
publicKey, err := crypto.ExportPublicKey()
|
||||
if err != nil {
|
||||
t.Fatalf("导出公钥失败: %v", err)
|
||||
}
|
||||
|
||||
if publicKey == "" {
|
||||
t.Fatal("导出的公钥为空")
|
||||
}
|
||||
}
|
||||
110
password/sha.go
Normal file
110
password/sha.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// SHACrypto 实现 SHA-256/SHA-512 密码哈希算法(带盐值)
|
||||
type SHACrypto struct {
|
||||
Hash func() hash.Hash
|
||||
HashName string
|
||||
SaltLength int
|
||||
}
|
||||
|
||||
// NewSHA256Crypto 创建 SHA-256 加密器
|
||||
func NewSHA256Crypto() *SHACrypto {
|
||||
return &SHACrypto{
|
||||
Hash: sha256.New,
|
||||
HashName: "sha256",
|
||||
SaltLength: 16, // 16 字节盐值
|
||||
}
|
||||
}
|
||||
|
||||
// NewSHA512Crypto 创建 SHA-512 加密器
|
||||
func NewSHA512Crypto() *SHACrypto {
|
||||
return &SHACrypto{
|
||||
Hash: sha512.New,
|
||||
HashName: "sha512",
|
||||
SaltLength: 16, // 16 字节盐值
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt 实现密码加密(带盐值)
|
||||
func (s *SHACrypto) Encrypt(password string) (string, error) {
|
||||
// 生成随机盐值
|
||||
salt := make([]byte, s.SaltLength)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 计算哈希
|
||||
hashValue := s.Hash()
|
||||
hashValue.Write(salt)
|
||||
hashValue.Write([]byte(password))
|
||||
hashBytes := hashValue.Sum(nil)
|
||||
|
||||
// 格式: sha256:$salt$hash 或 sha512:$salt$hash
|
||||
return fmt.Sprintf(
|
||||
"%s$%s$%s",
|
||||
s.HashName,
|
||||
base64.RawStdEncoding.EncodeToString(salt),
|
||||
hex.EncodeToString(hashBytes),
|
||||
), nil
|
||||
}
|
||||
|
||||
// Verify 验证密码
|
||||
func (s *SHACrypto) Verify(password, encrypted string) (bool, error) {
|
||||
// 解析哈希字符串
|
||||
parts := strings.Split(encrypted, "$")
|
||||
if len(parts) != 3 {
|
||||
return false, errors.New("无效的 SHA 哈希格式")
|
||||
}
|
||||
|
||||
hashName := parts[0]
|
||||
if hashName != s.HashName {
|
||||
return false, fmt.Errorf("哈希算法不匹配: 期望 %s, 实际 %s", s.HashName, hashName)
|
||||
}
|
||||
|
||||
// 解码盐值
|
||||
salt, err := base64.RawStdEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 解码原始哈希值
|
||||
originalHash, err := hex.DecodeString(parts[2])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 计算新哈希
|
||||
hashValue := s.Hash()
|
||||
hashValue.Write(salt)
|
||||
hashValue.Write([]byte(password))
|
||||
newHash := hashValue.Sum(nil)
|
||||
|
||||
// 安全比较
|
||||
return compareHash(newHash, originalHash), nil
|
||||
}
|
||||
|
||||
// compareHash 安全比较两个哈希值
|
||||
func compareHash(h1, h2 []byte) bool {
|
||||
if len(h1) != len(h2) {
|
||||
return false
|
||||
}
|
||||
result := 0
|
||||
for i := 0; i < len(h1); i++ {
|
||||
result |= int(h1[i] ^ h2[i])
|
||||
}
|
||||
return result == 0
|
||||
}
|
||||
53
password/sha_test.go
Normal file
53
password/sha_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSHACrypto_EncryptAndVerify_SHA256(t *testing.T) {
|
||||
crypto := NewSHA256Crypto()
|
||||
|
||||
password := "securepassword"
|
||||
encrypted, err := crypto.Encrypt(password)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
isValid, err := crypto.Verify(password, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证结果不匹配")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHACrypto_EncryptAndVerify_SHA512(t *testing.T) {
|
||||
crypto := NewSHA512Crypto()
|
||||
|
||||
password := "securepassword"
|
||||
encrypted, err := crypto.Encrypt(password)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == "" {
|
||||
t.Fatal("加密结果为空")
|
||||
}
|
||||
t.Log(encrypted)
|
||||
|
||||
isValid, err := crypto.Verify(password, encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("验证失败: %v", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
t.Fatal("验证结果不匹配")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user