PaymentCenter/app/third/paymentService/psbc/encrypt.go

107 lines
3.4 KiB
Go
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.

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()
}