88 lines
2.7 KiB
Go
88 lines
2.7 KiB
Go
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"`
|
||
}
|