Files
ocdp-go/backend/internal/pkg/crypto/crypto.go
mangomqy c5e51ed069 ocdp v1
2025-11-13 02:54:06 +00:00

129 lines
2.7 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"
"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
}