// Package wecom 提供企业微信外部系统 Adapter。 package wecom import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "io" "github.com/break/junhong_cmp_fiber/pkg/errors" ) var credentialAAD = []byte("junhong-wecom-credential-v1") // CredentialCipher 使用 AES-256-GCM 加解密企业微信敏感凭据。 type CredentialCipher struct { aead cipher.AEAD } // NewCredentialCipher 从 32 字节 Base64 密钥创建凭据加密器。 func NewCredentialCipher(encodedKey string) (*CredentialCipher, error) { key, err := base64.StdEncoding.DecodeString(encodedKey) if err != nil || len(key) != 32 { return nil, errors.New(errors.CodeWeComCredentialInvalid, "企业微信凭据加密密钥必须是 32 字节 Base64") } block, err := aes.NewCipher(key) if err != nil { return nil, errors.Wrap(errors.CodeWeComCredentialInvalid, err, "初始化企业微信凭据加密器失败") } aead, err := cipher.NewGCM(block) if err != nil { return nil, errors.Wrap(errors.CodeWeComCredentialInvalid, err, "初始化企业微信凭据加密模式失败") } return &CredentialCipher{aead: aead}, nil } // Encrypt 加密单个敏感凭据,返回 nonce 与密文组合。 func (c *CredentialCipher) Encrypt(plaintext string) ([]byte, error) { if c == nil || c.aead == nil || plaintext == "" { return nil, errors.New(errors.CodeWeComCredentialInvalid) } nonce := make([]byte, c.aead.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, errors.Wrap(errors.CodeInternalError, err, "生成企业微信凭据随机数失败") } return c.aead.Seal(nonce, nonce, []byte(plaintext), credentialAAD), nil } // Decrypt 解密单个企业微信敏感凭据。 func (c *CredentialCipher) Decrypt(ciphertext []byte) (string, error) { if c == nil || c.aead == nil || len(ciphertext) <= c.aead.NonceSize() { return "", errors.New(errors.CodeWeComCredentialInvalid) } nonce := ciphertext[:c.aead.NonceSize()] plaintext, err := c.aead.Open(nil, nonce, ciphertext[c.aead.NonceSize():], credentialAAD) if err != nil { return "", errors.Wrap(errors.CodeWeComCredentialInvalid, err, "解密企业微信凭据失败") } return string(plaintext), nil }