Files
backend/internal/pkg/crypto/message_encryptor.go
lafay b028f7e1d3 feat: enhance logging and message encryption features
- Introduced new logging services: operation logs, login logs, and data change logs.
- Added support for message content encryption in the Message model, including methods for encrypting and decrypting message segments.
- Updated router to include admin log handling and integrated log service for access logging.
- Modified configuration to support encryption settings.
2026-03-15 20:38:22 +08:00

174 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
"sync"
)
var (
ErrInvalidKey = errors.New("invalid encryption key: must be 32 bytes")
ErrInvalidCiphertext = errors.New("invalid ciphertext: too short")
ErrDecryptFailed = errors.New("decryption failed")
)
// MessageEncryptor 消息加密器
// 使用 AES-256-GCM 算法进行加密
type MessageEncryptor struct {
key []byte
gcm cipher.AEAD
keyVersion int
mu sync.RWMutex
}
// globalEncryptor 全局加密器实例
var globalEncryptor *MessageEncryptor
var encryptorOnce sync.Once
// InitMessageEncryptor 初始化全局消息加密器
// key 必须是32字节256位的密钥
func InitMessageEncryptor(key string, keyVersion int) error {
keyBytes := []byte(key)
if len(keyBytes) != 32 {
return ErrInvalidKey
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return err
}
encryptorOnce.Do(func() {
globalEncryptor = &MessageEncryptor{
key: keyBytes,
gcm: gcm,
keyVersion: keyVersion,
}
})
return nil
}
// GetMessageEncryptor 获取全局加密器实例
func GetMessageEncryptor() *MessageEncryptor {
return globalEncryptor
}
// Encrypt 加密数据
// 返回 base64 编码的密文格式nonce + ciphertext + tag
func (e *MessageEncryptor) Encrypt(plaintext []byte) (string, error) {
if e == nil {
return "", errors.New("encryptor not initialized")
}
if len(plaintext) == 0 {
return "", nil
}
e.mu.RLock()
defer e.mu.RUnlock()
// 生成随机nonce12字节
nonce := make([]byte, e.gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
// 加密数据nonce附加在密文前面
ciphertext := e.gcm.Seal(nonce, nonce, plaintext, nil)
// 返回base64编码的密文
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// Decrypt 解密数据
// 输入 base64 编码的密文
func (e *MessageEncryptor) Decrypt(ciphertextBase64 string) ([]byte, error) {
if e == nil {
return nil, errors.New("encryptor not initialized")
}
if ciphertextBase64 == "" {
return nil, nil
}
e.mu.RLock()
defer e.mu.RUnlock()
// base64解码
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)
if err != nil {
return nil, err
}
// 检查密文长度
nonceSize := e.gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, ErrInvalidCiphertext
}
// 提取nonce和实际密文
nonce := ciphertext[:nonceSize]
actualCiphertext := ciphertext[nonceSize:]
// 解密
plaintext, err := e.gcm.Open(nil, nonce, actualCiphertext, nil)
if err != nil {
return nil, ErrDecryptFailed
}
return plaintext, nil
}
// GetKeyVersion 获取当前密钥版本
func (e *MessageEncryptor) GetKeyVersion() int {
if e == nil {
return 0
}
e.mu.RLock()
defer e.mu.RUnlock()
return e.keyVersion
}
// RotateKey 密钥轮换(用于密钥升级)
// 新密钥必须也是32字节
func (e *MessageEncryptor) RotateKey(newKey string, newVersion int) error {
keyBytes := []byte(newKey)
if len(keyBytes) != 32 {
return ErrInvalidKey
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return err
}
e.mu.Lock()
defer e.mu.Unlock()
e.key = keyBytes
e.gcm = gcm
e.keyVersion = newVersion
return nil
}
// IsInitialized 检查加密器是否已初始化
func IsInitialized() bool {
return globalEncryptor != nil
}