package crypto import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "encoding/base64" "errors" "io" ) // Encryptor 加密器接口 type Encryptor interface { Encrypt(plaintext string) (string, error) Decrypt(ciphertext string) (string, error) } // AESEncryptor AES 加密器 type AESEncryptor struct { key []byte } // NewAESEncryptor 创建 AES 加密器 // key: 加密密钥(会自动派生为32字节密钥) func NewAESEncryptor(key string) *AESEncryptor { // 使用 SHA256 派生固定长度的密钥 hash := sha256.Sum256([]byte(key)) return &AESEncryptor{ key: hash[:], } } // Encrypt 加密字符串 // 返回 Base64 编码的密文 func (e *AESEncryptor) Encrypt(plaintext string) (string, error) { if plaintext == "" { return "", nil } block, err := aes.NewCipher(e.key) if err != nil { return "", err } // 创建 GCM 模式 gcm, err := cipher.NewGCM(block) if err != nil { return "", err } // 生成随机 nonce nonce := make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return "", err } // 加密(nonce 会自动附加到密文前面) ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil) // Base64 编码 return base64.StdEncoding.EncodeToString(ciphertext), nil } // Decrypt 解密字符串 // 输入为 Base64 编码的密文 func (e *AESEncryptor) Decrypt(ciphertext string) (string, error) { if ciphertext == "" { return "", nil } // Base64 解码 data, err := base64.StdEncoding.DecodeString(ciphertext) if err != nil { return "", err } block, err := aes.NewCipher(e.key) if err != nil { return "", err } gcm, err := cipher.NewGCM(block) if err != nil { return "", err } nonceSize := gcm.NonceSize() if len(data) < nonceSize { return "", errors.New("ciphertext too short") } // 提取 nonce 和实际密文 nonce, cipherBytes := data[:nonceSize], data[nonceSize:] // 解密 plaintext, err := gcm.Open(nil, nonce, cipherBytes, nil) if err != nil { return "", err } return string(plaintext), nil } // MaskSensitiveData 脱敏显示敏感数据 // 如果数据为空或已加密标记,返回掩码 func MaskSensitiveData(data string) string { if data == "" { return "" } return "••••••••" // 统一返回8个点,不泄露长度信息 } // IsEncrypted 检查字符串是否已加密 // 简单检查:加密后的数据是 Base64 格式且长度较长 func IsEncrypted(data string) bool { if data == "" { return false } // 加密后的数据至少有 nonce(12) + tag(16) + 内容,Base64后会更长 if len(data) < 40 { return false } // 尝试 Base64 解码 _, err := base64.StdEncoding.DecodeString(data) return err == nil }