xy_sh-20260724181350/xy_sh/generate.md

718 lines
27 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卡券/直充权益下单接口**,其他接口不实现。
文档中提到的加密规则:
1. SM4-CBC 加密业务数据
2. SM3 with salt 签名timestamp + encryptedData
3. 请求头携带 timestamp 和 sign
让我生成所有文件。
注意:文档中的 SM3 签名使用的是 `SmUtil.sm3WithSalt(sm3Salt.getBytes()).digestHex(timestamp+encryptedData)`,这是带盐的 SM3 哈希。在 Go 中SM3 没有内置的 "with salt" 方法,但我们可以模拟:将 salt 作为 HMAC 密钥或者将 salt 拼接到数据中。根据 Java hutool 的 `sm3WithSalt` 实现,它实际上是将 salt 作为密钥进行 HMAC-SM3 计算。所以我们需要使用 HMAC-SM3。
从工具返回的代码中,我们有 `HMACSM3` 函数,它使用标准 HMAC 算法,底层使用 SM3 哈希函数。这正是我们需要的。
现在生成所有文件:
1. go.mod
2. cmd/server/main.go
3. internal/config/config.go
4. internal/entities/request.go
5. internal/entities/response.go
6. internal/middleware/middleware.go
7. internal/service/service.go
8. internal/router/router.go
9. internal/biz/biz.go
10. internal/test/example_test.go
11. pkg/crypto/crypto.go
Let me generate all files now.</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
)
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/pkg/crypto/crypto.go
```go
package crypto
import (
"bytes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"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 哈希 / HMAC-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)
}
// HMACSM3 HMAC-SM3计算
// 对应 Java hutool 的 sm3WithSalt(saltBytes).digestHex(data)
func HMACSM3(data []byte, key []byte) string {
h := hmac.New(sm3.New, key)
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// ============================================================
// 时间戳 / 随机数
// ============================================================
// 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
}
// ============================================================
// 签名生成与验证(文档专用)
// ============================================================
// GenerateSign 生成签名
// 规则HMAC-SM3(salt, timestamp + encryptedData) -> hex
func GenerateSign(timestamp string, encryptedData string, sm3Salt []byte) string {
data := timestamp + encryptedData
return HMACSM3([]byte(data), sm3Salt)
}
// VerifySign 验证签名
func VerifySign(timestamp string, encryptedData string, sign string, sm3Salt []byte) bool {
expected := GenerateSign(timestamp, encryptedData, sm3Salt)
return expected == sign
}
```
// File: xy_sh/internal/config/config.go
```go
package config
import "os"
// Config 应用配置
type Config struct {
ServerPort string // 服务监听端口
SM4Key []byte // SM4 加密密钥16字节
SM3Salt []byte // SM3 签名盐值
}
// LoadConfig 加载配置(从环境变量读取,生产环境建议使用配置文件或配置中心)
func LoadConfig() *Config {
return &Config{
ServerPort: getEnv("SERVER_PORT", "8080"),
SM4Key: []byte(getEnv("SM4_KEY", "1234567890123456")), // 默认16字节测试密钥
SM3Salt: []byte(getEnv("SM3_SALT", "default_sm3_salt")),
}
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
```
// File: xy_sh/internal/entities/request.go
```go
package entities
// EncryptedRequest 通用加密请求体
type EncryptedRequest struct {
EncryptedData string `json:"encryptedData"`
}
// OrderCreateRequest 卡券/直充权益下单业务参数(解密后)
type OrderCreateRequest struct {
ActCode string `json:"actCode"` // 活动code
GoodsCode string `json:"goodsCode"` // 供应商商品编号
ActOrderNum string `json:"actOrderNum"` // 活动方订单号
Account string `json:"account,omitempty"` // 充值账号
CallbackURL string `json:"callbackUrl,omitempty"` // 回调地址
}
```
// 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"` // 加密后的业务数据字符串
}
// OrderCreateResponse 下单响应业务数据(解密后)
type OrderCreateResponse struct {
OrderNo string `json:"orderNo"` // 供应商订单号
CouponNo string `json:"couponNo,omitempty"` // 卡号
CouponCode string `json:"couponCode,omitempty"` // 卡密
Status int `json:"status"` // -1发放中 0成功 1失败 2已核销 3已过期
ExpireTime string `json:"expireTime,omitempty"` // 有效期
}
```
// File: xy_sh/internal/middleware/middleware.go
```go
package middleware
import (
"log"
"github.com/gofiber/fiber/v2"
"xy_sh/pkg/crypto"
)
// SM3Salt 全局SM3盐值由config初始化时注入
var SM3Salt []byte
// SM4Key 全局SM4密钥由config初始化时注入
var SM4Key []byte
// AuthMiddleware 签名验证中间件
// 验证请求头中的 timestamp 和 sign
func AuthMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
timestamp := c.Get("timestamp")
sign := c.Get("sign")
if timestamp == "" || sign == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"code": -1,
"msg": "缺少timestamp或sign请求头",
})
}
// 读取请求体中的 encryptedData
var body struct {
EncryptedData string `json:"encryptedData"`
}
if err := c.BodyParser(&body); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": -1,
"msg": "请求体解析失败",
})
}
// 验证签名
if !crypto.VerifySign(timestamp, body.EncryptedData, sign, SM3Salt) {
log.Printf("[中间件] 签名验证失败: timestamp=%s, sign=%s", timestamp, sign)
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"code": -1,
"msg": "签名验证失败",
})
}
log.Printf("[中间件] 签名验证通过: timestamp=%s", timestamp)
return c.Next()
}
}
```
// File: xy_sh/internal/biz/biz.go
```go
package biz
import (
"encoding/json"
"fmt"
"log"
"xy_sh/internal/entities"
"xy_sh/pkg/crypto"
)
// Biz 业务逻辑层
type Biz struct {
SM4Key []byte
}
// NewBiz 创建业务逻辑实例
func NewBiz(sm4Key []byte) *Biz {
return &Biz{SM4Key: sm4Key}
}
// DecryptRequest 解密请求体中的 encryptedData 并解析为指定结构体
func (b *Biz) DecryptRequest(encryptedData string, target interface{}) error {
plaintext, err := crypto.SM4CBCDecrypt(encryptedData, b.SM4Key)
if err != nil {
return fmt.Errorf("SM4解密失败: %v", err)
}
if err := json.Unmarshal(plaintext, target); err != nil {
return fmt.Errorf("JSON解析失败: %v", err)
}
log.Printf("[业务] 解密请求数据: %s", string(plaintext))
return nil
}
// EncryptResponse 加密响应业务数据
func (b *Biz) EncryptResponse(data interface{}) (string, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
encrypted, err := crypto.SM4CBCEncrypt(jsonBytes, b.SM4Key)
if err != nil {
return "", fmt.Errorf("SM4加密失败: %v", err)
}
log.Printf("[业务] 加密响应数据: %s", string(jsonBytes))
return encrypted, nil
}
// CreateOrder 卡券/直充权益下单业务逻辑
// TODO: 根据实际业务需求实现
func (b *Biz) CreateOrder(req *entities.OrderCreateRequest) (*entities.OrderCreateResponse, error) {
log.Printf("[业务] 下单请求: actCode=%s, goodsCode=%s, actOrderNum=%s, account=%s",
req.ActCode, req.GoodsCode, req.ActOrderNum, req.Account)
// TODO: 调用供应商实际接口或数据库操作
// 以下为模拟实现
resp := &entities.OrderCreateResponse{
OrderNo: fmt.Sprintf("HM%d%s", crypto.GenerateTimestampMillis(), "27073398f3772c00"),
Status: 0,
ExpireTime: "2025-09-12 00:00:00",
}
log.Printf("[业务] 下单成功: orderNo=%s", resp.OrderNo)
return resp, nil
}
```
// File: xy_sh/internal/service/service.go
```go
package service
import (
"log"
"github.com/gofiber/fiber/v2"
"xy_sh/internal/biz"
"xy_sh/internal/entities"
"xy_sh/internal/middleware"
)
// Service HTTP服务层
type Service struct {
biz *biz.Biz
}
// NewService 创建服务实例
func NewService(biz *biz.Biz) *Service {
return &Service{biz: biz}
}
// HandleOrderCreate 卡券/直充权益下单接口
func (s *Service) HandleOrderCreate(c *fiber.Ctx) error {
// 解析请求体
var req entities.EncryptedRequest
if err := c.BodyParser(&req); err != nil {
log.Printf("[服务] 请求体解析失败: %v", err)
return c.Status(fiber.StatusBadRequest).JSON(entities.ApiResponse{
Code: -1,
Msg: "请求体解析失败",
})
}
// 解密业务参数
var bizReq entities.OrderCreateRequest
if err := s.biz.DecryptRequest(req.EncryptedData, &bizReq); err != nil {
log.Printf("[服务] 解密业务参数失败: %v", err)
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "解密业务参数失败: " + err.Error(),
})
}
// 调用业务逻辑
bizResp, err := s.biz.CreateOrder(&bizReq)
if err != nil {
log.Printf("[服务] 下单失败: %v", err)
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: err.Error(),
})
}
// 加密响应业务数据
encryptedData, err := s.biz.EncryptResponse(bizResp)
if err != nil {
log.Printf("[服务] 加密响应数据失败: %v", err)
return c.JSON(entities.ApiResponse{
Code: -1,
Msg: "加密响应数据失败",
})
}
log.Printf("[服务] 下单成功: orderNo=%s", bizResp.OrderNo)
return c.JSON(entities.ApiResponse{
Code: 0,
Msg: "请求成功",
Data: encryptedData,
})
}
```
// File: xy_sh/internal/router/router.go
```go
package router
import (
"github.com/gofiber/fiber/v2"
"xy_sh/internal/middleware"
"xy_sh/internal/service"
)
// SetupRoutes 注册路由
func SetupRoutes(app *fiber.App, svc *service.Service) {
// 所有接口都需要签名验证
api := app.Group("/api", middleware.AuthMiddleware())
// 接口1卡券/直充权益下单接口
api.Post("/order/create", svc.HandleOrderCreate)
}
```
// File: xy_sh/cmd/server/main.go
```go
package main
import (
"log"
"github.com/gofiber/fiber/v2"
"xy_sh/internal/biz"
"xy_sh/internal/config"
"xy_sh/internal/middleware"
"xy_sh/internal/router"
"xy_sh/internal/service"
)
func main() {
// 加载配置
cfg := config.LoadConfig()
// 初始化全局加密密钥(注入中间件)
middleware.SM4Key = cfg.SM4Key
middleware.SM3Salt = cfg.SM3Salt
// 初始化业务层
bizLayer := biz.NewBiz(cfg.SM4Key)
// 初始化服务层
svc := service.NewService(bizLayer)
// 创建 Fiber 应用
app := fiber.New(fiber.Config{
AppName: "xy_sh - 卡券/直充权益服务",
})
// 注册路由
router.SetupRoutes(app, svc)
// 启动服务
addr := ":" + cfg.ServerPort
log.Printf("[启动] 服务监听地址: %s", addr)
log.Fatal(app.Listen(addr))
}
```
// File: xy_sh/internal/test/example_test.go
```go
package test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"testing"
"xy_sh/pkg/crypto"
)
// 测试配置(需与服务器配置一致)
var (
testSM4Key = []byte("1234567890123456")
testSM3Salt = []byte("default_sm3_salt")
testBaseURL = "http://localhost:8080"
)
// TestOrderCreate 模拟卡券/直充权益下单接口请求
func TestOrderCreate(t *testing.T) {
// 1. 构造业务参数
bizReq := map[string]interface{}{
"actCode": "ACT001",
"goodsCode": "123456",
"actOrderNum": "00001",
"account": "19912345678",
"callbackUrl": "https://xxx/notice",
}
// 2. 序列化并加密
bizJSON, _ := json.Marshal(bizReq)
encryptedData, err := crypto.SM4CBCEncrypt(bizJSON, testSM4Key)
if err != nil {
t.Fatalf("SM4加密失败: %v", err)
}
// 3. 生成签名
timestamp := crypto.GenerateTimestampMillis()
sign := crypto.GenerateSign(timestamp, encryptedData, testSM3Salt)
// 4. 构造请求体
reqBody := map[string]string{
"encryptedData": encryptedData,
}
reqBodyJSON, _ := json.Marshal(reqBody)
// 5. 发送HTTP请求
req, err := http.NewRequest("POST", testBaseURL+"/api/order/create", bytes.NewReader(reqBodyJSON))
if err != nil {
t.Fatalf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("timestamp", timestamp)
req.Header.Set("sign", sign)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 6. 读取响应
respBody, _ := io.ReadAll(resp.Body)
fmt.Printf("响应状态码: %d\n", resp.StatusCode)
fmt.Printf("响应体: %s\n", string(respBody))
// 7. 解析响应
var apiResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data json.RawMessage `json:"data"`
}
if err := json.Unmarshal(respBody, &apiResp); err != nil {
t.Fatalf("解析响应失败: %v", err)
}
if apiResp.Code != 0 {
t.Fatalf("请求失败: code=%d, msg=%s", apiResp.Code, apiResp.Msg)
}
// 8. 解密响应数据
if len(apiResp.Data) > 0 {
// data 是字符串(加密后的数据)
var encryptedDataStr string
if err := json.Unmarshal(apiResp.Data, &encryptedDataStr); err != nil {
t.Fatalf("解析加密数据失败: %v", err)
}
plaintext, err := crypto.SM4CBCDecrypt(encryptedDataStr, testSM4Key)
if err != nil {
t.Fatalf("解密响应数据失败: %v", err)
}
fmt.Printf("解密后业务数据: %s\n", string(plaintext))
}
t.Log("下单接口测试通过")
}
```
现在生成所有文件完成,让我检查一下是否所有文件都已生成:
1. ✅ xy_sh/go.mod
2. ✅ xy_sh/cmd/server/main.go
3. ✅ xy_sh/internal/config/config.go
4. ✅ xy_sh/internal/entities/request.go
5. ✅ xy_sh/internal/entities/response.go
6. ✅ xy_sh/internal/middleware/middleware.go
7. ✅ xy_sh/internal/service/service.go
8. ✅ xy_sh/internal/router/router.go
9. ✅ xy_sh/internal/biz/biz.go
10. ✅ xy_sh/internal/test/example_test.go
11. ✅ xy_sh/pkg/crypto/crypto.go
=== 服务端生成完成 ===
## 加密实现
以下是从加密工具获取的完整实现:
### 加密实现 1
{
"success": true,
"data": "\n### 参数拼接规范\n\n**常见拼接方式**\n\n1. **字典序排序**按key字典序排序后拼接 `key=value\u0026key2=value2`\n2. **固定顺序**:按文档指定顺序拼接\n3. **JSON字符串**:整个请求体作为签名串\n\n**代码模板**\n\n```go\npackage crypto\n\nimport (\n \"fmt\"\n \"sort\"\n \"strings\"\n)\n\n// BuildSignString 方式1字典序排序拼接\nfunc BuildSignString(params map[string]string) string {\n keys := make([]string, 0, len(params))\n for k, v := range params {\n if v != \"\" \u0026\u0026 k != \"sign\" \u0026\u0026 k != \"signature\" {\n keys = append(keys, k)\n }\n }\n sort.Strings(keys)\n \n var parts []string\n for _, k := range keys {\n parts = append(parts, fmt.Sprintf(\"%s=%s\", k, params[k]))\n }\n return strings.Join(parts, \"\u0026\")\n}\n\n// BuildSignStringOrdered 方式2固定顺序拼接\nfunc BuildSignStringOrdered(params map[string]string, orderedKeys []string) string {\n var parts []string\n for _, k := range orderedKeys {\n if v, ok := params[k]; ok \u0026\u0026 v != \"\" {\n parts = append(parts, fmt.Sprintf(\"%s=%s\", k, v))\n }\n }\n return strings.Join(parts, \"\u0026\")\n}\n```\n\n**注意事项**\n- 确认文档指定的排序规则(字典序/固定顺序)\n- 确认空值是否要包含(通常排除空值)\n- 确认是否需要URL编码\n- 注意排除签名字段本身\n",
"tool": "param_concat"
}
### 加密实现 2
{
"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"
}
### 加密实现 3
{
"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"
}
### 加密实现 4
{
"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"
}