xy_sh-20260724171749/xy_sh/generate.md

1027 lines
32 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.

现在开始生成所有文件。
// 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
)
require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.28.0 // indirect
)
```
// 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"
// Config 应用配置
type Config struct {
ServerPort string // 服务监听端口
SM4Key string // SM4 加密密钥
SM3Salt string // SM3 盐值
}
// LoadConfig 加载配置
func LoadConfig() *Config {
return &Config{
ServerPort: getEnv("SERVER_PORT", "8080"),
SM4Key: getEnv("SM4_KEY", ""),
SM3Salt: getEnv("SM3_SALT", ""),
}
}
func getEnv(key, defaultVal string) string {
if val := os.Getenv(key); val != "" {
return val
}
return defaultVal
}
```
// File: xy_sh/internal/entities/request.go
```go
package entities
// EncryptedRequest 加密后的请求体
type EncryptedRequest struct {
EncryptedData string `json:"encryptedData"`
}
// OrderRequest 卡券/直充权益下单业务参数
type OrderRequest struct {
ActCode string `json:"actCode"`
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"`
OrderNo string `json:"orderNo"`
}
// WechatRechargeRequest 微信立减金充值业务参数
type WechatRechargeRequest struct {
ActCode string `json:"actCode"`
OrderNo string `json:"orderNo"`
GoodsCode string `json:"goodsCode"`
ActOrderNum string `json:"actOrderNum"`
AppID string `json:"appId"`
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"`
}
// CardInfo 卡券信息
type CardInfo struct {
CouponNo string `json:"couponNo"`
CouponCode string `json:"couponCode"`
ExpireTime string `json:"expireTime"`
}
```
// File: xy_sh/internal/entities/response.go
```go
package entities
// ApiResponse 通用API响应
type ApiResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
// 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"`
}
// WechatRechargeResponse 微信立减金充值响应业务数据
type WechatRechargeResponse struct {
OrderNo string `json:"orderNo"`
Status int `json:"status"`
CouponID string `json:"couponId,omitempty"`
}
// CallbackResponse 回调通知响应
type CallbackResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
```
// 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 := c.Get("timestamp")
sign := c.Get("sign")
if timestamp == "" || sign == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "缺少timestamp或sign请求头",
})
}
// 解析请求体获取 encryptedData
var req struct {
EncryptedData string `json:"encryptedData"`
}
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体解析失败",
})
}
// 签名规则sm3WithSalt(timestamp + encryptedData)
rawStr := timestamp + req.EncryptedData
expectedSign, err := crypto.SM3WithSalt([]byte(rawStr), []byte(cfg.SM3Salt))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": -1,
"msg": "签名计算失败",
})
}
if !strings.EqualFold(sign, expectedSign) {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"code": -1,
"msg": "签名验证失败",
})
}
return c.Next()
}
}
// ParseEncryptedBody 解密请求体中间件将解密后的数据存入c.Locals
func ParseEncryptedBody(cfg *config.Config) fiber.Handler {
return func(c *fiber.Ctx) error {
var req struct {
EncryptedData string `json:"encryptedData"`
}
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体解析失败",
})
}
if req.EncryptedData == "" {
return c.Next()
}
key := []byte(cfg.SM4Key)
plaintext, err := crypto.SM4CBCDecrypt(req.EncryptedData, key)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "解密失败: " + err.Error(),
})
}
c.Locals("decryptedBody", string(plaintext))
return c.Next()
}
}
// EncryptResponse 加密响应数据中间件
func EncryptResponse(cfg *config.Config) fiber.Handler {
return func(c *fiber.Ctx) error {
// 先执行后续handler
err := c.Next()
if err != nil {
return err
}
// 获取响应体
body := c.Response().Body()
if len(body) == 0 {
return nil
}
var resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data json.RawMessage `json:"data,omitempty"`
}
if err := json.Unmarshal(body, &resp); err != nil {
return nil
}
// 如果data不为空需要加密
if len(resp.Data) > 0 && string(resp.Data) != "null" {
key := []byte(cfg.SM4Key)
encrypted, err := crypto.SM4CBCEncrypt(resp.Data, key)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": -1,
"msg": "响应加密失败",
})
}
resp.Data = json.RawMessage(`"` + encrypted + `"`)
encryptedBody, _ := json.Marshal(resp)
c.Response().SetBody(encryptedBody)
}
return nil
}
}
```
// File: xy_sh/internal/biz/biz.go
```go
package biz
import (
"encoding/json"
"fmt"
"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}
}
// DecryptAndUnmarshal 解密并反序列化请求数据
func (b *Biz) DecryptAndUnmarshal(encryptedData string, target interface{}) error {
key := []byte(b.cfg.SM4Key)
plaintext, err := crypto.SM4CBCDecrypt(encryptedData, key)
if err != nil {
return fmt.Errorf("解密失败: %v", err)
}
if err := json.Unmarshal(plaintext, target); err != nil {
return fmt.Errorf("JSON解析失败: %v", err)
}
return nil
}
// EncryptAndMarshal 序列化并加密响应数据
func (b *Biz) EncryptAndMarshal(data interface{}) (string, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
key := []byte(b.cfg.SM4Key)
encrypted, err := crypto.SM4CBCEncrypt(jsonBytes, key)
if err != nil {
return "", fmt.Errorf("加密失败: %v", err)
}
return encrypted, nil
}
// CreateOrder 卡券/直充权益下单
func (b *Biz) CreateOrder(req *entities.OrderRequest) (*entities.OrderResponse, error) {
// TODO: 实现下单业务逻辑
// 1. 校验参数
// 2. 调用供应商接口
// 3. 返回结果
return &entities.OrderResponse{
OrderNo: "HM" + fmt.Sprintf("%d", 0),
Status: 0,
ExpireTime: "2025-12-31 23:59:59",
}, nil
}
// QueryOrder 查询订单
func (b *Biz) QueryOrder(req *entities.QueryRequest) (*entities.QueryResponse, error) {
// TODO: 实现查询业务逻辑
return &entities.QueryResponse{
OrderNo: req.OrderNo,
Status: 0,
}, nil
}
// WechatRecharge 微信立减金充值
func (b *Biz) WechatRecharge(req *entities.WechatRechargeRequest) (*entities.WechatRechargeResponse, error) {
// TODO: 实现微信立减金充值业务逻辑
return &entities.WechatRechargeResponse{
OrderNo: req.OrderNo,
Status: 0,
CouponID: "123456",
}, nil
}
// HandleCallback 处理回调通知
func (b *Biz) HandleCallback(req *entities.CallbackRequest) error {
// TODO: 实现回调处理逻辑
// 1. 更新订单状态
// 2. 通知相关系统
return nil
}
```
// 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 HTTP服务层
type Service struct {
biz *biz.Biz
cfg *config.Config
}
// NewService 创建服务实例
func NewService(cfg *config.Config) *Service {
return &Service{
biz: biz.NewBiz(cfg),
cfg: cfg,
}
}
// CreateOrder 卡券/直充权益下单
func (s *Service) CreateOrder(c *fiber.Ctx) error {
// 获取解密后的请求体
decryptedBody, ok := c.Locals("decryptedBody").(string)
if !ok || decryptedBody == "" {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "解密后的请求体为空",
})
}
var req entities.OrderRequest
if err := json.Unmarshal([]byte(decryptedBody), &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "请求参数解析失败: " + err.Error(),
})
}
// 调用业务逻辑
resp, err := s.biz.CreateOrder(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
// 加密响应数据
encryptedData, err := s.biz.EncryptAndMarshal(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败",
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// QueryOrder 卡券/直充/微信立减金订单查询
func (s *Service) QueryOrder(c *fiber.Ctx) error {
decryptedBody, ok := c.Locals("decryptedBody").(string)
if !ok || decryptedBody == "" {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "解密后的请求体为空",
})
}
var req entities.QueryRequest
if err := json.Unmarshal([]byte(decryptedBody), &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "请求参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.QueryOrder(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptAndMarshal(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败",
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// WechatRecharge 微信立减金订单充值
func (s *Service) WechatRecharge(c *fiber.Ctx) error {
decryptedBody, ok := c.Locals("decryptedBody").(string)
if !ok || decryptedBody == "" {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "解密后的请求体为空",
})
}
var req entities.WechatRechargeRequest
if err := json.Unmarshal([]byte(decryptedBody), &req); err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "请求参数解析失败: " + err.Error(),
})
}
resp, err := s.biz.WechatRecharge(&req)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
encryptedData, err := s.biz.EncryptAndMarshal(resp)
if err != nil {
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "响应加密失败",
})
}
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
// HandleCallback 处理供应商回调通知
func (s *Service) HandleCallback(c *fiber.Ctx) error {
decryptedBody, ok := c.Locals("decryptedBody").(string)
if !ok || decryptedBody == "" {
return c.Status(fiber.StatusBadRequest).SendString("invalid request")
}
var req entities.CallbackRequest
if err := json.Unmarshal([]byte(decryptedBody), &req); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("invalid request")
}
if err := s.biz.HandleCallback(&req); err != nil {
// 处理失败返回非200状态码触发重试
return c.Status(fiber.StatusInternalServerError).SendString("process failed")
}
// 处理成功返回HTTP 200且内容为ok
return c.Status(fiber.StatusOK).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路由组应用签名验证和解密中间件
api := app.Group("/api", middleware.VerifySign(cfg), middleware.ParseEncryptedBody(cfg))
// 接口1卡券/直充权益下单
api.Post("/order/create", svc.CreateOrder)
// 接口2卡券/直充/微信立减金订单查询
api.Post("/order/query", svc.QueryOrder)
// 接口3微信立减金订单充值
api.Post("/order/wechat-recharge", svc.WechatRecharge)
// 接口4卡券/直充/微信立减金充值结果通知(回调接口,同样需要验签和解密)
api.Post("/callback/notice", svc.HandleCallback)
return app
}
```
// File: xy_sh/internal/test/example_test.go
```go
package test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"testing"
"time"
"xy_sh/internal/config"
"xy_sh/internal/entities"
"xy_sh/pkg/crypto"
)
// TestCreateOrder 模拟卡券/直充权益下单请求
func TestCreateOrder(t *testing.T) {
cfg := &config.Config{
ServerPort: "8080",
SM4Key: "1234567890abcdef", // 16字节密钥
SM3Salt: "test_salt",
}
// 构造业务参数
req := entities.OrderRequest{
ActCode: "ACT001",
GoodsCode: "123456",
ActOrderNum: "00001",
Account: "19912345678",
CallbackURL: "https://xxx/notice",
}
// 加密业务参数
reqJSON, _ := json.Marshal(req)
encryptedData, err := crypto.SM4CBCEncrypt(reqJSON, []byte(cfg.SM4Key))
if err != nil {
t.Fatalf("加密失败: %v", err)
}
// 生成签名
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
rawStr := timestamp + encryptedData
sign, err := crypto.SM3WithSalt([]byte(rawStr), []byte(cfg.SM3Salt))
if err != nil {
t.Fatalf("签名失败: %v", err)
}
// 构造请求体
body := map[string]string{
"encryptedData": encryptedData,
}
bodyJSON, _ := json.Marshal(body)
// 发送请求
httpReq, _ := http.NewRequest("POST", "http://localhost:"+cfg.ServerPort+"/api/order/create", bytes.NewReader(bodyJSON))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("timestamp", timestamp)
httpReq.Header.Set("sign", sign)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
t.Logf("响应: %s", string(respBody))
}
// TestQueryOrder 模拟订单查询请求
func TestQueryOrder(t *testing.T) {
cfg := &config.Config{
ServerPort: "8080",
SM4Key: "1234567890abcdef",
SM3Salt: "test_salt",
}
req := entities.QueryRequest{
ActCode: "FBG6vdYqGE4mGX7EH/woEg==",
OrderNo: "HM1757046717684000102707339840b23222",
}
reqJSON, _ := json.Marshal(req)
encryptedData, err := crypto.SM4CBCEncrypt(reqJSON, []byte(cfg.SM4Key))
if err != nil {
t.Fatalf("加密失败: %v", err)
}
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
rawStr := timestamp + encryptedData
sign, _ := crypto.SM3WithSalt([]byte(rawStr), []byte(cfg.SM3Salt))
body := map[string]string{"encryptedData": encryptedData}
bodyJSON, _ := json.Marshal(body)
httpReq, _ := http.NewRequest("POST", "http://localhost:"+cfg.ServerPort+"/api/order/query", bytes.NewReader(bodyJSON))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("timestamp", timestamp)
httpReq.Header.Set("sign", sign)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
t.Logf("响应: %s", string(respBody))
}
// TestWechatRecharge 模拟微信立减金充值请求
func TestWechatRecharge(t *testing.T) {
cfg := &config.Config{
ServerPort: "8080",
SM4Key: "1234567890abcdef",
SM3Salt: "test_salt",
}
req := entities.WechatRechargeRequest{
ActCode: "FBG6vdYqGE4mGX7EH/woEg==",
GoodsCode: "0001",
ActOrderNum: "0001",
OpenID: "0001",
AppID: "00001",
}
reqJSON, _ := json.Marshal(req)
encryptedData, err := crypto.SM4CBCEncrypt(reqJSON, []byte(cfg.SM4Key))
if err != nil {
t.Fatalf("加密失败: %v", err)
}
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
rawStr := timestamp + encryptedData
sign, _ := crypto.SM3WithSalt([]byte(rawStr), []byte(cfg.SM3Salt))
body := map[string]string{"encryptedData": encryptedData}
bodyJSON, _ := json.Marshal(body)
httpReq, _ := http.NewRequest("POST", "http://localhost:"+cfg.ServerPort+"/api/order/wechat-recharge", bytes.NewReader(bodyJSON))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("timestamp", timestamp)
httpReq.Header.Set("sign", sign)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
t.Logf("响应: %s", string(respBody))
}
// TestCallback 模拟回调通知请求
func TestCallback(t *testing.T) {
cfg := &config.Config{
ServerPort: "8080",
SM4Key: "1234567890abcdef",
SM3Salt: "test_salt",
}
req := entities.CallbackRequest{
OrderNo: "HM202509291010001",
ActOrderNum: "XY2025092910100001",
Status: 3,
Account: "19912345678",
}
reqJSON, _ := json.Marshal(req)
encryptedData, err := crypto.SM4CBCEncrypt(reqJSON, []byte(cfg.SM4Key))
if err != nil {
t.Fatalf("加密失败: %v", err)
}
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
rawStr := timestamp + encryptedData
sign, _ := crypto.SM3WithSalt([]byte(rawStr), []byte(cfg.SM3Salt))
body := map[string]string{"encryptedData": encryptedData}
bodyJSON, _ := json.Marshal(body)
httpReq, _ := http.NewRequest("POST", "http://localhost:"+cfg.ServerPort+"/api/callback/notice", bytes.NewReader(bodyJSON))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("timestamp", timestamp)
httpReq.Header.Set("sign", sign)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
t.Logf("响应: %s", string(respBody))
}
```
// File: xy_sh/pkg/crypto/crypto.go
```go
package crypto
import (
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"bytes"
"math/big"
"time"
"github.com/tjfoc/gmsm/sm3"
"github.com/tjfoc/gmsm/sm4"
)
// ==================== SM4-CBC 加密/解密 ====================
// SM4CBCEncrypt SM4-CBC模式加密
func SM4CBCEncrypt(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())
iv := make([]byte, block.BlockSize())
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", fmt.Errorf("生成IV失败: %v", err)
}
mode := cipher.NewCBCEncrypter(block, iv)
ciphertext := make([]byte, len(padded))
mode.CryptBlocks(ciphertext, padded)
result := append(iv, ciphertext...)
return base64.StdEncoding.EncodeToString(result), nil
}
// SM4CBCDecrypt SM4-CBC模式解密
func SM4CBCDecrypt(encryptedData string, key []byte) ([]byte, error) {
if len(key) != 16 {
return nil, fmt.Errorf("SM4密钥长度必须为16字节")
}
data, 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(data) < blockSize {
return nil, fmt.Errorf("数据长度不足")
}
iv := data[:blockSize]
ciphertext := data[blockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
plaintext, err = pkcs7UnPadding(plaintext)
if err != nil {
return nil, fmt.Errorf("去除填充失败: %v", err)
}
return plaintext, nil
}
// 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
}
// ==================== SM3 哈希 ====================
// SM3Hash 计算SM3哈希值
func SM3Hash(data []byte, encoding string) (string, error) {
h := sm3.New()
h.Write(data)
hashBytes := h.Sum(nil)
if encoding == "base64" {
return base64.StdEncoding.EncodeToString(hashBytes), nil
}
return hex.EncodeToString(hashBytes), nil
}
// SM3HashString 计算字符串的SM3哈希值
func SM3HashString(data string, encoding string) (string, error) {
return SM3Hash([]byte(data), encoding)
}
// SM3Verify 验证数据与哈希值是否匹配
func SM3Verify(data []byte, hash string, encoding string) (bool, error) {
expected, err := SM3Hash(data, encoding)
if err != nil {
return false, err
}
return expected == hash, nil
}
// HMACSM3 HMAC-SM3计算
func HMACSM3(data []byte, key []byte) string {
h := hmac.New(sm3.New, key)
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// SM3WithSalt SM3加盐哈希对应Java的SmUtil.sm3WithSalt
// 实现方式使用HMAC-SM3将salt作为HMAC密钥
func SM3WithSalt(data []byte, salt []byte) (string, error) {
h := hmac.New(sm3.New, salt)
h.Write(data)
return hex.EncodeToString(h.Sum(nil)), nil
}
// ==================== 时间戳和随机数 ====================
// GenerateTimestamp 生成秒级时间戳
func GenerateTimestamp() string {
return fmt.Sprintf("%d", time.Now().Unix())
}
// GenerateTimestampMillis 生成毫秒级时间戳
func GenerateTimestampMillis() string {
return fmt.Sprintf("%d", time.Now().UnixMilli())
}
// GenerateNonce 生成指定长度的随机字符串
func GenerateNonce(length int) (string, error) {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
return "", err
}
b[i] = charset[num.Int64()]
}
return string(b), nil
}
```
=== 服务端生成完成 ===
## 加密实现
以下是从加密工具获取的完整实现:
### 加密实现 1
{
"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"
}
### 加密实现 2
{
"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"
}
### 加密实现 3
{
"success": true,
"data": "\n### 时间戳和随机数生成规范\n\n**代码模板**\n\n```go\npackage crypto\n\nimport (\n \"crypto/rand\"\n \"fmt\"\n \"math/big\"\n \"time\"\n)\n\n// GenerateTimestamp 生成秒级时间戳\nfunc GenerateTimestamp() string {\n return fmt.Sprintf(\"%d\", time.Now().Unix())\n}\n\n// GenerateTimestampMillis 生成毫秒级时间戳\nfunc GenerateTimestampMillis() string {\n return fmt.Sprintf(\"%d\", time.Now().UnixMilli())\n}\n\n// GenerateNonce 生成指定长度的随机字符串(加密安全)\nfunc GenerateNonce(length int) (string, error) {\n const charset = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n b := make([]byte, length)\n for i := range b {\n num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))\n if err != nil {\n return \"\", err\n }\n b[i] = charset[num.Int64()]\n }\n return string(b), nil\n}\n```\n\n**注意事项**\n- 确认文档要求的是秒级还是毫秒级时间戳\n- 确认nonce的长度要求通常16-32位\n- 生产环境建议使用加密安全的随机数生成器\n",
"tool": "nonce_timestamp"
}