package payment import ( "crypto/rand" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "math/big" "strings" "PaymentCenter/app/third/paymentService/psbc/internal/sm2" psbcutil "PaymentCenter/app/third/paymentService/psbc/internal/util" "github.com/ZZMarquis/gm/sm4" ) // EncryptRequest 加密请求 // 对齐 YouChuKoffee 的 EncryptRequest → postbank.Encrypt 流程: // 1. 序列化完整 request({head, body} 一起) // 2. SM4 CBC 加密 → base64 → addNewline(每76字符加 \r\n) // 3. SM2 加密 SM4 密钥(使用 SOP 公钥)→ HEX 大写作为 encryptKey // 4. signature = SM2_Sign(request_base64 + encryptKey + accessToken) // 5. 返回 {request, signature, encryptKey, accessToken} func EncryptRequest(request interface{}, cfg Config) (map[string]string, error) { client := NewClient(cfg) accessToken := "" // Step 1: 序列化完整 request({head, body} 一起加密) inputBytes, err := json.Marshal(request) if err != nil { return nil, fmt.Errorf("序列化请求失败: %v", err) } inputJson := string(inputBytes) // Step 2: SM4 CBC 加密 sm4Key := psbcutil.GenerateSM4Key() iv := psbcutil.GetSM4IV() // UISwD9fW6cFh9SNS paddedData := psbcutil.Padding([]byte(inputJson), 1) tmp, err := sm4.CBCEncrypt(sm4Key, iv, paddedData) if err != nil { return nil, fmt.Errorf("SM4加密失败: %v", err) } responseMsg := base64.StdEncoding.EncodeToString(tmp) responseMsg = addNewline(responseMsg) // 对齐 YouChuKoffee,每76字符加 \r\n // Step 3: SM2 加密 SM4 密钥(使用 copied sm2 包,对齐 YouChuKoffee 的 Sm2Encrypt) sopPubKey, err := sm2.ReadPublicKeyFromHex(cfg.SopPublicKey) if err != nil { return nil, fmt.Errorf("读取SOP公钥失败: %v", err) } encryptKeyBytes, err := psbcutil.Sm2Encrypt(sopPubKey, sm4Key) if err != nil { return nil, fmt.Errorf("SM2加密SM4密钥失败: %v", err) } encryptKey := strings.ToUpper(hex.EncodeToString(encryptKeyBytes)) // Step 4: 签名(对齐 YouChuKoffee sign():UserID = MerchantId) signContent := fmt.Sprintf("%s%s%s", responseMsg, encryptKey, accessToken) signature, err := sign(cfg.MerchantId, client.cfg.PrivateKeyCallback, signContent) if err != nil { return nil, fmt.Errorf("生成签名失败: %v", err) } result := map[string]string{ "request": responseMsg, "signature": signature, "encryptKey": encryptKey, "accessToken": accessToken, } return result, nil } // signWithUserID 使用指定 UserID 对内容进行 SM2 签名 // 对齐 YouChuKoffee 的 sign() + rSToSign():直接返回 r.Text(16) + "#" + s.Text(16),不做 base64 编码 func sign(merchantId string, privateKeyHex string, signContent string) (string, error) { privateKey, err := sm2.ReadPrivateKeyFromHex(privateKeyHex) if err != nil { return "", err } r, s, err := sm2.Sm2Sign(privateKey, []byte(signContent), []byte(merchantId), rand.Reader) if err != nil { return "", err } return rSToSign(r, s), nil } func rSToSign(r *big.Int, s *big.Int) string { rStr := r.Text(16) sStr := s.Text(16) return fmt.Sprintf("%s#%s", rStr, sStr) } // addNewline 每76个字符添加 \r\n,对齐 YouChuKoffee 的 postbank.addNewline func addNewline(str string) string { lineLength := 76 var result strings.Builder for i := 0; i < len(str); i++ { if i > 0 && i%lineLength == 0 { result.WriteString("\r\n") } result.WriteByte(str[i]) } return result.String() }