xy_sh-20260724115439/xy_sh/generate.md

1718 lines
52 KiB
Markdown
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.

好的,现在我已经获取了所有加密工具的实现代码。让我开始生成完整的服务端代码。
根据文档分析:
1. **SM4-ECB**:用于加密/解密业务数据文档没有提到IV示例数据无IV前缀
2. **SM3带盐哈希**:用于生成签名(`sm3WithSalt`
3. 所有接口使用 `timestamp` + `sign` 请求头,`encryptedData` 请求体
现在开始生成所有文件:
// File: xy_sh/go.mod
```go
module xy_sh
go 1.26
require (
github.com/gofiber/fiber/v2 v2.52.6
github.com/tjfoc/gmsm v1.4.1
)
```
// File: xy_sh/cmd/server/main.go
```go
package main
import (
"log"
"xy_sh/internal/config"
"xy_sh/internal/router"
)
func main() {
cfg := config.LoadConfig()
app := router.SetupRouter(cfg)
log.Printf("服务启动,监听端口: %s", cfg.ServerPort)
if err := app.Listen(":" + cfg.ServerPort); err != nil {
log.Fatalf("服务启动失败: %v", err)
}
}
```
// File: xy_sh/internal/config/config.go
```go
package config
import "os"
type Config struct {
ServerPort string
SM3Salt string
SM4Key []byte
}
func LoadConfig() *Config {
port := os.Getenv("SERVER_PORT")
if port == "" {
port = "8080"
}
sm3Salt := os.Getenv("SM3_SALT")
if sm3Salt == "" {
sm3Salt = "default_sm3_salt"
}
sm4KeyStr := os.Getenv("SM4_KEY")
var sm4Key []byte
if sm4KeyStr != "" {
sm4Key = []byte(sm4KeyStr)
} else {
sm4Key = []byte("1234567890123456") // 16字节默认密钥
}
return &Config{
ServerPort: port,
SM3Salt: sm3Salt,
SM4Key: sm4Key,
}
}
```
// File: xy_sh/internal/entities/request.go
```go
package entities
// OrderRequest 卡券/直充权益下单接口请求业务参数
type OrderRequest struct {
ActCode string `json:"actCode"` // 活动code
GoodsCode string `json:"goodsCode"` // 供应商商品编号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
Account string `json:"account,omitempty"` // 充值账号
CallbackUrl string `json:"callbackUrl,omitempty"` // 回调地址
}
// QueryRequest 订单查询接口请求业务参数
type QueryRequest struct {
ActCode string `json:"actCode"` // 活动code
OrderNo string `json:"orderNo"` // 供应商订单号
}
// WechatRechargeRequest 微信立减金订单充值接口请求业务参数
type WechatRechargeRequest struct {
ActCode string `json:"actCode"` // 活动code
OrderNo string `json:"orderNo"` // 供应商订单号
GoodsCode string `json:"goodsCode"` // 供应商商品编号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
AppId string `json:"appId"` // 公众账号ID
OpenId string `json:"openId"` // 微信用户标识
CallbackUrl string `json:"callbackUrl,omitempty"` // 回调地址
}
// CallbackRequest 充值结果通知接口回调业务参数
type CallbackRequest struct {
OrderNo string `json:"orderNo"` // 供应商订单号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
Status int `json:"status"` // 订单状态
Account string `json:"account,omitempty"` // 直充账号
CardInfo *CardInfo `json:"cardInfo,omitempty"` // 卡券信息
CouponId string `json:"couponId,omitempty"` // 微信优惠id
}
// CardInfo 卡券信息
type CardInfo struct {
CouponNo string `json:"couponNo"` // 卡号/短链
CouponCode string `json:"couponCode"` // 卡密
ExpireTime string `json:"expireTime"` // 过期时间
}
// EncryptedRequest 加密请求体
type EncryptedRequest struct {
EncryptedData string `json:"encryptedData"` // SM4加密后的业务数据
}
```
// File: xy_sh/internal/entities/response.go
```go
package entities
// ApiResponse 通用API响应
type ApiResponse struct {
Code int `json:"code"` // 返回状态编码0成功 -1失败
Msg string `json:"msg"` // 返回错误信息
Data interface{} `json:"data"` // JSON格式业务数据进行SM4加密后的字符串
}
// OrderResponse 下单接口响应业务参数
type OrderResponse struct {
OrderNo string `json:"orderNo"` // 权益订单号
CouponNo string `json:"couponNo,omitempty"` // 卡号
CouponCode string `json:"couponCode,omitempty"` // 卡密
Status int `json:"status"` // 状态
ExpireTime string `json:"expireTime,omitempty"` // 有效期
}
// QueryResponse 查询接口响应业务参数
type QueryResponse struct {
OrderNo string `json:"orderNo"` // 权益订单号
Status int `json:"status"` // 订单状态
Account string `json:"account,omitempty"` // 充值账号
CardInfo *CardInfo `json:"cardInfo,omitempty"` // 卡券信息
CouponId string `json:"couponId,omitempty"` // 优惠id
}
// WechatRechargeResponse 微信立减金充值响应业务参数
type WechatRechargeResponse struct {
OrderNo string `json:"orderNo"` // 权益订单号
Status int `json:"status"` // 订单状态
CouponId string `json:"couponId,omitempty"` // 微信优惠id
}
```
// File: xy_sh/internal/middleware/middleware.go
```go
package middleware
import (
"encoding/json"
"strings"
"github.com/gofiber/fiber/v2"
"xy_sh/internal/config"
"xy_sh/pkg/crypto"
)
// VerifySign 验证签名中间件
func VerifySign(cfg *config.Config) fiber.Handler {
return func(c *fiber.Ctx) error {
// 从请求头获取timestamp和sign
timestamp := c.Get("timestamp")
sign := c.Get("sign")
if timestamp == "" || sign == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "缺少timestamp或sign请求头",
})
}
// 获取请求体
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体不能为空",
})
}
// 解析请求体获取encryptedData
var req struct {
EncryptedData string `json:"encryptedData"`
}
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体格式错误",
})
}
// 验证签名SM3(salt + timestamp + encryptedData)
expectedSign := crypto.SM3WithSalt(timestamp+req.EncryptedData, cfg.SM3Salt)
if !strings.EqualFold(expectedSign, sign) {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"code": -1,
"msg": "签名验证失败",
})
}
// 将解密后的业务参数存入上下文
decrypted, err := crypto.SM4ECBDecrypt(req.EncryptedData, cfg.SM4Key)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "解密失败: " + err.Error(),
})
}
c.Locals("decryptedBody", decrypted)
return c.Next()
}
}
```
// File: xy_sh/internal/biz/biz.go
```go
package biz
import (
"encoding/json"
"xy_sh/internal/config"
"xy_sh/internal/entities"
"xy_sh/pkg/crypto"
)
// Biz 业务逻辑层
type Biz struct {
cfg *config.Config
}
// NewBiz 创建业务逻辑实例
func NewBiz(cfg *config.Config) *Biz {
return &Biz{cfg: cfg}
}
// EncryptResponse 加密响应数据
func (b *Biz) EncryptResponse(data interface{}) (string, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return "", err
}
return crypto.SM4ECBEncrypt(jsonBytes, b.cfg.SM4Key)
}
// DecryptRequest 解密请求数据
func (b *Biz) DecryptRequest(encryptedData string) ([]byte, error) {
return crypto.SM4ECBDecrypt(encryptedData, b.cfg.SM4Key)
}
// ProcessOrder 处理下单请求
func (b *Biz) ProcessOrder(req *entities.OrderRequest) (*entities.OrderResponse, error) {
// TODO: 实现下单业务逻辑
// 1. 校验参数
// 2. 调用供应商接口或处理业务
// 3. 返回结果
return &entities.OrderResponse{
OrderNo: "HM" + generateOrderNo(),
Status: 0,
ExpireTime: "2025-12-31 23:59:59",
}, nil
}
// ProcessQuery 处理订单查询请求
func (b *Biz) ProcessQuery(req *entities.QueryRequest) (*entities.QueryResponse, error) {
// TODO: 实现订单查询业务逻辑
return &entities.QueryResponse{
OrderNo: req.OrderNo,
Status: 0,
}, nil
}
// ProcessWechatRecharge 处理微信立减金充值请求
func (b *Biz) ProcessWechatRecharge(req *entities.WechatRechargeRequest) (*entities.WechatRechargeResponse, error) {
// TODO: 实现微信立减金充值业务逻辑
return &entities.WechatRechargeResponse{
OrderNo: req.OrderNo,
Status: 0,
}, nil
}
// ProcessCallback 处理回调通知
func (b *Biz) ProcessCallback(req *entities.CallbackRequest) error {
// TODO: 实现回调通知处理逻辑
// 1. 更新订单状态
// 2. 处理卡券信息
// 3. 处理微信优惠信息
return nil
}
// generateOrderNo 生成订单号(示例)
func generateOrderNo() string {
// TODO: 实现订单号生成逻辑
return "0000000000000000"
}
```
// File: xy_sh/internal/service/service.go
```go
package service
import (
"encoding/json"
"github.com/gofiber/fiber/v2"
"xy_sh/internal/biz"
"xy_sh/internal/config"
"xy_sh/internal/entities"
)
// Service 服务层
type Service struct {
biz *biz.Biz
cfg *config.Config
}
// NewService 创建服务实例
func NewService(cfg *config.Config) *Service {
return &Service{
biz: biz.NewBiz(cfg),
cfg: cfg,
}
}
// Order 卡券/直充权益下单接口
func (s *Service) Order(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.OrderRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "业务参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.ProcessOrder(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptResponse(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败: " + err.Error(),
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// Query 卡券/直充/微信立减金订单查询接口
func (s *Service) Query(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.QueryRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "业务参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.ProcessQuery(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptResponse(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败: " + err.Error(),
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// WechatRecharge 微信立减金订单充值接口
func (s *Service) WechatRecharge(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.WechatRechargeRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "业务参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.ProcessWechatRecharge(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptResponse(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败: " + err.Error(),
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// Callback 卡券/直充/微信立减金充值结果通知接口
func (s *Service) Callback(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.CallbackRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("参数解析失败")
}
if err := s.biz.ProcessCallback(&req); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
return c.SendString("ok")
}
```
// File: xy_sh/internal/router/router.go
```go
package router
import (
"github.com/gofiber/fiber/v2"
"xy_sh/internal/config"
"xy_sh/internal/middleware"
"xy_sh/internal/service"
)
// SetupRouter 设置路由
func SetupRouter(cfg *config.Config) *fiber.App {
app := fiber.New()
svc := service.NewService(cfg)
// 需要签名验证的路由组
api := app.Group("/api", middleware.VerifySign(cfg))
{
// 接口1卡券/直充权益下单接口
api.Post("/order", svc.Order)
// 接口2卡券/直充/微信立减金订单查询接口
api.Post("/query", svc.Query)
// 接口3微信立减金订单充值接口
api.Post("/wechat/recharge", svc.WechatRecharge)
// 接口4卡券/直充/微信立减金充值结果通知接口
api.Post("/callback", svc.Callback)
}
return app
}
```
// File: xy_sh/internal/test/example.go
```go
package test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"xy_sh/pkg/crypto"
)
// 测试配置(请替换为实际值)
const (
sm3Salt = "your_sm3_salt"
sm4Key = "1234567890123456" // 16字节密钥
)
// APIClient API客户端示例
type APIClient struct {
BaseURL string
SM3Salt string
SM4Key []byte
}
// NewAPIClient 创建API客户端
func NewAPIClient(baseURL string) *APIClient {
return &APIClient{
BaseURL: baseURL,
SM3Salt: sm3Salt,
SM4Key: []byte(sm4Key),
}
}
// EncryptAndSign 加密业务数据并生成签名
func (c *APIClient) EncryptAndSign(bizData interface{}) (timestamp string, encryptedData string, sign string, err error) {
// 序列化业务数据
jsonBytes, err := json.Marshal(bizData)
if err != nil {
return "", "", "", fmt.Errorf("序列化失败: %v", err)
}
// SM4加密
encryptedData, err = crypto.SM4ECBEncrypt(jsonBytes, c.SM4Key)
if err != nil {
return "", "", "", fmt.Errorf("加密失败: %v", err)
}
// 生成时间戳(毫秒级)
timestamp = fmt.Sprintf("%d", time.Now().UnixMilli())
// 生成签名SM3(salt + timestamp + encryptedData)
sign = crypto.SM3WithSalt(timestamp+encryptedData, c.SM3Salt)
return timestamp, encryptedData, sign, nil
}
// SendRequest 发送请求
func (c *APIClient) SendRequest(path string, bizData interface{}) (map[string]interface{}, error) {
timestamp, encryptedData, sign, err := c.EncryptAndSign(bizData)
if err != nil {
return nil, err
}
// 构建请求体
reqBody := map[string]string{
"encryptedData": encryptedData,
}
reqBytes, _ := json.Marshal(reqBody)
// 创建请求
req, err := http.NewRequest("POST", c.BaseURL+path, bytes.NewReader(reqBytes))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("timestamp", timestamp)
req.Header.Set("sign", sign)
// 发送请求
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
var result map[string]interface{}
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return result, nil
}
// ExampleOrder 下单接口示例
func ExampleOrder() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"actCode": "ACT001",
"goodsCode": "123456",
"actOrderNum": "00001",
"account": "19912345678",
"callbackUrl": "https://xxx/notice",
}
result, err := client.SendRequest("/api/order", bizData)
if err != nil {
fmt.Printf("下单失败: %v\n", err)
return
}
fmt.Printf("下单响应: %+v\n", result)
// 如果有加密数据,解密查看
if data, ok := result["data"].(string); ok && data != "" {
decrypted, err := crypto.SM4ECBDecrypt(data, client.SM4Key)
if err == nil {
fmt.Printf("解密后数据: %s\n", string(decrypted))
}
}
}
// ExampleQuery 查询接口示例
func ExampleQuery() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"actCode": "FBG6vdYqGE4mGX7EH/woEg==",
"orderNo": "HM1757046717684000102707339840b23222",
}
result, err := client.SendRequest("/api/query", bizData)
if err != nil {
fmt.Printf("查询失败: %v\n", err)
return
}
fmt.Printf("查询响应: %+v\n", result)
}
// ExampleWechatRecharge 微信立减金充值示例
func ExampleWechatRecharge() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"actCode": "FBG6vdYqGE4mGX7EH/woEg==",
"goodsCode": "0001",
"actOrderNum": "0001",
"openId": "0001",
"appId": "00001",
}
result, err := client.SendRequest("/api/wechat/recharge", bizData)
if err != nil {
fmt.Printf("充值失败: %v\n", err)
return
}
fmt.Printf("充值响应: %+v\n", result)
}
// ExampleCallback 回调通知示例(模拟供应商调用行方)
func ExampleCallback() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"orderNo": "HM202509291010001",
"actOrderNum": "XY2025092910100001",
"status": 3,
"account": "19912345678",
}
result, err := client.SendRequest("/api/callback", bizData)
if err != nil {
fmt.Printf("回调失败: %v\n", err)
return
}
fmt.Printf("回调响应: %+v\n", result)
}
```
// File: xy_sh/pkg/crypto/crypto.go
```go
package crypto
import (
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"
"github.com/tjfoc/gmsm/sm3"
"github.com/tjfoc/gmsm/sm4"
)
// SM4ECBEncrypt SM4-ECB模式加密
func SM4ECBEncrypt(plaintext []byte, key []byte) (string, error) {
if len(key) != 16 {
return "", fmt.Errorf("SM4密钥长度必须为16字节")
}
block, err := sm4.NewCipher(key)
if err != nil {
return "", fmt.Errorf("创建SM4 cipher失败: %v", err)
}
padded := pkcs7Padding(plaintext, block.BlockSize())
ciphertext := make([]byte, len(padded))
for i := 0; i < len(padded); i += block.BlockSize() {
block.Encrypt(ciphertext[i:i+block.BlockSize()], padded[i:i+block.BlockSize()])
}
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// SM4ECBDecrypt SM4-ECB模式解密
func SM4ECBDecrypt(encryptedData string, key []byte) ([]byte, error) {
if len(key) != 16 {
return nil, fmt.Errorf("SM4密钥长度必须为16字节")
}
ciphertext, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return nil, fmt.Errorf("base64解码失败: %v", err)
}
block, err := sm4.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("创建SM4 cipher失败: %v", err)
}
blockSize := block.BlockSize()
if len(ciphertext)%blockSize != 0 {
return nil, fmt.Errorf("密文长度不是块大小的整数倍")
}
plaintext := make([]byte, len(ciphertext))
for i := 0; i < len(ciphertext); i += blockSize {
block.Decrypt(plaintext[i:i+blockSize], ciphertext[i:i+blockSize])
}
plaintext, err = pkcs7UnPadding(plaintext)
if err != nil {
return nil, fmt.Errorf("去除填充失败: %v", err)
}
return plaintext, nil
}
// SM3WithSalt 带盐的SM3哈希计算
// 对应Java的 SmUtil.sm3WithSalt(saltBytes).digestHex(data)
func SM3WithSalt(data string, salt string) string {
// SM3带盐哈希先对盐进行SM3哈希然后用结果作为密钥进行HMAC-SM3
saltHash := sm3.Sm3Sum([]byte(salt))
h := sm3.New()
h.Write(saltHash)
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
// SM3Hash 计算SM3哈希值
func SM3Hash(data []byte) string {
return hex.EncodeToString(sm3.Sm3Sum(data))
}
// pkcs7Padding PKCS7填充
func pkcs7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// pkcs7UnPadding 去除PKCS7填充
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, fmt.Errorf("数据为空")
}
padding := int(data[length-1])
if padding > length || padding == 0 {
return nil, fmt.Errorf("无效的填充")
}
for i := length - padding; i < length; i++ {
if data[i] != byte(padding) {
return nil, fmt.Errorf("无效的填充")
}
}
return data[:length-padding], nil
}
```
现在让我检查一下所有文件是否都生成了:
1. ✅ go.mod
2. ✅ cmd/server/main.go
3. ✅ internal/service/service.go
4. ✅ internal/router/router.go
5. ✅ internal/test/example.go
6. ✅ internal/biz/biz.go
7. ✅ internal/entities/request.go
8. ✅ internal/entities/response.go
9. ✅ internal/middleware/middleware.go
10. ✅ internal/config/config.go
11. ✅ pkg/crypto/crypto.go
所有文件都已生成!让我输出完整的代码。
等等让我重新检查一下SM3WithSalt的实现。Java的 `SmUtil.sm3WithSalt(saltBytes).digestHex(data)` 是怎么实现的?
在Hutool中`sm3WithSalt` 的实现是:
1. 将盐值进行SM3哈希
2. 将盐的哈希值和数据拼接
3. 再进行SM3哈希
所以应该是:`SM3(SM3(salt) + data)`
让我修正一下实现。
实际上更准确地说Hutool的 `sm3WithSalt` 实现是:
```java
public static SM3 sm3WithSalt(byte[] salt) {
return new SM3(salt);
}
```
`digestHex(data)` 是计算 `SM3(salt + data)` 或者类似的方式。
在Hutool中SM3带盐的实现是将盐作为初始向量或与数据拼接。
更准确地说Hutool的SM3带盐实现是
1. 先对盐进行SM3哈希得到盐的摘要
2. 将盐的摘要与数据拼接
3. 再进行SM3哈希
或者更简单地说,就是 `SM3(salt + data)`
让我用更准确的方式实现:
```go
func SM3WithSalt(data string, salt string) string {
h := sm3.New()
h.Write([]byte(salt))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
```
这样就是 `SM3(salt + data)`和Java的 `sm3WithSalt` 行为一致。
好的让我更新crypto.go中的实现。
实际上Hutool的SM3带盐实现比较复杂。让我查一下...
Hutool的 `SmUtil.sm3WithSalt(byte[] salt)` 返回一个SM3对象该对象在初始化时使用盐值。当调用 `digestHex(data)` 时,它计算的是 `SM3(salt || data)`
所以我的实现 `SM3(salt + data)` 是正确的。
好的,让我输出所有文件。</think>现在开始生成所有文件:
// File: xy_sh/go.mod
```go
module xy_sh
go 1.26
require (
github.com/gofiber/fiber/v2 v2.52.6
github.com/tjfoc/gmsm v1.4.1
)
```
// File: xy_sh/cmd/server/main.go
```go
package main
import (
"log"
"xy_sh/internal/config"
"xy_sh/internal/router"
)
func main() {
cfg := config.LoadConfig()
app := router.SetupRouter(cfg)
log.Printf("服务启动,监听端口: %s", cfg.ServerPort)
if err := app.Listen(":" + cfg.ServerPort); err != nil {
log.Fatalf("服务启动失败: %v", err)
}
}
```
// File: xy_sh/internal/config/config.go
```go
package config
import "os"
type Config struct {
ServerPort string
SM3Salt string
SM4Key []byte
}
func LoadConfig() *Config {
port := os.Getenv("SERVER_PORT")
if port == "" {
port = "8080"
}
sm3Salt := os.Getenv("SM3_SALT")
if sm3Salt == "" {
sm3Salt = "default_sm3_salt"
}
sm4KeyStr := os.Getenv("SM4_KEY")
var sm4Key []byte
if sm4KeyStr != "" {
sm4Key = []byte(sm4KeyStr)
} else {
sm4Key = []byte("1234567890123456")
}
return &Config{
ServerPort: port,
SM3Salt: sm3Salt,
SM4Key: sm4Key,
}
}
```
// File: xy_sh/internal/entities/request.go
```go
package entities
// OrderRequest 卡券/直充权益下单接口请求业务参数
type OrderRequest struct {
ActCode string `json:"actCode"` // 活动code
GoodsCode string `json:"goodsCode"` // 供应商商品编号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
Account string `json:"account,omitempty"` // 充值账号
CallbackUrl string `json:"callbackUrl,omitempty"` // 回调地址
}
// QueryRequest 订单查询接口请求业务参数
type QueryRequest struct {
ActCode string `json:"actCode"` // 活动code
OrderNo string `json:"orderNo"` // 供应商订单号
}
// WechatRechargeRequest 微信立减金订单充值接口请求业务参数
type WechatRechargeRequest struct {
ActCode string `json:"actCode"` // 活动code
OrderNo string `json:"orderNo"` // 供应商订单号
GoodsCode string `json:"goodsCode"` // 供应商商品编号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
AppId string `json:"appId"` // 公众账号ID
OpenId string `json:"openId"` // 微信用户标识
CallbackUrl string `json:"callbackUrl,omitempty"` // 回调地址
}
// CallbackRequest 充值结果通知接口回调业务参数
type CallbackRequest struct {
OrderNo string `json:"orderNo"` // 供应商订单号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
Status int `json:"status"` // 订单状态
Account string `json:"account,omitempty"` // 直充账号
CardInfo *CardInfo `json:"cardInfo,omitempty"` // 卡券信息
CouponId string `json:"couponId,omitempty"` // 微信优惠id
}
// CardInfo 卡券信息
type CardInfo struct {
CouponNo string `json:"couponNo"` // 卡号/短链
CouponCode string `json:"couponCode"` // 卡密
ExpireTime string `json:"expireTime"` // 过期时间
}
// EncryptedRequest 加密请求体
type EncryptedRequest struct {
EncryptedData string `json:"encryptedData"` // SM4加密后的业务数据
}
```
// File: xy_sh/internal/entities/response.go
```go
package entities
// ApiResponse 通用API响应
type ApiResponse struct {
Code int `json:"code"` // 返回状态编码0成功 -1失败
Msg string `json:"msg"` // 返回错误信息
Data interface{} `json:"data"` // JSON格式业务数据进行SM4加密后的字符串
}
// OrderResponse 下单接口响应业务参数
type OrderResponse struct {
OrderNo string `json:"orderNo"` // 权益订单号
CouponNo string `json:"couponNo,omitempty"` // 卡号
CouponCode string `json:"couponCode,omitempty"` // 卡密
Status int `json:"status"` // 状态
ExpireTime string `json:"expireTime,omitempty"` // 有效期
}
// QueryResponse 查询接口响应业务参数
type QueryResponse struct {
OrderNo string `json:"orderNo"` // 权益订单号
Status int `json:"status"` // 订单状态
Account string `json:"account,omitempty"` // 充值账号
CardInfo *CardInfo `json:"cardInfo,omitempty"` // 卡券信息
CouponId string `json:"couponId,omitempty"` // 优惠id
}
// WechatRechargeResponse 微信立减金充值响应业务参数
type WechatRechargeResponse struct {
OrderNo string `json:"orderNo"` // 权益订单号
Status int `json:"status"` // 订单状态
CouponId string `json:"couponId,omitempty"` // 微信优惠id
}
```
// File: xy_sh/internal/middleware/middleware.go
```go
package middleware
import (
"encoding/json"
"strings"
"github.com/gofiber/fiber/v2"
"xy_sh/internal/config"
"xy_sh/pkg/crypto"
)
// VerifySign 验证签名中间件
// 从请求头获取timestamp和sign验证SM3(salt + timestamp + encryptedData)是否匹配
func VerifySign(cfg *config.Config) fiber.Handler {
return func(c *fiber.Ctx) error {
timestamp := c.Get("timestamp")
sign := c.Get("sign")
if timestamp == "" || sign == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "缺少timestamp或sign请求头",
})
}
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体不能为空",
})
}
var req struct {
EncryptedData string `json:"encryptedData"`
}
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体格式错误",
})
}
// 验证签名SM3(salt + timestamp + encryptedData)
expectedSign := crypto.SM3WithSalt(timestamp+req.EncryptedData, cfg.SM3Salt)
if !strings.EqualFold(expectedSign, sign) {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"code": -1,
"msg": "签名验证失败",
})
}
// 解密业务数据并存入上下文
decrypted, err := crypto.SM4ECBDecrypt(req.EncryptedData, cfg.SM4Key)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "解密失败: " + err.Error(),
})
}
c.Locals("decryptedBody", decrypted)
return c.Next()
}
}
```
// File: xy_sh/internal/biz/biz.go
```go
package biz
import (
"encoding/json"
"xy_sh/internal/config"
"xy_sh/internal/entities"
"xy_sh/pkg/crypto"
)
// Biz 业务逻辑层
type Biz struct {
cfg *config.Config
}
// NewBiz 创建业务逻辑实例
func NewBiz(cfg *config.Config) *Biz {
return &Biz{cfg: cfg}
}
// EncryptResponse 加密响应数据SM4-ECB
func (b *Biz) EncryptResponse(data interface{}) (string, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return "", err
}
return crypto.SM4ECBEncrypt(jsonBytes, b.cfg.SM4Key)
}
// DecryptRequest 解密请求数据SM4-ECB
func (b *Biz) DecryptRequest(encryptedData string) ([]byte, error) {
return crypto.SM4ECBDecrypt(encryptedData, b.cfg.SM4Key)
}
// ProcessOrder 处理下单请求
func (b *Biz) ProcessOrder(req *entities.OrderRequest) (*entities.OrderResponse, error) {
// TODO: 实现下单业务逻辑
// 1. 校验参数
// 2. 调用供应商接口或处理业务
// 3. 返回结果
return &entities.OrderResponse{
OrderNo: "HM" + generateOrderNo(),
Status: 0,
ExpireTime: "2025-12-31 23:59:59",
}, nil
}
// ProcessQuery 处理订单查询请求
func (b *Biz) ProcessQuery(req *entities.QueryRequest) (*entities.QueryResponse, error) {
// TODO: 实现订单查询业务逻辑
return &entities.QueryResponse{
OrderNo: req.OrderNo,
Status: 0,
}, nil
}
// ProcessWechatRecharge 处理微信立减金充值请求
func (b *Biz) ProcessWechatRecharge(req *entities.WechatRechargeRequest) (*entities.WechatRechargeResponse, error) {
// TODO: 实现微信立减金充值业务逻辑
return &entities.WechatRechargeResponse{
OrderNo: req.OrderNo,
Status: 0,
}, nil
}
// ProcessCallback 处理回调通知
func (b *Biz) ProcessCallback(req *entities.CallbackRequest) error {
// TODO: 实现回调通知处理逻辑
// 1. 更新订单状态
// 2. 处理卡券信息
// 3. 处理微信优惠信息
return nil
}
// generateOrderNo 生成订单号(示例)
func generateOrderNo() string {
// TODO: 实现订单号生成逻辑
return "0000000000000000"
}
```
// File: xy_sh/internal/service/service.go
```go
package service
import (
"encoding/json"
"github.com/gofiber/fiber/v2"
"xy_sh/internal/biz"
"xy_sh/internal/config"
"xy_sh/internal/entities"
)
// Service 服务层
type Service struct {
biz *biz.Biz
cfg *config.Config
}
// NewService 创建服务实例
func NewService(cfg *config.Config) *Service {
return &Service{
biz: biz.NewBiz(cfg),
cfg: cfg,
}
}
// Order 卡券/直充权益下单接口
func (s *Service) Order(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.OrderRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "业务参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.ProcessOrder(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptResponse(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败: " + err.Error(),
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// Query 卡券/直充/微信立减金订单查询接口
func (s *Service) Query(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.QueryRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "业务参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.ProcessQuery(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptResponse(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败: " + err.Error(),
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// WechatRecharge 微信立减金订单充值接口
func (s *Service) WechatRecharge(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.WechatRechargeRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "业务参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.ProcessWechatRecharge(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptResponse(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败: " + err.Error(),
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// Callback 卡券/直充/微信立减金充值结果通知接口
// 行方回调通知接口:接收供应商推送的充值结果
// 返回HTTP 200且内容为"ok"表示接收成功
func (s *Service) Callback(c *fiber.Ctx) error {
decrypted := c.Locals("decryptedBody").([]byte)
var req entities.CallbackRequest
if err := json.Unmarshal(decrypted, &req); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("参数解析失败")
}
if err := s.biz.ProcessCallback(&req); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
return c.SendString("ok")
}
```
// File: xy_sh/internal/router/router.go
```go
package router
import (
"github.com/gofiber/fiber/v2"
"xy_sh/internal/config"
"xy_sh/internal/middleware"
"xy_sh/internal/service"
)
// SetupRouter 设置路由
func SetupRouter(cfg *config.Config) *fiber.App {
app := fiber.New()
svc := service.NewService(cfg)
// 需要签名验证的路由组
api := app.Group("/api", middleware.VerifySign(cfg))
{
// 接口1卡券/直充权益下单接口
api.Post("/order", svc.Order)
// 接口2卡券/直充/微信立减金订单查询接口
api.Post("/query", svc.Query)
// 接口3微信立减金订单充值接口
api.Post("/wechat/recharge", svc.WechatRecharge)
// 接口4卡券/直充/微信立减金充值结果通知接口(行方回调)
api.Post("/callback", svc.Callback)
}
return app
}
```
// File: xy_sh/internal/test/example.go
```go
package test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"xy_sh/pkg/crypto"
)
// 测试配置(请替换为实际值)
const (
sm3Salt = "your_sm3_salt"
sm4Key = "1234567890123456" // 16字节密钥
)
// APIClient API客户端示例
type APIClient struct {
BaseURL string
SM3Salt string
SM4Key []byte
}
// NewAPIClient 创建API客户端
func NewAPIClient(baseURL string) *APIClient {
return &APIClient{
BaseURL: baseURL,
SM3Salt: sm3Salt,
SM4Key: []byte(sm4Key),
}
}
// EncryptAndSign 加密业务数据并生成签名
func (c *APIClient) EncryptAndSign(bizData interface{}) (timestamp string, encryptedData string, sign string, err error) {
jsonBytes, err := json.Marshal(bizData)
if err != nil {
return "", "", "", fmt.Errorf("序列化失败: %v", err)
}
encryptedData, err = crypto.SM4ECBEncrypt(jsonBytes, c.SM4Key)
if err != nil {
return "", "", "", fmt.Errorf("加密失败: %v", err)
}
timestamp = fmt.Sprintf("%d", time.Now().UnixMilli())
// 签名SM3(salt + timestamp + encryptedData)
sign = crypto.SM3WithSalt(timestamp+encryptedData, c.SM3Salt)
return timestamp, encryptedData, sign, nil
}
// SendRequest 发送请求
func (c *APIClient) SendRequest(path string, bizData interface{}) (map[string]interface{}, error) {
timestamp, encryptedData, sign, err := c.EncryptAndSign(bizData)
if err != nil {
return nil, err
}
reqBody := map[string]string{
"encryptedData": encryptedData,
}
reqBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", c.BaseURL+path, bytes.NewReader(reqBytes))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("timestamp", timestamp)
req.Header.Set("sign", sign)
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
var result map[string]interface{}
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return result, nil
}
// ExampleOrder 下单接口示例
func ExampleOrder() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"actCode": "ACT001",
"goodsCode": "123456",
"actOrderNum": "00001",
"account": "19912345678",
"callbackUrl": "https://xxx/notice",
}
result, err := client.SendRequest("/api/order", bizData)
if err != nil {
fmt.Printf("下单失败: %v\n", err)
return
}
fmt.Printf("下单响应: %+v\n", result)
if data, ok := result["data"].(string); ok && data != "" {
decrypted, err := crypto.SM4ECBDecrypt(data, client.SM4Key)
if err == nil {
fmt.Printf("解密后数据: %s\n", string(decrypted))
}
}
}
// ExampleQuery 查询接口示例
func ExampleQuery() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"actCode": "FBG6vdYqGE4mGX7EH/woEg==",
"orderNo": "HM1757046717684000102707339840b23222",
}
result, err := client.SendRequest("/api/query", bizData)
if err != nil {
fmt.Printf("查询失败: %v\n", err)
return
}
fmt.Printf("查询响应: %+v\n", result)
}
// ExampleWechatRecharge 微信立减金充值示例
func ExampleWechatRecharge() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"actCode": "FBG6vdYqGE4mGX7EH/woEg==",
"goodsCode": "0001",
"actOrderNum": "0001",
"openId": "0001",
"appId": "00001",
}
result, err := client.SendRequest("/api/wechat/recharge", bizData)
if err != nil {
fmt.Printf("充值失败: %v\n", err)
return
}
fmt.Printf("充值响应: %+v\n", result)
}
// ExampleCallback 回调通知示例(模拟供应商调用行方)
func ExampleCallback() {
client := NewAPIClient("http://localhost:8080")
bizData := map[string]interface{}{
"orderNo": "HM202509291010001",
"actOrderNum": "XY2025092910100001",
"status": 3,
"account": "19912345678",
}
result, err := client.SendRequest("/api/callback", bizData)
if err != nil {
fmt.Printf("回调失败: %v\n", err)
return
}
fmt.Printf("回调响应: %+v\n", result)
}
```
// File: xy_sh/pkg/crypto/crypto.go
```go
package crypto
import (
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"
"github.com/tjfoc/gmsm/sm3"
"github.com/tjfoc/gmsm/sm4"
)
// SM4ECBEncrypt SM4-ECB模式加密
// 使用 github.com/tjfoc/gmsm/sm4 实现
// 输出base64编码
func SM4ECBEncrypt(plaintext []byte, key []byte) (string, error) {
if len(key) != 16 {
return "", fmt.Errorf("SM4密钥长度必须为16字节")
}
block, err := sm4.NewCipher(key)
if err != nil {
return "", fmt.Errorf("创建SM4 cipher失败: %v", err)
}
padded := pkcs7Padding(plaintext, block.BlockSize())
ciphertext := make([]byte, len(padded))
for i := 0; i < len(padded); i += block.BlockSize() {
block.Encrypt(ciphertext[i:i+block.BlockSize()], padded[i:i+block.BlockSize()])
}
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// SM4ECBDecrypt SM4-ECB模式解密
// 输入base64编码的密文返回解密后的明文
func SM4ECBDecrypt(encryptedData string, key []byte) ([]byte, error) {
if len(key) != 16 {
return nil, fmt.Errorf("SM4密钥长度必须为16字节")
}
ciphertext, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return nil, fmt.Errorf("base64解码失败: %v", err)
}
block, err := sm4.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("创建SM4 cipher失败: %v", err)
}
blockSize := block.BlockSize()
if len(ciphertext)%blockSize != 0 {
return nil, fmt.Errorf("密文长度不是块大小的整数倍")
}
plaintext := make([]byte, len(ciphertext))
for i := 0; i < len(ciphertext); i += blockSize {
block.Decrypt(plaintext[i:i+blockSize], ciphertext[i:i+blockSize])
}
plaintext, err = pkcs7UnPadding(plaintext)
if err != nil {
return nil, fmt.Errorf("去除填充失败: %v", err)
}
return plaintext, nil
}
// SM3WithSalt 带盐的SM3哈希计算
// 对应Java的 SmUtil.sm3WithSalt(saltBytes).digestHex(data)
// 实现方式SM3(salt + data),输出十六进制字符串
func SM3WithSalt(data string, salt string) string {
h := sm3.New()
h.Write([]byte(salt))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
// SM3Hash 计算SM3哈希值十六进制输出
func SM3Hash(data []byte) string {
return hex.EncodeToString(sm3.Sm3Sum(data))
}
// pkcs7Padding PKCS7填充
func pkcs7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// pkcs7UnPadding 去除PKCS7填充
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, fmt.Errorf("数据为空")
}
padding := int(data[length-1])
if padding > length || padding == 0 {
return nil, fmt.Errorf("无效的填充")
}
for i := length - padding; i < length; i++ {
if data[i] != byte(padding) {
return nil, fmt.Errorf("无效的填充")
}
}
return data[:length-padding], nil
}
```
=== 服务端生成完成 ===
## 加密实现
以下是从加密工具获取的完整实现:
### 加密实现 1
{
"success": true,
"data": "\n### SM4-CBC 国密对称加密完整实现指南\n\n**⚠️ 重要:必须使用第三方库,不要自己实现 SM4 算法!**\n**推荐使用github.com/tjfoc/gmsm**\n\n**前置要求**\n```bash\ngo get github.com/tjfoc/gmsm\n```\n\n**配置参数**\n- 编码方式: base64\n\n**完整代码模板(请直接复制使用)**\n\n```go\npackage crypto\n\nimport (\n \"crypto/cipher\"\n \"crypto/rand\"\n \"encoding/base64\"\n \"encoding/hex\"\n \"fmt\"\n \"io\"\n \"bytes\"\n\n \"github.com/tjfoc/gmsm/sm4\" // ⚠️ 必须使用此库不要自己实现SM4\n)\n\n// SM4CBCEncrypt SM4-CBC模式加密\n// 使用 github.com/tjfoc/gmsm/sm4 实现\nfunc SM4CBCEncrypt(plaintext []byte, key []byte) (string, error) {\n if len(key) != 16 {\n return \"\", fmt.Errorf(\"SM4密钥长度必须为16字节\")\n }\n\n // 使用 gmsm 库创建 SM4 cipher\n block, err := sm4.NewCipher(key)\n if err != nil {\n return \"\", fmt.Errorf(\"创建SM4 cipher失败: %v\", err)\n }\n\n padded := pkcs7Padding(plaintext, block.BlockSize())\n\n iv := make([]byte, block.BlockSize())\n if _, err := io.ReadFull(rand.Reader, iv); err != nil {\n return \"\", fmt.Errorf(\"生成IV失败: %v\", err)\n }\n\n mode := cipher.NewCBCEncrypter(block, iv)\n ciphertext := make([]byte, len(padded))\n mode.CryptBlocks(ciphertext, padded)\n\n result := append(iv, ciphertext...)\n if \"base64\" == \"hex\" {\n return hex.EncodeToString(result), nil\n }\n return base64.StdEncoding.EncodeToString(result), nil\n}\n\n// SM4CBCDecrypt SM4-CBC模式解密\n// 使用 github.com/tjfoc/gmsm/sm4 实现\nfunc SM4CBCDecrypt(encryptedData string, key []byte) ([]byte, error) {\n if len(key) != 16 {\n return nil, fmt.Errorf(\"SM4密钥长度必须为16字节\")\n }\n\n var data []byte\n var err error\n if \"base64\" == \"hex\" {\n data, err = hex.DecodeString(encryptedData)\n } else {\n data, err = base64.StdEncoding.DecodeString(encryptedData)\n }\n if err != nil {\n return nil, fmt.Errorf(\"解码失败: %v\", err)\n }\n\n block, err := sm4.NewCipher(key)\n if err != nil {\n return nil, fmt.Errorf(\"创建SM4 cipher失败: %v\", err)\n }\n\n blockSize := block.BlockSize()\n if len(data) \u003c blockSize {\n return nil, fmt.Errorf(\"数据长度不足\")\n }\n iv := data[:blockSize]\n ciphertext := data[blockSize:]\n\n mode := cipher.NewCBCDecrypter(block, iv)\n plaintext := make([]byte, len(ciphertext))\n mode.CryptBlocks(plaintext, ciphertext)\n\n plaintext, err = pkcs7UnPadding(plaintext)\n if err != nil {\n return nil, fmt.Errorf(\"去除填充失败: %v\", err)\n }\n\n return plaintext, nil\n}\n\n// pkcs7Padding PKCS7填充\nfunc pkcs7Padding(data []byte, blockSize int) []byte {\n padding := blockSize - len(data)%blockSize\n padText := bytes.Repeat([]byte{byte(padding)}, padding)\n return append(data, padText...)\n}\n\n// pkcs7UnPadding 去除PKCS7填充\nfunc pkcs7UnPadding(data []byte) ([]byte, error) {\n length := len(data)\n if length == 0 {\n return nil, fmt.Errorf(\"数据为空\")\n }\n padding := int(data[length-1])\n if padding \u003e length || padding == 0 {\n return nil, fmt.Errorf(\"无效的填充\")\n }\n for i := length - padding; i \u003c length; i++ {\n if data[i] != byte(padding) {\n return nil, fmt.Errorf(\"无效的填充\")\n }\n }\n return data[:length-padding], nil\n}\n```\n\n**⚠️ 重要提醒**\n1. **不要自己实现 SM4 算法**,请直接使用 github.com/tjfoc/gmsm\n2. 这个库是国密官方推荐的 Go 实现,安全可靠\n3. 在 go.mod 中添加依赖:`require github.com/tjfoc/gmsm v1.4.1`\n\n**注意事项**\n- SM4 密钥固定为 16 字节\n- IV 必须随机生成且每次不同\n- CBC 模式需要 IVECB 模式不需要\n",
"tool": "sm4_cbc_encrypt"
}
### 加密实现 2
{
"success": true,
"data": "\n### SM3 国密哈希算法完整实现指南\n\n**适用场景**:文档要求使用国密 SM3 算法进行摘要计算或签名验证\n\n**前置要求**:需要安装 github.com/tjfoc/gmsm\n\n```bash\ngo get github.com/tjfoc/gmsm\n```\n\n**配置参数**\n- 编码方式: hex\n- 包含 HMAC: true\n\n**完整代码模板**\n\n```go\npackage crypto\n\nimport (\n \"crypto/hmac\"\n \"encoding/base64\"\n \"encoding/hex\"\n \"fmt\"\n \"os\"\n \n \"github.com/tjfoc/gmsm/sm3\"\n)\n\n// SM3Hash 计算SM3哈希值\n// data: 待哈希的数据\n// encoding: 输出编码方式 (hex/base64)\n// 返回: 编码后的哈希值\nfunc SM3Hash(data []byte, encoding string) (string, error) {\n h := sm3.New()\n h.Write(data)\n hashBytes := h.Sum(nil)\n \n if encoding == \"base64\" {\n return base64.StdEncoding.EncodeToString(hashBytes), nil\n }\n return hex.EncodeToString(hashBytes), nil\n}\n\n// SM3HashString 计算字符串的SM3哈希值便捷方法\nfunc SM3HashString(data string, encoding string) (string, error) {\n return SM3Hash([]byte(data), encoding)\n}\n\n// SM3HashFile 计算文件的SM3哈希值\nfunc SM3HashFile(filePath string, encoding string) (string, error) {\n data, err := os.ReadFile(filePath)\n if err != nil {\n return \"\", fmt.Errorf(\"读取文件失败: %v\", err)\n }\n return SM3Hash(data, encoding)\n}\n\n// SM3Verify 验证数据与哈希值是否匹配\nfunc SM3Verify(data []byte, hash string, encoding string) (bool, error) {\n expected, err := SM3Hash(data, encoding)\n if err != nil {\n return false, err\n }\n return expected == hash, nil\n}\n\n// HMACSM3 HMAC-SM3计算\n// 使用标准HMAC算法底层使用SM3哈希函数\nfunc HMACSM3(data []byte, key []byte) string {\n // 使用标准HMAC底层哈希函数用SM3\n h := hmac.New(sm3.New, key)\n h.Write(data)\n return hex.EncodeToString(h.Sum(nil))\n}\n```\n\n**使用示例**\n```go\n// 计算字符串哈希\nhash, _ := SM3HashString(\"hello world\", \"hex\")\nfmt.Println(hash) // 输出64位十六进制字符串\n\n// 验证哈希\nvalid, _ := SM3Verify([]byte(\"hello world\"), hash, \"hex\")\nfmt.Println(valid) // true\n\n// HMAC-SM3示例\nkey := []byte(\"secret_key\")\ndata := []byte(\"hello world\")\nhmacResult := HMACSM3(data, key)\nfmt.Println(hmacResult)\n```\n\n**注意事项**\n- SM3 输出固定 256 位32字节的哈希值\n- 十六进制输出为 64 位字符串\n- 常用于数字签名、完整性校验等场景\n- SM3 是国密标准哈希算法,与 SHA-256 类似\n- HMAC-SM3 需要使用标准库 crypto/hmac底层哈希函数使用 sm3.New\n",
"tool": "sm3_hash"
}
### 加密实现 3
{
"success": true,
"data": "\n### SM4-ECB 国密对称加密完整实现指南\n\n**⚠️ 重要提示**ECB模式不安全仅用于兼容老旧系统。\n\n**⚠️ 重要:必须使用第三方库,不要自己实现 SM4 算法!**\n**推荐使用github.com/tjfoc/gmsm**\n\n**前置要求**\n```bash\ngo get github.com/tjfoc/gmsm\n```\n\n**配置参数**\n- 编码方式: base64\n\n**完整代码模板(请直接复制使用)**\n\n```go\npackage crypto\n\nimport (\n \"encoding/base64\"\n \"encoding/hex\"\n \"fmt\"\n \"bytes\"\n\n \"github.com/tjfoc/gmsm/sm4\" // ⚠️ 必须使用此库不要自己实现SM4\n)\n\n// SM4ECBEncrypt SM4-ECB模式加密\n// 使用 github.com/tjfoc/gmsm/sm4 实现\nfunc SM4ECBEncrypt(plaintext []byte, key []byte) (string, error) {\n if len(key) != 16 {\n return \"\", fmt.Errorf(\"SM4密钥长度必须为16字节\")\n }\n\n block, err := sm4.NewCipher(key)\n if err != nil {\n return \"\", fmt.Errorf(\"创建SM4 cipher失败: %v\", err)\n }\n\n padded := pkcs7Padding(plaintext, block.BlockSize())\n\n ciphertext := make([]byte, len(padded))\n for i := 0; i \u003c len(padded); i += block.BlockSize() {\n block.Encrypt(ciphertext[i:i+block.BlockSize()], padded[i:i+block.BlockSize()])\n }\n\n if \"base64\" == \"hex\" {\n return hex.EncodeToString(ciphertext), nil\n }\n return base64.StdEncoding.EncodeToString(ciphertext), nil\n}\n\n// SM4ECBDecrypt SM4-ECB模式解密\n// 使用 github.com/tjfoc/gmsm/sm4 实现\nfunc SM4ECBDecrypt(encryptedData string, key []byte) ([]byte, error) {\n if len(key) != 16 {\n return nil, fmt.Errorf(\"SM4密钥长度必须为16字节\")\n }\n\n var ciphertext []byte\n var err error\n if \"base64\" == \"hex\" {\n ciphertext, err = hex.DecodeString(encryptedData)\n } else {\n ciphertext, err = base64.StdEncoding.DecodeString(encryptedData)\n }\n if err != nil {\n return nil, fmt.Errorf(\"解码失败: %v\", err)\n }\n\n block, err := sm4.NewCipher(key)\n if err != nil {\n return nil, fmt.Errorf(\"创建SM4 cipher失败: %v\", err)\n }\n\n blockSize := block.BlockSize()\n if len(ciphertext)%blockSize != 0 {\n return nil, fmt.Errorf(\"密文长度不是块大小的整数倍\")\n }\n\n plaintext := make([]byte, len(ciphertext))\n for i := 0; i \u003c len(ciphertext); i += blockSize {\n block.Decrypt(plaintext[i:i+blockSize], ciphertext[i:i+blockSize])\n }\n\n plaintext, err = pkcs7UnPadding(plaintext)\n if err != nil {\n return nil, fmt.Errorf(\"去除填充失败: %v\", err)\n }\n\n return plaintext, nil\n}\n\n// pkcs7Padding 和 pkcs7UnPadding 函数请参考 CBC 模式实现\n```\n\n**⚠️ 重要提醒**\n1. **不要自己实现 SM4 算法**,请直接使用 github.com/tjfoc/gmsm\n2. 这个库是国密官方推荐的 Go 实现,安全可靠\n3. 在 go.mod 中添加依赖:`require github.com/tjfoc/gmsm v1.4.1`\n\n**⚠️ 安全警告**\n- ECB模式不应在新系统中使用\n- 仅用于兼容老旧的国密系统\n- 推荐使用 SM4-CBC 模式\n",
"tool": "sm4_ecb_encrypt"
}