mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
324 lines
7.6 KiB
Go
324 lines
7.6 KiB
Go
package utils
|
||
|
||
import (
|
||
"crypto/aes"
|
||
"crypto/cipher"
|
||
"crypto/rand"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
"sync"
|
||
|
||
"github.com/spf13/viper"
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
// CryptoManager 加密管理器,提供高性能的加密解密服务
|
||
type CryptoManager struct {
|
||
key []byte
|
||
gcm cipher.AEAD
|
||
mutex sync.RWMutex
|
||
inited bool
|
||
}
|
||
|
||
// 全局加密管理器实例
|
||
var cryptoManager = &CryptoManager{}
|
||
|
||
// initCrypto 初始化加密管理器
|
||
// 缓存密钥和GCM实例,避免重复创建
|
||
func (cm *CryptoManager) initCrypto() error {
|
||
cm.mutex.Lock()
|
||
defer cm.mutex.Unlock()
|
||
|
||
if cm.inited {
|
||
return nil
|
||
}
|
||
|
||
// 从配置中获取密钥
|
||
secret := viper.GetString("encryption_key")
|
||
if secret == "" {
|
||
secret = "default-secret"
|
||
}
|
||
|
||
// 生成AES密钥
|
||
sum := sha256.Sum256([]byte(secret))
|
||
cm.key = sum[:]
|
||
|
||
// 创建AES cipher
|
||
block, err := aes.NewCipher(cm.key)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 创建GCM
|
||
gcm, err := cipher.NewGCM(block)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
cm.gcm = gcm
|
||
cm.inited = true
|
||
return nil
|
||
}
|
||
|
||
// EncryptString 字符串加密(AES-256-GCM)
|
||
// 使用缓存的密钥和GCM实例,提高性能
|
||
func EncryptString(plain string) (string, error) {
|
||
if err := cryptoManager.initCrypto(); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
cryptoManager.mutex.RLock()
|
||
gcm := cryptoManager.gcm
|
||
cryptoManager.mutex.RUnlock()
|
||
|
||
// 生成随机nonce
|
||
nonce := make([]byte, gcm.NonceSize())
|
||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 加密
|
||
ciphertext := gcm.Seal(nil, nonce, []byte(plain), nil)
|
||
buf := append(nonce, ciphertext...)
|
||
return base64.StdEncoding.EncodeToString(buf), nil
|
||
}
|
||
|
||
// DecryptString 字符串解密(AES-256-GCM)
|
||
// 使用缓存的密钥和GCM实例,提高性能
|
||
func DecryptString(enc string) (string, error) {
|
||
if err := cryptoManager.initCrypto(); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
cryptoManager.mutex.RLock()
|
||
gcm := cryptoManager.gcm
|
||
cryptoManager.mutex.RUnlock()
|
||
|
||
// 解码base64
|
||
data, err := base64.StdEncoding.DecodeString(enc)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 检查数据长度
|
||
if len(data) < gcm.NonceSize() {
|
||
return "", errors.New("ciphertext too short")
|
||
}
|
||
|
||
// 分离nonce和密文
|
||
nonce := data[:gcm.NonceSize()]
|
||
ciphertext := data[gcm.NonceSize():]
|
||
|
||
// 解密
|
||
plain, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return string(plain), nil
|
||
}
|
||
|
||
// ResetCrypto 重置加密管理器(用于配置更新后重新初始化)
|
||
func ResetCrypto() {
|
||
cryptoManager.mutex.Lock()
|
||
defer cryptoManager.mutex.Unlock()
|
||
cryptoManager.inited = false
|
||
cryptoManager.key = nil
|
||
cryptoManager.gcm = nil
|
||
}
|
||
|
||
// EncryptStringBatch 批量加密字符串
|
||
// 减少锁竞争,提高批量处理性能
|
||
func EncryptStringBatch(plains []string) ([]string, error) {
|
||
if err := cryptoManager.initCrypto(); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
cryptoManager.mutex.RLock()
|
||
gcm := cryptoManager.gcm
|
||
cryptoManager.mutex.RUnlock()
|
||
|
||
results := make([]string, len(plains))
|
||
for i, plain := range plains {
|
||
// 生成随机nonce
|
||
nonce := make([]byte, gcm.NonceSize())
|
||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 加密
|
||
ciphertext := gcm.Seal(nil, nonce, []byte(plain), nil)
|
||
buf := append(nonce, ciphertext...)
|
||
results[i] = base64.StdEncoding.EncodeToString(buf)
|
||
}
|
||
return results, nil
|
||
}
|
||
|
||
// DecryptStringBatch 批量解密字符串
|
||
// 减少锁竞争,提高批量处理性能
|
||
func DecryptStringBatch(encs []string) ([]string, error) {
|
||
if err := cryptoManager.initCrypto(); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
cryptoManager.mutex.RLock()
|
||
gcm := cryptoManager.gcm
|
||
cryptoManager.mutex.RUnlock()
|
||
|
||
results := make([]string, len(encs))
|
||
for i, enc := range encs {
|
||
// 解码base64
|
||
data, err := base64.StdEncoding.DecodeString(enc)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 检查数据长度
|
||
if len(data) < gcm.NonceSize() {
|
||
return nil, errors.New("ciphertext too short")
|
||
}
|
||
|
||
// 分离nonce和密文
|
||
nonce := data[:gcm.NonceSize()]
|
||
ciphertext := data[gcm.NonceSize():]
|
||
|
||
// 解密
|
||
plain, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
results[i] = string(plain)
|
||
}
|
||
return results, nil
|
||
}
|
||
|
||
// GenerateRandomSalt 生成随机密码盐值
|
||
// 生成32字节(64个十六进制字符)的随机盐值,用于加密
|
||
// 返回: 十六进制格式的盐值字符串和错误信息
|
||
func GenerateRandomSalt() (string, error) {
|
||
length := 32 // 固定32字节
|
||
|
||
// 生成随机字节
|
||
bytes := make([]byte, length)
|
||
if _, err := io.ReadFull(rand.Reader, bytes); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 转换为十六进制字符串
|
||
return fmt.Sprintf("%x", bytes), nil
|
||
}
|
||
|
||
// EncryptStringWithSalt 使用盐值进行字符串加密(AES-256-GCM)
|
||
// 将明文和盐值组合后进行加密,增强安全性
|
||
// plain: 待加密的明文字符串
|
||
// salt: 加密盐值
|
||
// 返回: base64编码的密文字符串和错误信息
|
||
func EncryptStringWithSalt(plain, salt string) (string, error) {
|
||
if err := cryptoManager.initCrypto(); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
cryptoManager.mutex.RLock()
|
||
gcm := cryptoManager.gcm
|
||
cryptoManager.mutex.RUnlock()
|
||
|
||
// 将明文和盐值组合
|
||
combined := plain + salt
|
||
|
||
// 生成随机nonce
|
||
nonce := make([]byte, gcm.NonceSize())
|
||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 加密
|
||
ciphertext := gcm.Seal(nil, nonce, []byte(combined), nil)
|
||
buf := append(nonce, ciphertext...)
|
||
return base64.StdEncoding.EncodeToString(buf), nil
|
||
}
|
||
|
||
// DecryptStringWithSalt 使用盐值进行字符串解密(AES-256-GCM)
|
||
// 解密密文并移除盐值,返回原始明文
|
||
// enc: base64编码的密文字符串
|
||
// salt: 解密盐值
|
||
// 返回: 解密后的明文字符串和错误信息
|
||
func DecryptStringWithSalt(enc, salt string) (string, error) {
|
||
if err := cryptoManager.initCrypto(); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
cryptoManager.mutex.RLock()
|
||
gcm := cryptoManager.gcm
|
||
cryptoManager.mutex.RUnlock()
|
||
|
||
// 解码base64
|
||
data, err := base64.StdEncoding.DecodeString(enc)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 检查数据长度
|
||
if len(data) < gcm.NonceSize() {
|
||
return "", errors.New("ciphertext too short")
|
||
}
|
||
|
||
// 分离nonce和密文
|
||
nonce := data[:gcm.NonceSize()]
|
||
ciphertext := data[gcm.NonceSize():]
|
||
|
||
// 解密
|
||
plain, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 移除盐值,返回原始明文
|
||
combined := string(plain)
|
||
if len(combined) < len(salt) {
|
||
return "", errors.New("decrypted data too short")
|
||
}
|
||
|
||
// 验证盐值是否匹配
|
||
if combined[len(combined)-len(salt):] != salt {
|
||
return "", errors.New("salt mismatch")
|
||
}
|
||
|
||
return combined[:len(combined)-len(salt)], nil
|
||
}
|
||
|
||
// HashPasswordWithSalt 使用盐值对密码进行哈希处理
|
||
// 将密码和盐值组合后使用bcrypt进行哈希
|
||
// password: 原始密码
|
||
// salt: 密码盐值
|
||
// 返回: bcrypt哈希值和错误信息
|
||
func HashPasswordWithSalt(password, salt string) (string, error) {
|
||
// 将密码和盐值组合
|
||
combined := password + salt
|
||
|
||
// 使用bcrypt进行哈希(成本因子12,平衡安全性和性能)
|
||
hashed, err := bcrypt.GenerateFromPassword([]byte(combined), 12)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return string(hashed), nil
|
||
}
|
||
|
||
// VerifyPasswordWithSalt 验证密码和盐值的组合是否匹配哈希值
|
||
// password: 原始密码
|
||
// salt: 密码盐值
|
||
// hashedPassword: 存储的哈希密码
|
||
// 返回: 验证结果(true表示匹配)
|
||
func VerifyPasswordWithSalt(password, salt, hashedPassword string) bool {
|
||
// 将密码和盐值组合
|
||
combined := password + salt
|
||
|
||
// 使用bcrypt验证
|
||
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(combined))
|
||
return err == nil
|
||
}
|