package esign import ( "crypto/hmac" "crypto/md5" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "sort" "strconv" "strings" "time" ) // appendSignDataString 拼接待签名字符串 func appendSignDataString(httpMethod, accept, contentMD5, contentType, date, headers, pathAndParameters string) string { if accept == "" { accept = "*/*" } if contentType == "" { contentType = "application/json; charset=UTF-8" } // 前四项 signStr := httpMethod + "\n" + accept + "\n" + contentMD5 + "\n" + contentType + "\n" // 处理 date if date == "" { signStr += "\n" } else { signStr += date + "\n" } // 处理 headers if headers == "" { signStr += pathAndParameters } else { signStr += headers + "\n" + pathAndParameters } return signStr } // generateSignature 生成e签宝API请求签名 // 使用HMAC-SHA256算法对请求参数进行签名 // // 参数说明: // - appSecret: 应用密钥 // - httpMethod: HTTP方法(GET、POST等) // - accept: Accept头值 // - contentMD5: 请求体MD5值 // - contentType: Content-Type头值 // - date: Date头值 // - headers: 自定义头部信息 // - pathAndParameters: 请求路径和参数 // // 返回: Base64编码的签名字符串 func generateSignature(appSecret, httpMethod, accept, contentMD5, contentType, date, headers, pathAndParameters string) string { // 构建待签名字符串,按照e签宝API规范拼接(兼容Python实现细节) signStr := appendSignDataString(httpMethod, accept, contentMD5, contentType, date, headers, pathAndParameters) // 使用HMAC-SHA256计算签名 h := hmac.New(sha256.New, []byte(appSecret)) h.Write([]byte(signStr)) digestBytes := h.Sum(nil) // 对摘要结果进行Base64编码 signature := base64.StdEncoding.EncodeToString(digestBytes) return signature } // generateNonce 生成随机字符串 // 使用当前时间的纳秒数作为随机字符串 // // 返回: 纳秒时间戳字符串 func generateNonce() string { return strconv.FormatInt(time.Now().UnixNano(), 10) } // getContentMD5 计算请求体的MD5值 // 对请求体进行MD5哈希计算,然后进行Base64编码 // // 参数: // - body: 请求体字节数组 // // 返回: Base64编码的MD5值 func getContentMD5(body []byte) string { md5Sum := md5.Sum(body) return base64.StdEncoding.EncodeToString(md5Sum[:]) } // getCurrentTimestamp 获取当前时间戳(毫秒) // // 返回: 毫秒级时间戳字符串 func getCurrentTimestamp() string { return strconv.FormatInt(time.Now().UnixNano()/1e6, 10) } // getCurrentDate 获取当前UTC时间字符串 // 格式: "Mon, 02 Jan 2006 15:04:05 GMT" // // 返回: RFC1123格式的UTC时间字符串 func getCurrentDate() string { return time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT") } // formatDateForTemplate 格式化日期用于模板填写 // 格式: "2006年01月02日" // // 返回: 中文格式的日期字符串 func formatDateForTemplate() string { return time.Now().Format("2006年01月02日") } // generateFileName 生成带时间戳的文件名 // // 参数: // - baseName: 基础文件名 // - extension: 文件扩展名 // // 返回: 带时间戳的文件名 func generateFileName(baseName, extension string) string { timestamp := time.Now().Format("20060102_150405") return baseName + "_" + timestamp + "." + extension } // calculateExpireTime 计算过期时间戳 // // 参数: // - days: 过期天数 // // 返回: 毫秒级时间戳 func calculateExpireTime(days int) int64 { return time.Now().AddDate(0, 0, days).UnixMilli() } // verifySignature 验证e签宝回调签名 func VerifySignature(callbackData interface{}, headers map[string]string, queryParams map[string]string, appSecret string) error { // 1. 获取签名相关参数 signature, ok := headers["X-Tsign-Open-Signature"] if !ok { return fmt.Errorf("缺少签名头: X-Tsign-Open-Signature") } timestamp, ok := headers["X-Tsign-Open-Timestamp"] if !ok { return fmt.Errorf("缺少时间戳头: X-Tsign-Open-Timestamp") } // 2. 构建查询参数字符串 var queryKeys []string for key := range queryParams { queryKeys = append(queryKeys, key) } sort.Strings(queryKeys) // 按ASCII码升序排序 var queryValues []string for _, key := range queryKeys { queryValues = append(queryValues, queryParams[key]) } queryString := strings.Join(queryValues, "") // 3. 获取请求体数据 bodyData, err := getRequestBodyString(callbackData) if err != nil { return fmt.Errorf("获取请求体数据失败: %w", err) } // 4. 构建验签数据 data := timestamp + queryString + bodyData // 5. 计算签名 expectedSignature := calculateSignature(data, appSecret) // 6. 比较签名 if strings.ToLower(expectedSignature) != strings.ToLower(signature) { return fmt.Errorf("签名验证失败") } return nil } // calculateSignature 计算HMAC-SHA256签名 func calculateSignature(data, secret string) string { h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(data)) return strings.ToUpper(hex.EncodeToString(h.Sum(nil))) } // getRequestBodyString 获取请求体字符串 func getRequestBodyString(callbackData interface{}) (string, error) { // 将map转换为JSON字符串 jsonBytes, err := json.Marshal(callbackData) if err != nil { return "", fmt.Errorf("JSON序列化失败: %w", err) } return string(jsonBytes), nil }