chore: apply task changes
This commit is contained in:
89
internal/gateway/crypto.go
Normal file
89
internal/gateway/crypto.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user