Files
junhong_cmp_fiber/internal/gateway/crypto.go
2026-01-30 17:05:44 +08:00

90 lines
2.4 KiB
Go
Raw 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 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("&timestamp=")
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
}