f
This commit is contained in:
113
internal/shared/crypto/crypto.go
Normal file
113
internal/shared/crypto/crypto.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PKCS7填充
|
||||
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
// 去除PKCS7填充
|
||||
func PKCS7UnPadding(origData []byte) ([]byte, error) {
|
||||
length := len(origData)
|
||||
if length == 0 {
|
||||
return nil, errors.New("input data error")
|
||||
}
|
||||
unpadding := int(origData[length-1])
|
||||
if unpadding > length {
|
||||
return nil, errors.New("unpadding size is invalid")
|
||||
}
|
||||
|
||||
// 检查填充字节是否一致
|
||||
for i := 0; i < unpadding; i++ {
|
||||
if origData[length-1-i] != byte(unpadding) {
|
||||
return nil, errors.New("invalid padding")
|
||||
}
|
||||
}
|
||||
|
||||
return origData[:(length - unpadding)], nil
|
||||
}
|
||||
|
||||
// AES CBC模式加密,Base64传入传出
|
||||
func AesEncrypt(plainText []byte, key string) (string, error) {
|
||||
keyBytes, err := hex.DecodeString(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
block, err := aes.NewCipher(keyBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
blockSize := block.BlockSize()
|
||||
plainText = PKCS7Padding(plainText, blockSize)
|
||||
|
||||
cipherText := make([]byte, blockSize+len(plainText))
|
||||
iv := cipherText[:blockSize] // 使用前blockSize字节作为IV
|
||||
_, err = io.ReadFull(rand.Reader, iv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText[blockSize:], plainText)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(cipherText), nil
|
||||
}
|
||||
|
||||
// AES CBC模式解密,Base64传入传出
|
||||
func AesDecrypt(cipherTextBase64 string, key string) ([]byte, error) {
|
||||
keyBytes, err := hex.DecodeString(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(keyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockSize := block.BlockSize()
|
||||
if len(cipherText) < blockSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
iv := cipherText[:blockSize]
|
||||
cipherText = cipherText[blockSize:]
|
||||
|
||||
if len(cipherText)%blockSize != 0 {
|
||||
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
|
||||
plainText, err := PKCS7UnPadding(cipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plainText, nil
|
||||
}
|
||||
|
||||
// Md5Encrypt 用于对传入的message进行MD5加密
|
||||
func Md5Encrypt(message string) string {
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(message)) // 将字符串转换为字节切片并写入
|
||||
return hex.EncodeToString(hash.Sum(nil)) // 将哈希值转换为16进制字符串并返回
|
||||
}
|
||||
63
internal/shared/crypto/generate.go
Normal file
63
internal/shared/crypto/generate.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
mathrand "math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 生成AES-128密钥的函数,符合市面规范
|
||||
func GenerateSecretKey() (string, error) {
|
||||
key := make([]byte, 16) // 16字节密钥
|
||||
_, err := io.ReadFull(rand.Reader, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(key), nil
|
||||
}
|
||||
|
||||
func GenerateSecretId() (string, error) {
|
||||
// 创建一个字节数组,用于存储随机数据
|
||||
bytes := make([]byte, 8) // 因为每个字节表示两个16进制字符
|
||||
|
||||
// 读取随机字节到数组中
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 将字节数组转换为16进制字符串
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
// GenerateTransactionID 生成16位数的交易单号
|
||||
func GenerateTransactionID() string {
|
||||
length := 16
|
||||
// 获取当前时间戳
|
||||
timestamp := time.Now().UnixNano()
|
||||
|
||||
// 转换为字符串
|
||||
timeStr := strconv.FormatInt(timestamp, 10)
|
||||
|
||||
// 生成随机数
|
||||
mathrand.Seed(time.Now().UnixNano())
|
||||
randomPart := strconv.Itoa(mathrand.Intn(1000000))
|
||||
|
||||
// 组合时间戳和随机数
|
||||
combined := timeStr + randomPart
|
||||
|
||||
// 如果长度超出指定值,则截断;如果不够,则填充随机字符
|
||||
if len(combined) >= length {
|
||||
return combined[:length]
|
||||
}
|
||||
|
||||
// 如果长度不够,填充0
|
||||
for len(combined) < length {
|
||||
combined += strconv.Itoa(mathrand.Intn(10)) // 填充随机数
|
||||
}
|
||||
|
||||
return combined
|
||||
}
|
||||
357
internal/shared/crypto/signature.go
Normal file
357
internal/shared/crypto/signature.go
Normal file
@@ -0,0 +1,357 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
const (
|
||||
// SignatureTimestampTolerance 签名时间戳容差(秒),防止重放攻击
|
||||
SignatureTimestampTolerance = 300 // 5分钟
|
||||
)
|
||||
|
||||
// GenerateSignature 生成HMAC-SHA256签名
|
||||
// params: 需要签名的参数map
|
||||
// secretKey: 签名密钥
|
||||
// timestamp: 时间戳(秒)
|
||||
// nonce: 随机字符串
|
||||
func GenerateSignature(params map[string]string, secretKey string, timestamp int64, nonce string) string {
|
||||
// 1. 构建待签名字符串:按key排序,拼接成 key1=value1&key2=value2 格式
|
||||
var keys []string
|
||||
for k := range params {
|
||||
if k != "signature" { // 排除签名字段本身
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var parts []string
|
||||
for _, k := range keys {
|
||||
parts = append(parts, fmt.Sprintf("%s=%s", k, params[k]))
|
||||
}
|
||||
|
||||
// 2. 添加时间戳和随机数
|
||||
parts = append(parts, fmt.Sprintf("timestamp=%d", timestamp))
|
||||
parts = append(parts, fmt.Sprintf("nonce=%s", nonce))
|
||||
|
||||
// 3. 拼接成待签名字符串
|
||||
signString := strings.Join(parts, "&")
|
||||
|
||||
// 4. 使用HMAC-SHA256计算签名
|
||||
mac := hmac.New(sha256.New, []byte(secretKey))
|
||||
mac.Write([]byte(signString))
|
||||
signature := mac.Sum(nil)
|
||||
|
||||
// 5. 返回hex编码的签名
|
||||
return hex.EncodeToString(signature)
|
||||
}
|
||||
|
||||
// VerifySignature 验证HMAC-SHA256签名
|
||||
// params: 请求参数map(包含signature字段)
|
||||
// secretKey: 签名密钥
|
||||
// timestamp: 时间戳(秒)
|
||||
// nonce: 随机字符串
|
||||
func VerifySignature(params map[string]string, secretKey string, timestamp int64, nonce string) error {
|
||||
// 1. 检查签名字段是否存在
|
||||
signature, exists := params["signature"]
|
||||
if !exists || signature == "" {
|
||||
return errors.New("签名字段缺失")
|
||||
}
|
||||
|
||||
// 2. 验证时间戳(防止重放攻击)
|
||||
now := time.Now().Unix()
|
||||
if timestamp <= 0 {
|
||||
return errors.New("时间戳无效")
|
||||
}
|
||||
if abs(now-timestamp) > SignatureTimestampTolerance {
|
||||
return fmt.Errorf("请求已过期,时间戳超出容差范围(当前时间:%d,请求时间:%d)", now, timestamp)
|
||||
}
|
||||
|
||||
// 3. 重新计算签名
|
||||
expectedSignature := GenerateSignature(params, secretKey, timestamp, nonce)
|
||||
|
||||
// 4. 将hex字符串转换为字节数组进行比较
|
||||
signatureBytes, err := hex.DecodeString(signature)
|
||||
if err != nil {
|
||||
return fmt.Errorf("签名格式错误: %w", err)
|
||||
}
|
||||
expectedBytes, err := hex.DecodeString(expectedSignature)
|
||||
if err != nil {
|
||||
return fmt.Errorf("签名计算错误: %w", err)
|
||||
}
|
||||
|
||||
// 5. 使用常量时间比较防止时序攻击
|
||||
if !hmac.Equal(signatureBytes, expectedBytes) {
|
||||
return errors.New("签名验证失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignatureWithNonceCheck 验证HMAC-SHA256签名并检查nonce唯一性(防止重放攻击)
|
||||
// params: 请求参数map(包含signature字段)
|
||||
// secretKey: 签名密钥
|
||||
// timestamp: 时间戳(秒)
|
||||
// nonce: 随机字符串
|
||||
// cache: 缓存服务,用于存储已使用的nonce
|
||||
// cacheKeyPrefix: 缓存键前缀
|
||||
func VerifySignatureWithNonceCheck(
|
||||
ctx context.Context,
|
||||
params map[string]string,
|
||||
secretKey string,
|
||||
timestamp int64,
|
||||
nonce string,
|
||||
cache interfaces.CacheService,
|
||||
cacheKeyPrefix string,
|
||||
) error {
|
||||
// 1. 先进行基础签名验证
|
||||
if err := VerifySignature(params, secretKey, timestamp, nonce); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 检查nonce是否已被使用(防止重放攻击)
|
||||
// 使用请求指纹:phone+timestamp+nonce 作为唯一标识
|
||||
phone := params["phone"]
|
||||
if phone == "" {
|
||||
return errors.New("手机号不能为空")
|
||||
}
|
||||
|
||||
// 构建nonce唯一性检查的缓存键
|
||||
nonceKey := fmt.Sprintf("%s:nonce:%s:%d:%s", cacheKeyPrefix, phone, timestamp, nonce)
|
||||
|
||||
// 检查nonce是否已被使用
|
||||
exists, err := cache.Exists(ctx, nonceKey)
|
||||
if err != nil {
|
||||
// 缓存查询失败,记录错误但继续验证(避免缓存故障导致服务不可用)
|
||||
return fmt.Errorf("检查nonce唯一性失败: %w", err)
|
||||
}
|
||||
if exists {
|
||||
return errors.New("请求已被使用,请勿重复提交")
|
||||
}
|
||||
|
||||
// 3. 将nonce标记为已使用,TTL设置为时间戳容差+1分钟(确保在容差范围内不会重复使用)
|
||||
ttl := time.Duration(SignatureTimestampTolerance+60) * time.Second
|
||||
if err := cache.Set(ctx, nonceKey, true, ttl); err != nil {
|
||||
// 记录错误但不影响验证流程(避免缓存故障导致服务不可用)
|
||||
return fmt.Errorf("标记nonce已使用失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 自定义编码字符集(不使用标准Base64字符集,增加破解难度)
|
||||
// 使用自定义字符集:数字+大写字母(排除易混淆的I和O)+小写字母(排除易混淆的i和l)+特殊字符
|
||||
const customEncodeCharset = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
|
||||
// EncodeRequest 使用自定义编码方案编码请求参数
|
||||
// 编码方式:类似Base64,但使用自定义字符集,并加入简单的混淆
|
||||
func EncodeRequest(data string) string {
|
||||
// 1. 将字符串转换为字节数组
|
||||
bytes := []byte(data)
|
||||
|
||||
// 2. 使用自定义Base64变种编码
|
||||
encoded := customBase64Encode(bytes)
|
||||
|
||||
// 3. 添加简单的字符混淆(字符偏移)
|
||||
confused := applyCharShift(encoded, 7) // 偏移7个位置
|
||||
|
||||
return confused
|
||||
}
|
||||
|
||||
// DecodeRequest 解码请求参数
|
||||
func DecodeRequest(encodedData string) (string, error) {
|
||||
// 1. 先还原字符混淆
|
||||
unconfused := reverseCharShift(encodedData, 7)
|
||||
|
||||
// 2. 使用自定义Base64变种解码
|
||||
decoded, err := customBase64Decode(unconfused)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解码失败: %w", err)
|
||||
}
|
||||
|
||||
return string(decoded), nil
|
||||
}
|
||||
|
||||
// customBase64Encode 自定义Base64编码(使用自定义字符集)
|
||||
func customBase64Encode(data []byte) string {
|
||||
if len(data) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result []byte
|
||||
charset := []byte(customEncodeCharset)
|
||||
|
||||
// 将3个字节(24位)编码为4个字符
|
||||
for i := 0; i < len(data); i += 3 {
|
||||
// 读取3个字节
|
||||
var b1, b2, b3 byte
|
||||
b1 = data[i]
|
||||
if i+1 < len(data) {
|
||||
b2 = data[i+1]
|
||||
}
|
||||
if i+2 < len(data) {
|
||||
b3 = data[i+2]
|
||||
}
|
||||
|
||||
// 组合成24位
|
||||
combined := uint32(b1)<<16 | uint32(b2)<<8 | uint32(b3)
|
||||
|
||||
// 分成4个6位段
|
||||
result = append(result, charset[(combined>>18)&0x3F])
|
||||
result = append(result, charset[(combined>>12)&0x3F])
|
||||
|
||||
if i+1 < len(data) {
|
||||
result = append(result, charset[(combined>>6)&0x3F])
|
||||
} else {
|
||||
result = append(result, '=') // 填充字符
|
||||
}
|
||||
|
||||
if i+2 < len(data) {
|
||||
result = append(result, charset[combined&0x3F])
|
||||
} else {
|
||||
result = append(result, '=') // 填充字符
|
||||
}
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// customBase64Decode 自定义Base64解码
|
||||
func customBase64Decode(encoded string) ([]byte, error) {
|
||||
if len(encoded) == 0 {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
charset := []byte(customEncodeCharset)
|
||||
charsetMap := make(map[byte]int)
|
||||
for i, c := range charset {
|
||||
charsetMap[c] = i
|
||||
}
|
||||
|
||||
var result []byte
|
||||
data := []byte(encoded)
|
||||
|
||||
// 将4个字符解码为3个字节
|
||||
for i := 0; i < len(data); i += 4 {
|
||||
if i+3 >= len(data) {
|
||||
return nil, fmt.Errorf("编码数据长度不正确")
|
||||
}
|
||||
|
||||
// 获取4个字符的索引
|
||||
var idx [4]int
|
||||
for j := 0; j < 4; j++ {
|
||||
if data[i+j] == '=' {
|
||||
idx[j] = 0 // 填充字符
|
||||
} else {
|
||||
val, ok := charsetMap[data[i+j]]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("无效的编码字符: %c", data[i+j])
|
||||
}
|
||||
idx[j] = val
|
||||
}
|
||||
}
|
||||
|
||||
// 组合成24位
|
||||
combined := uint32(idx[0])<<18 | uint32(idx[1])<<12 | uint32(idx[2])<<6 | uint32(idx[3])
|
||||
|
||||
// 提取3个字节
|
||||
result = append(result, byte((combined>>16)&0xFF))
|
||||
if data[i+2] != '=' {
|
||||
result = append(result, byte((combined>>8)&0xFF))
|
||||
}
|
||||
if data[i+3] != '=' {
|
||||
result = append(result, byte(combined&0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// applyCharShift 应用字符偏移混淆
|
||||
func applyCharShift(data string, shift int) string {
|
||||
charset := customEncodeCharset
|
||||
charsetLen := len(charset)
|
||||
result := make([]byte, len(data))
|
||||
|
||||
for i, c := range []byte(data) {
|
||||
if c == '=' {
|
||||
result[i] = c // 填充字符不变
|
||||
continue
|
||||
}
|
||||
|
||||
// 查找字符在字符集中的位置
|
||||
idx := -1
|
||||
for j, ch := range []byte(charset) {
|
||||
if ch == c {
|
||||
idx = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
result[i] = c // 不在字符集中,保持不变
|
||||
} else {
|
||||
// 应用偏移
|
||||
newIdx := (idx + shift) % charsetLen
|
||||
result[i] = charset[newIdx]
|
||||
}
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// reverseCharShift 还原字符偏移混淆
|
||||
func reverseCharShift(data string, shift int) string {
|
||||
charset := customEncodeCharset
|
||||
charsetLen := len(charset)
|
||||
result := make([]byte, len(data))
|
||||
|
||||
for i, c := range []byte(data) {
|
||||
if c == '=' {
|
||||
result[i] = c // 填充字符不变
|
||||
continue
|
||||
}
|
||||
|
||||
// 查找字符在字符集中的位置
|
||||
idx := -1
|
||||
for j, ch := range []byte(charset) {
|
||||
if ch == c {
|
||||
idx = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
result[i] = c // 不在字符集中,保持不变
|
||||
} else {
|
||||
// 还原偏移
|
||||
newIdx := (idx - shift + charsetLen) % charsetLen
|
||||
result[i] = charset[newIdx]
|
||||
}
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// abs 计算绝对值
|
||||
func abs(x int64) int64 {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// ParseTimestamp 从字符串解析时间戳
|
||||
func ParseTimestamp(ts string) (int64, error) {
|
||||
return strconv.ParseInt(ts, 10, 64)
|
||||
}
|
||||
150
internal/shared/crypto/west_crypto.go
Normal file
150
internal/shared/crypto/west_crypto.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
const (
|
||||
KEY_SIZE = 16 // AES-128, 16 bytes
|
||||
)
|
||||
|
||||
// Encrypt encrypts the given data using AES encryption in ECB mode with PKCS5 padding
|
||||
func WestDexEncrypt(data, secretKey string) (string, error) {
|
||||
key := generateAESKey(KEY_SIZE*8, []byte(secretKey))
|
||||
ciphertext, err := aesEncrypt([]byte(data), key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given base64-encoded string using AES encryption in ECB mode with PKCS5 padding
|
||||
func WestDexDecrypt(encodedData, secretKey string) ([]byte, error) {
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(encodedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := generateAESKey(KEY_SIZE*8, []byte(secretKey))
|
||||
plaintext, err := aesDecrypt(ciphertext, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// generateAESKey generates a key for AES encryption using a SHA-1 based PRNG
|
||||
func generateAESKey(length int, password []byte) []byte {
|
||||
h := sha1.New()
|
||||
h.Write(password)
|
||||
state := h.Sum(nil)
|
||||
|
||||
keyBytes := make([]byte, 0, length/8)
|
||||
for len(keyBytes) < length/8 {
|
||||
h := sha1.New()
|
||||
h.Write(state)
|
||||
state = h.Sum(nil)
|
||||
keyBytes = append(keyBytes, state...)
|
||||
}
|
||||
|
||||
return keyBytes[:length/8]
|
||||
}
|
||||
|
||||
// aesEncrypt encrypts plaintext using AES in ECB mode with PKCS5 padding
|
||||
func aesEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paddedPlaintext := pkcs5Padding(plaintext, block.BlockSize())
|
||||
ciphertext := make([]byte, len(paddedPlaintext))
|
||||
mode := newECBEncrypter(block)
|
||||
mode.CryptBlocks(ciphertext, paddedPlaintext)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// aesDecrypt decrypts ciphertext using AES in ECB mode with PKCS5 padding
|
||||
func aesDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
mode := newECBDecrypter(block)
|
||||
mode.CryptBlocks(plaintext, ciphertext)
|
||||
return pkcs5Unpadding(plaintext), nil
|
||||
}
|
||||
|
||||
// pkcs5Padding pads the input to a multiple of the block size using PKCS5 padding
|
||||
func pkcs5Padding(src []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(src)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(src, padtext...)
|
||||
}
|
||||
|
||||
// pkcs5Unpadding removes PKCS5 padding from the input
|
||||
func pkcs5Unpadding(src []byte) []byte {
|
||||
length := len(src)
|
||||
unpadding := int(src[length-1])
|
||||
return src[:(length - unpadding)]
|
||||
}
|
||||
|
||||
// ECB mode encryption/decryption
|
||||
type ecb struct {
|
||||
b cipher.Block
|
||||
blockSize int
|
||||
}
|
||||
|
||||
func newECB(b cipher.Block) *ecb {
|
||||
return &ecb{
|
||||
b: b,
|
||||
blockSize: b.BlockSize(),
|
||||
}
|
||||
}
|
||||
|
||||
type ecbEncrypter ecb
|
||||
|
||||
func newECBEncrypter(b cipher.Block) cipher.BlockMode {
|
||||
return (*ecbEncrypter)(newECB(b))
|
||||
}
|
||||
|
||||
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
|
||||
|
||||
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
panic("crypto/cipher: input not full blocks")
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
}
|
||||
for len(src) > 0 {
|
||||
x.b.Encrypt(dst, src[:x.blockSize])
|
||||
src = src[x.blockSize:]
|
||||
dst = dst[x.blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
type ecbDecrypter ecb
|
||||
|
||||
func newECBDecrypter(b cipher.Block) cipher.BlockMode {
|
||||
return (*ecbDecrypter)(newECB(b))
|
||||
}
|
||||
|
||||
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
|
||||
|
||||
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
panic("crypto/cipher: input not full blocks")
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
}
|
||||
for len(src) > 0 {
|
||||
x.b.Decrypt(dst, src[:x.blockSize])
|
||||
src = src[x.blockSize:]
|
||||
dst = dst[x.blockSize:]
|
||||
}
|
||||
}
|
||||
213
internal/shared/crypto/west_crypto_test.go
Normal file
213
internal/shared/crypto/west_crypto_test.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWestDexEncryptDecrypt(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data string
|
||||
secretKey string
|
||||
}{
|
||||
{
|
||||
name: "简单文本",
|
||||
data: "hello world",
|
||||
secretKey: "mySecretKey123",
|
||||
},
|
||||
{
|
||||
name: "中文文本",
|
||||
data: "你好世界",
|
||||
secretKey: "中文密钥",
|
||||
},
|
||||
{
|
||||
name: "JSON数据",
|
||||
data: `{"name":"张三","age":30,"city":"北京"}`,
|
||||
secretKey: "jsonSecretKey",
|
||||
},
|
||||
{
|
||||
name: "长文本",
|
||||
data: "这是一个很长的文本,用来测试加密解密功能是否正常工作。包含各种字符:123456789!@#$%^&*()_+-=[]{}|;':\",./<>?",
|
||||
secretKey: "longTextKey",
|
||||
},
|
||||
{
|
||||
name: "空字符串",
|
||||
data: "",
|
||||
secretKey: "emptyDataKey",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// 加密
|
||||
encrypted, err := WestDexEncrypt(tc.data, tc.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("原始数据: %s", tc.data)
|
||||
t.Logf("密钥: %s", tc.secretKey)
|
||||
t.Logf("加密结果: %s", encrypted)
|
||||
|
||||
// 解密
|
||||
decrypted, err := WestDexDecrypt(encrypted, tc.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
decryptedStr := string(decrypted)
|
||||
t.Logf("解密结果: %s", decryptedStr)
|
||||
|
||||
// 验证解密结果是否与原始数据一致
|
||||
if decryptedStr != tc.data {
|
||||
t.Errorf("解密结果不匹配: 期望 %s, 实际 %s", tc.data, decryptedStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWestDexDecryptOutput(t *testing.T) {
|
||||
// 专门用来查看解密结果的测试
|
||||
testData := []struct {
|
||||
name string
|
||||
data string
|
||||
secretKey string
|
||||
encryptedData string // 预设的加密数据
|
||||
}{
|
||||
{
|
||||
name: "测试数据1",
|
||||
data: "0IdH/7L/ybMY00dne6clsk7VYBXPHkFfDagilHTzSHt9wTxref38uX8cDe7fJCGksbDQnMGo8GfsyEIpiCfj+w==",
|
||||
secretKey: "121a1e41fc1690dd6b90afbcacd80cf4",
|
||||
},
|
||||
{
|
||||
name: "中文数据",
|
||||
data: "用户数据",
|
||||
secretKey: "密钥123",
|
||||
},
|
||||
{
|
||||
name: "API数据",
|
||||
data: "api_call_data",
|
||||
secretKey: "production_key",
|
||||
},
|
||||
{
|
||||
name: "JSON格式",
|
||||
data: `{"user_id":12345,"name":"张三","status":"active"}`,
|
||||
secretKey: "json_key",
|
||||
},
|
||||
}
|
||||
|
||||
for i, td := range testData {
|
||||
decrypted, err := WestDexDecrypt(td.data, td.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("测试 %d - %s:", i+1, td.name)
|
||||
t.Logf(" 原始数据: %s", td.data)
|
||||
t.Logf(" 使用密钥: %s", td.secretKey)
|
||||
t.Logf(" 解密结果: %s", string(decrypted))
|
||||
t.Logf(" 解密正确: %v", string(decrypted) == td.data)
|
||||
t.Log("---")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecificDecrypt(t *testing.T) {
|
||||
// 如果你有特定的加密数据想要解密,可以在这里测试
|
||||
specificTests := []struct {
|
||||
name string
|
||||
encryptedData string
|
||||
secretKey string
|
||||
expectedData string // 如果知道预期结果的话
|
||||
}{
|
||||
// 示例:如果你有具体的加密数据想要解密,可以添加到这里
|
||||
// {
|
||||
// name: "特定数据解密",
|
||||
// encryptedData: "你的加密数据",
|
||||
// secretKey: "你的密钥",
|
||||
// expectedData: "预期的解密结果",
|
||||
// },
|
||||
}
|
||||
|
||||
t.Log("=== 特定数据解密测试 ===")
|
||||
for _, test := range specificTests {
|
||||
decrypted, err := WestDexDecrypt(test.encryptedData, test.secretKey)
|
||||
if err != nil {
|
||||
t.Logf("%s - 解密失败: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
result := string(decrypted)
|
||||
t.Logf("%s:", test.name)
|
||||
t.Logf(" 加密数据: %s", test.encryptedData)
|
||||
t.Logf(" 使用密钥: %s", test.secretKey)
|
||||
t.Logf(" 解密结果: %s", result)
|
||||
|
||||
if test.expectedData != "" {
|
||||
t.Logf(" 预期结果: %s", test.expectedData)
|
||||
t.Logf(" 解密正确: %v", result == test.expectedData)
|
||||
}
|
||||
t.Log("---")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWestDexDecryptWithWrongKey(t *testing.T) {
|
||||
// 测试用错误密钥解密
|
||||
data := "sensitive data"
|
||||
correctKey := "correct_key"
|
||||
wrongKey := "wrong_key"
|
||||
|
||||
// 用正确密钥加密
|
||||
encrypted, err := WestDexEncrypt(data, correctKey)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 用错误密钥解密
|
||||
decrypted, err := WestDexDecrypt(encrypted, wrongKey)
|
||||
if err != nil {
|
||||
t.Logf("用错误密钥解密失败(这是预期的): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
decryptedStr := string(decrypted)
|
||||
t.Logf("原始数据: %s", data)
|
||||
t.Logf("用错误密钥解密结果: %s", decryptedStr)
|
||||
|
||||
// 验证解密结果应该与原始数据不同
|
||||
if decryptedStr == data {
|
||||
t.Error("用错误密钥解密不应该得到正确结果")
|
||||
}
|
||||
}
|
||||
|
||||
// 基准测试
|
||||
func BenchmarkWestDexEncrypt(b *testing.B) {
|
||||
data := "这是一个用于基准测试的数据字符串"
|
||||
secretKey := "benchmarkKey"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := WestDexEncrypt(data, secretKey)
|
||||
if err != nil {
|
||||
b.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWestDexDecrypt(b *testing.B) {
|
||||
data := "这是一个用于基准测试的数据字符串"
|
||||
secretKey := "benchmarkKey"
|
||||
|
||||
// 先加密一次获得密文
|
||||
encrypted, err := WestDexEncrypt(data, secretKey)
|
||||
if err != nil {
|
||||
b.Fatalf("预加密失败: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := WestDexDecrypt(encrypted, secretKey)
|
||||
if err != nil {
|
||||
b.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user