You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
2.2 KiB
75 lines
2.2 KiB
package comm
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
cryptorand "crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// AES-256-GCM 字段加密:console 后台与业务服务共用的单一实现。
|
|
//
|
|
// 「应用的服务配置」(app_service_config) 由 console 前端写、业务服务启动时读/回写,两端读写同一张加密表,
|
|
// 故加密算法必须完全一致——统一收敛到本文件(console 的 svcEncrypt/svcDecrypt、业务侧 seed 均委托这里),
|
|
// 避免各处复制导致密文格式漂移。密钥由 ${FIELD_ENCRYPT_KEY} 注入,console 与业务服务必须配同一值。
|
|
|
|
// EncMask 加密字段返回前端时的脱敏占位符;更新时前端回传该占位/空串则保留原密文。
|
|
const EncMask = "[encrypted]"
|
|
|
|
// normAESKey 把任意长度的密钥规整为 32 字节(AES-256):不足补零、超长截断。
|
|
func normAESKey(key string) []byte {
|
|
k := make([]byte, 32)
|
|
copy(k, []byte(key))
|
|
return k
|
|
}
|
|
|
|
// Encrypt 用 AES-256-GCM 加密明文,返回 base64(nonce‖ciphertext)。空明文返回空串。
|
|
func Encrypt(key, plaintext string) (string, error) {
|
|
if plaintext == "" {
|
|
return "", nil
|
|
}
|
|
block, err := aes.NewCipher(normAESKey(key))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
nonce := make([]byte, gcm.NonceSize())
|
|
if _, err = io.ReadFull(cryptorand.Reader, nonce); err != nil {
|
|
return "", err
|
|
}
|
|
ct := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
|
|
return base64.StdEncoding.EncodeToString(ct), nil
|
|
}
|
|
|
|
// Decrypt 解密 Encrypt 产出的密文,返回明文。
|
|
func Decrypt(key, ciphertext string) (string, error) {
|
|
if ciphertext == "" {
|
|
return "", nil
|
|
}
|
|
raw, err := base64.StdEncoding.DecodeString(ciphertext)
|
|
if err != nil {
|
|
return "", fmt.Errorf("base64 解码失败: %w", err)
|
|
}
|
|
block, err := aes.NewCipher(normAESKey(key))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ns := gcm.NonceSize()
|
|
if len(raw) < ns {
|
|
return "", fmt.Errorf("密文过短")
|
|
}
|
|
plain, err := gcm.Open(nil, raw[:ns], raw[ns:], nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("GCM 解密失败: %w", err)
|
|
}
|
|
return string(plain), nil
|
|
}
|
|
|