voucher/internal/pkg/wechat/utils/aes.go

88 lines
2.7 KiB
Go
Raw Permalink 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 utils
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
)
const (
keyLengthByte = 32 // AES-256 密钥长度(字节)
authTagLengthByte = 16 // GCM 认证标签长度(字节)
)
// AesUtil 用于微信支付 AES-256-GCM 解密的工具类
type AesUtil struct {
aesKey []byte // 32字节的AES密钥
}
// NewAesUtil 创建AesUtil实例验证密钥长度
func NewAesUtil(aesKey string) (*AesUtil, error) {
if len(aesKey) != keyLengthByte {
return nil, errors.New("无效的ApiV3Key长度应为32个字节")
}
return &AesUtil{aesKey: []byte(aesKey)}, nil
}
// DecryptToString 解密 AEAD_AES_256_GCM 加密的数据
// associatedData: 附加认证数据
// nonceStr: 随机数12字节
// ciphertext: 加密后的密文Base64编码
func (a *AesUtil) DecryptToString(associatedData, nonceStr, ciphertext string) (string, error) {
// 1. Base64解码密文
cipherBytes, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", fmt.Errorf("密文Base64解码失败: %w", err)
}
// 2. 验证密文长度(需包含认证标签)
if len(cipherBytes) <= authTagLengthByte {
return "", errors.New("密文长度不足,无法解析认证标签")
}
// 3. 分离密文和认证标签GCM模式中认证标签通常附加在密文末尾
ctext := cipherBytes[:len(cipherBytes)-authTagLengthByte]
authTag := cipherBytes[len(cipherBytes)-authTagLengthByte:]
// 4. 初始化AES-GCM加密器
block, err := aes.NewCipher(a.aesKey)
if err != nil {
return "", fmt.Errorf("创建AES加密器失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("创建GCM模式失败: %w", err)
}
// 5. 构建附加数据GCM的AAD
additionalData := []byte(associatedData)
// 6. 解密GCM模式会自动验证认证标签
plaintext, err := gcm.Open(nil, []byte(nonceStr), append(ctext, authTag...), additionalData)
if err != nil {
return "", fmt.Errorf("解密失败(可能是密钥错误或数据被篡改): %w", err)
}
return string(plaintext), nil
}
type Resource struct {
OriginalType string `json:"original_type"`
Algorithm string `json:"algorithm"`
Ciphertext string `json:"ciphertext"` // 如支付通知中的 encrypted_data
AssociatedData string `json:"associated_data"` // 微信支付回调的附加数据 通常为空字符串或回调相关信息
Nonce string `json:"nonce"` // 12字节字符串
}
type WxNotifyBody struct {
Id string `json:"id"`
CreateTime string `json:"create_time"`
ResourceType string `json:"resource_type"`
EventType string `json:"event_type"`
Summary string `json:"summary"`
Resource Resource `json:"resource"`
}