// Package gateway 提供 Gateway API 加密和签名工具函数 package gateway import ( "crypto/aes" "crypto/cipher" "crypto/md5" "encoding/base64" "encoding/hex" "strconv" "strings" "github.com/break/junhong_cmp_fiber/pkg/errors" ) const aesBlockSize = 16 // 注意:AES-ECB 存在严重安全缺陷(相同明文块会产生相同密文块), // 这是 Gateway 强制要求无法改变,生产环境必须使用 HTTPS 保障传输层安全。 func aesEncrypt(data []byte, appSecret string) (string, error) { key := md5.Sum([]byte(appSecret)) block, err := aes.NewCipher(key[:]) if err != nil { return "", errors.Wrap(errors.CodeGatewayEncryptError, err, "数据加密失败") } // 使用 PKCS5 进行填充,确保明文长度为 16 的整数倍 padded := pkcs5Padding(data, aesBlockSize) encrypted := make([]byte, len(padded)) newECBEncrypter(block).CryptBlocks(encrypted, padded) return base64.StdEncoding.EncodeToString(encrypted), nil } // generateSign 生成 Gateway 签名(appId、data、timestamp、key 字母序) func generateSign(appID, encryptedData string, timestamp int64, appSecret string) string { var builder strings.Builder builder.WriteString("appId=") builder.WriteString(appID) builder.WriteString("&data=") builder.WriteString(encryptedData) builder.WriteString("×tamp=") builder.WriteString(strconv.FormatInt(timestamp, 10)) builder.WriteString("&key=") builder.WriteString(appSecret) sum := md5.Sum([]byte(builder.String())) return strings.ToUpper(hex.EncodeToString(sum[:])) } // ecb 表示 AES-ECB 加密模式的基础结构 type ecb struct { b cipher.Block blockSize int } type ecbEncrypter ecb func newECBEncrypter(b cipher.Block) cipher.BlockMode { if b == nil { panic("crypto/cipher: 传入的加密块为空") } return &ecbEncrypter{b: b, blockSize: b.BlockSize()} } func (x *ecbEncrypter) BlockSize() int { return x.blockSize } func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { if len(src)%x.blockSize != 0 { panic("crypto/cipher: 输入数据不是完整块") } for len(src) > 0 { x.b.Encrypt(dst, src[:x.blockSize]) src = src[x.blockSize:] dst = dst[x.blockSize:] } } // pkcs5Padding 对明文进行 PKCS5 填充 func pkcs5Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padded := make([]byte, len(data)+padding) copy(padded, data) for i := len(data); i < len(padded); i++ { padded[i] = byte(padding) } return padded }