2024-06-19 18:32:34 +08:00
|
|
|
package postbank
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/ZZMarquis/gm/sm4"
|
|
|
|
"math/big"
|
|
|
|
"qteam/app/utils/postbank/internal/sm2"
|
|
|
|
"qteam/app/utils/postbank/internal/util"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
func checkInData(reqData map[string]string, key string) (string, error) {
|
|
|
|
data, ok := reqData[key]
|
|
|
|
if !ok {
|
|
|
|
return "", errors.New("请求数据中不存在" + key)
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Decrypt(merchantId, privateKey, sopPublicKey, respJson string, isRequest bool) (string, error) {
|
|
|
|
var reqData map[string]string
|
|
|
|
err := json.Unmarshal([]byte(respJson), &reqData)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2024-06-25 10:12:48 +08:00
|
|
|
reqData["accessToken"] = ""
|
2024-06-19 18:32:34 +08:00
|
|
|
keys := [4]string{}
|
|
|
|
if isRequest {
|
|
|
|
keys = [4]string{"request", "signature", "encryptKey", "accessToken"}
|
|
|
|
} else {
|
|
|
|
keys = [4]string{"response", "signature", "encryptKey", "accessToken"}
|
|
|
|
}
|
|
|
|
var inEncryptKey, inAccessToken, inData, inSignature string
|
|
|
|
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
data, err := checkInData(reqData, keys[i])
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
switch keys[i] {
|
|
|
|
case "request", "response":
|
|
|
|
inData = data
|
|
|
|
case "signature":
|
|
|
|
inSignature = data
|
|
|
|
case "encryptKey":
|
|
|
|
inEncryptKey = data
|
|
|
|
case "accessToken":
|
|
|
|
inAccessToken = data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
checked := verify(fmt.Sprintf("%s%s%s", inData, inEncryptKey, inAccessToken), inSignature, sopPublicKey, merchantId)
|
|
|
|
if !checked {
|
|
|
|
return "", errors.New("签名验证失败")
|
|
|
|
}
|
|
|
|
|
|
|
|
priKey, err := sm2.ReadPrivateKeyFromHex(privateKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.New("读取私钥失败")
|
|
|
|
}
|
|
|
|
hexEncryptKey, err := hex.DecodeString(inEncryptKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.New("解密sm4key失败")
|
|
|
|
}
|
|
|
|
sm4Key, err := util.Sm2Decrypt(priKey, hexEncryptKey)
|
|
|
|
|
|
|
|
request, _ := base64.StdEncoding.DecodeString(inData)
|
|
|
|
|
|
|
|
encryptedSm4Key, err := sm4.CBCDecrypt(sm4Key, util.GetSM4IV(), request)
|
|
|
|
|
|
|
|
return string(util.Padding(encryptedSm4Key, 0)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Encrypt(merchantId, privateKey, sopPublicKey, inputJson, token string, isRequest bool) (string, error) {
|
|
|
|
sm4Key := util.GenerateSM4Key()
|
|
|
|
iv := util.GetSM4IV()
|
|
|
|
tmp, err := sm4.CBCEncrypt(sm4Key, iv, util.Padding([]byte(inputJson), 1))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
responseMsg := base64.StdEncoding.EncodeToString(tmp)
|
|
|
|
responseMsg = addNewline(responseMsg)
|
|
|
|
|
|
|
|
pubKey, err := sm2.ReadPublicKeyFromHex(sopPublicKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.New("读取私钥失败")
|
|
|
|
}
|
|
|
|
encryptKeyBytes, err := util.Sm2Encrypt(pubKey, sm4Key)
|
|
|
|
encryptKey := strings.ToUpper(hex.EncodeToString(encryptKeyBytes))
|
|
|
|
|
|
|
|
//accessToken := util.GenAccessToken(token)
|
|
|
|
accessToken := token
|
|
|
|
signContent := fmt.Sprintf("%s%s%s", responseMsg, encryptKey, accessToken)
|
|
|
|
signature, err := sign(merchantId, privateKey, signContent)
|
|
|
|
|
|
|
|
var reqData map[string]string
|
|
|
|
|
|
|
|
if isRequest {
|
|
|
|
reqData = map[string]string{
|
|
|
|
"request": responseMsg,
|
|
|
|
"signature": signature,
|
|
|
|
"encryptKey": encryptKey,
|
|
|
|
"accessToken": accessToken,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reqData = map[string]string{
|
|
|
|
"response": responseMsg,
|
|
|
|
"signature": signature,
|
|
|
|
"encryptKey": encryptKey,
|
|
|
|
"accessToken": accessToken,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonStr, err := json.Marshal(reqData)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(jsonStr), err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateKey 生成密钥对
|
|
|
|
func GenerateKey() (string, string) {
|
|
|
|
pri, _ := sm2.GenerateKey(rand.Reader)
|
|
|
|
hexPri := pri.D.Text(16)
|
|
|
|
// 获取公钥
|
|
|
|
publicKeyHex := publicKeyToString(&pri.PublicKey)
|
|
|
|
return strings.ToUpper(hexPri), publicKeyHex
|
|
|
|
}
|
|
|
|
|
|
|
|
// publicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
|
|
|
func publicKeyToString(publicKey *sm2.PublicKey) string {
|
|
|
|
xBytes := publicKey.X.Bytes()
|
|
|
|
yBytes := publicKey.Y.Bytes()
|
|
|
|
|
|
|
|
// 确保坐标字节切片长度相同
|
|
|
|
byteLen := len(xBytes)
|
|
|
|
if len(yBytes) > byteLen {
|
|
|
|
byteLen = len(yBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 为坐标补齐前导零
|
|
|
|
xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
|
|
|
|
yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)
|
|
|
|
|
|
|
|
// 添加 "04" 前缀
|
|
|
|
publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)
|
|
|
|
|
|
|
|
return strings.ToUpper(hex.EncodeToString(publicKeyBytes))
|
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
|
|
|
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 verify(content string, signature string, publicKeyStr string, merchantId string) bool {
|
|
|
|
pubKey, err := sm2.ReadPublicKeyFromHex(publicKeyStr)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("pubKeyBytes sm2 ReadPublicKeyFromHex err: %v", err))
|
|
|
|
}
|
|
|
|
r, s := signToRS(signature)
|
|
|
|
return sm2.Sm2Verify(pubKey, []byte(content), []byte(merchantId), r, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
func signToRS(signStr string) (*big.Int, *big.Int) {
|
|
|
|
signSub := strings.Split(signStr, "#")
|
|
|
|
if len(signSub) != 2 {
|
|
|
|
panic(fmt.Sprintf("err rs: %x", signSub))
|
|
|
|
}
|
|
|
|
r, _ := new(big.Int).SetString(signSub[0], 16)
|
|
|
|
s, _ := new(big.Int).SetString(signSub[1], 16)
|
|
|
|
return r, s
|
|
|
|
}
|
|
|
|
|
|
|
|
func rSToSign(r *big.Int, s *big.Int) string {
|
|
|
|
rStr := r.Text(16)
|
|
|
|
sStr := s.Text(16)
|
|
|
|
return fmt.Sprintf("%s#%s", rStr, sStr)
|
|
|
|
}
|