package helper
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"errors"
|
|
"git.tetele.net/tgo/crypter"
|
|
"github.com/ZZMarquis/gm/sm2"
|
|
"github.com/ZZMarquis/gm/sm3"
|
|
jsoniter "github.com/json-iterator/go"
|
|
"io/ioutil"
|
|
"log"
|
|
"math/big"
|
|
"net/http"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
|
|
// 合并map数据
|
|
func MergeMapData(x, y map[string]interface{}) map[string]interface{} {
|
|
|
|
n := make(map[string]interface{})
|
|
|
|
for i, v := range x {
|
|
for j, w := range y {
|
|
if i == j {
|
|
n[i] = w
|
|
|
|
} else {
|
|
if _, ok := n[i]; !ok {
|
|
n[i] = v
|
|
}
|
|
if _, ok := n[j]; !ok {
|
|
n[j] = w
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
// 针对传参为json或者数组类型的字段,进行字符串排序
|
|
func HttpBuildSort(data map[string]interface{}) string {
|
|
keySlice := []string{}
|
|
|
|
for k, _ := range data {
|
|
keySlice = append(keySlice, k)
|
|
}
|
|
// 排序字符串
|
|
sort.Strings(keySlice)
|
|
|
|
res := make(map[string]interface{}, 0)
|
|
|
|
for _, value := range keySlice {
|
|
data_val := ToStr(data[value])
|
|
|
|
if value != "" && data_val != "" {
|
|
res[value] = data_val
|
|
}
|
|
}
|
|
|
|
res_byte, _ := json.Marshal(res)
|
|
|
|
return string(res_byte)
|
|
}
|
|
|
|
// body参数处理
|
|
func FormatBodyData(body map[string]interface{}) map[string]interface{} {
|
|
data := map[string]interface{}{}
|
|
|
|
if len(body) > 0 {
|
|
for k, v := range body {
|
|
typeOfA := reflect.TypeOf(v)
|
|
|
|
if typeOfA.Kind().String() == "map" {
|
|
build_str_format, _ := InterfaceToMapInterface(v)
|
|
|
|
if len(build_str_format) < 1 {
|
|
continue
|
|
}
|
|
// json键值对处理
|
|
data[k] = HttpBuildSort(build_str_format)
|
|
// log.Println("排序后的数据: ", data[k])
|
|
} else if typeOfA.Kind().String() == "slice" {
|
|
val_slice := []interface{}{}
|
|
// json键值对处理
|
|
for _, val_inter := range v.([]interface{}) {
|
|
val_map, _ := InterfaceToMapInterface(val_inter)
|
|
each_val := HttpBuildSort(val_map)
|
|
val_slice = append(val_slice, each_val)
|
|
}
|
|
|
|
if len(val_slice) < 1 {
|
|
continue
|
|
}
|
|
|
|
data[k] = val_slice
|
|
// log.Println("排序后的数据: ", data[k])
|
|
} else {
|
|
data[k] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
// 字符串排序拼接
|
|
func CmbHttpBuildQuery(data map[string]interface{}, filter_sign bool) string {
|
|
keySlice := []string{}
|
|
// 剔除sign字段
|
|
for k, _ := range data {
|
|
if filter_sign && k == "sign" {
|
|
continue
|
|
}
|
|
keySlice = append(keySlice, k)
|
|
}
|
|
// 排序字符串
|
|
sort.Strings(keySlice)
|
|
|
|
var query string
|
|
|
|
for _, value := range keySlice {
|
|
if value != "" && data[value] != "" {
|
|
query += StringJoin(value, "=", ToStr(data[value]), "&")
|
|
}
|
|
}
|
|
|
|
build_str := strings.Trim(query, "&")
|
|
|
|
return build_str
|
|
}
|
|
|
|
// 生成签名sign--聚合支付
|
|
func SetSign(data map[string]interface{}, privateKey string) string {
|
|
build_str := CmbHttpBuildQuery(data, true)
|
|
|
|
priv := new(sm2.PrivateKey)
|
|
priv.Curve = sm2.GetSm2P256V1()
|
|
dBytes, _ := hex.DecodeString(privateKey)
|
|
priv.D = new(big.Int).SetBytes(dBytes)
|
|
|
|
signature, err := sm2.Sign(priv, []byte("1234567812345678"), []byte(build_str))
|
|
|
|
if err != nil {
|
|
log.Println("国密加密异常", err)
|
|
return ""
|
|
}
|
|
|
|
sign := base64.StdEncoding.EncodeToString(signature)
|
|
|
|
return sign
|
|
}
|
|
|
|
// 创建请求头信息--聚合支付
|
|
func HeaderInfo(appid, secret, sign string) map[string]string {
|
|
if appid == "" || secret == "" || sign == "" {
|
|
return map[string]string{}
|
|
}
|
|
|
|
headerData := map[string]string{}
|
|
|
|
headerData["appid"] = appid
|
|
headerData["sign"] = sign
|
|
headerData["secret"] = secret
|
|
headerData["timestamp"] = ToStr(time.Now().Unix())
|
|
|
|
build_str := CmbHttpBuildQuery(MapStringToInterface(headerData), false)
|
|
|
|
headerData["apisign"] = crypter.Md5Str(build_str)
|
|
|
|
delete(headerData, "sign")
|
|
delete(headerData, "secret")
|
|
|
|
return headerData
|
|
}
|
|
|
|
// 接口验签--各客户端请求我们接口验签
|
|
func CheckSign(appid, app_secret, timeStamp, nonce, sign string, body map[string]interface{}) error {
|
|
/*time_now := time.Now().Unix()
|
|
|
|
if time_now-ToInt64(timeStamp) > 10 {
|
|
return errors.New("请求时间超出范围,拒绝访问")
|
|
}*/
|
|
|
|
sign_info := map[string]interface{}{
|
|
"appid": appid,
|
|
"timestamp": timeStamp,
|
|
"nonce": nonce,
|
|
}
|
|
|
|
if len(body) > 0 {
|
|
body_format := FormatBodyDataNoList(body)
|
|
sign_info = MergeMapData(sign_info, body_format)
|
|
}
|
|
|
|
str := CmbHttpBuildQuery(sign_info, false) + "&" + app_secret
|
|
sign_str := crypter.Md5Str(str)
|
|
|
|
if sign_str != sign {
|
|
return errors.New("签名不正确")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// post 请求
|
|
func PostJsonData(url string, param interface{}, headerinfo map[string]string) ([]byte, error) {
|
|
httpClient := &http.Client{}
|
|
params, err := json.Marshal(param)
|
|
|
|
if err != nil {
|
|
return []byte(""), err
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(params))
|
|
if err != nil {
|
|
return []byte(""), err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
if len(headerinfo) > 0 {
|
|
for k, v := range headerinfo {
|
|
req.Header[k] = []string{v}
|
|
}
|
|
}
|
|
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return []byte(""), err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return []byte(""), err
|
|
}
|
|
|
|
return body, nil
|
|
}
|
|
|
|
func SetSignsm3(data map[string]interface{}) string {
|
|
build_str := HttpBuildjson(data, false)
|
|
h := sm3.New()
|
|
h.Write([]byte(build_str))
|
|
sum := h.Sum(nil)
|
|
sign := hex.EncodeToString(sum)
|
|
return sign
|
|
}
|
|
|
|
func CashierHeaderInfoCompany(appid, secret, sign, private_key string) map[string]string {
|
|
if appid == "" || secret == "" || sign == "" || private_key == "" {
|
|
return map[string]string{}
|
|
}
|
|
|
|
headerData := map[string]string{}
|
|
|
|
headerData["appid"] = appid
|
|
headerData["sign"] = sign
|
|
headerData["secret"] = secret
|
|
headerData["timestamp"] = ToStr(time.Now().Unix())
|
|
headerData["apisign"] = SetSignhex(headerData, private_key)
|
|
headerData["verify"] = "SM3withSM2"
|
|
delete(headerData, "secret")
|
|
return headerData
|
|
}
|
|
|
|
// 生成签名sign
|
|
func SetSignhex(data map[string]string, privateKey string) string {
|
|
build_str := CmbHttpBuildQuery(MapStringToInterface(data), false)
|
|
priv := new(sm2.PrivateKey)
|
|
priv.Curve = sm2.GetSm2P256V1()
|
|
dBytes, _ := hex.DecodeString(privateKey)
|
|
priv.D = new(big.Int).SetBytes(dBytes)
|
|
sign, _ := sm2.Sign(priv, []byte("1234567812345678"), []byte(build_str))
|
|
sign = SM2Asn1ToRaw(sign)
|
|
return hex.EncodeToString(sign)
|
|
}
|
|
func SM2Asn1ToRaw(data []byte) []byte {
|
|
i := 3
|
|
ret := []byte{}
|
|
if data[i] == 32 {
|
|
i = i + 1
|
|
} else if data[i] == 33 {
|
|
i = i + 2
|
|
}
|
|
for k := 0; k < 32; k++ {
|
|
ret = append(ret, data[i+k])
|
|
}
|
|
i = i + 32
|
|
i = i + 1
|
|
if data[i] == 32 {
|
|
i = i + 1
|
|
} else if data[i] == 33 {
|
|
i = i + 2
|
|
}
|
|
for k := 0; k < 32; k++ {
|
|
ret = append(ret, data[i+k])
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// 字符串排序拼接
|
|
func HttpBuildjson(data map[string]interface{}, filter_sign bool) string {
|
|
keySlice := []string{}
|
|
res := map[string]interface{}{}
|
|
// 剔除sign字段
|
|
for k, _ := range data {
|
|
if filter_sign && k == "sign" {
|
|
continue
|
|
}
|
|
keySlice = append(keySlice, k)
|
|
}
|
|
// 排序字符串
|
|
sort.Strings(keySlice)
|
|
for _, value := range keySlice {
|
|
if value != "" && data[value] != "" {
|
|
res[value] = data[value]
|
|
}
|
|
}
|
|
ss, _ := json.Marshal(res)
|
|
return string(ss)
|
|
}
|
|
|
|
// body参数处理
|
|
func FormatBodyDataNoList(body map[string]interface{}) map[string]interface{} {
|
|
data := map[string]interface{}{}
|
|
|
|
if len(body) > 0 {
|
|
for k, v := range body {
|
|
typeOfA := reflect.TypeOf(v)
|
|
|
|
if typeOfA.Kind().String() == "map" || typeOfA.Kind().String() == "slice" {
|
|
continue
|
|
} else {
|
|
if typeOfA.Kind().String() == "string" {
|
|
if ToStr(v) != "" {
|
|
data[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
// 聚合支付异步回调验签
|
|
func JhNotifyCheckSign(sign, public_key string, data map[string]string) bool {
|
|
build_sort_str := CmbHttpBuildQuery(MapStringToInterface(data), true)
|
|
pub_x := ""
|
|
pub_y := ""
|
|
// 私钥截取
|
|
if len(public_key) == 128 {
|
|
pub_x = public_key[:64]
|
|
pub_y = public_key[64:]
|
|
} else if len(public_key) == 130 && (public_key[:2] == "04") {
|
|
public_key = public_key[2:]
|
|
pub_x = public_key[:64]
|
|
pub_y = public_key[64:]
|
|
} else {
|
|
log.Println("异步验签公钥出错", public_key)
|
|
return false
|
|
}
|
|
|
|
sign_b64, _ := base64.StdEncoding.DecodeString(sign)
|
|
pub := new(sm2.PublicKey)
|
|
pub.Curve = sm2.GetSm2P256V1()
|
|
xBytes, _ := hex.DecodeString(pub_x)
|
|
yBytes, _ := hex.DecodeString(pub_y)
|
|
pub.X = new(big.Int).SetBytes(xBytes)
|
|
pub.Y = new(big.Int).SetBytes(yBytes)
|
|
result := sm2.Verify(pub, []byte("1234567812345678"), []byte(build_sort_str), sign_b64)
|
|
|
|
if !result {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|