diff --git a/cmb.go b/cmb.go new file mode 100644 index 0000000..27fcaa4 --- /dev/null +++ b/cmb.go @@ -0,0 +1,402 @@ +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, "&") + + log.Println("拼接后的数据: ", build_str) + return build_str +} + +// 生成签名sign--聚合支付 +func SetSign(data map[string]interface{}, privateKey string) string { + log.Println("招行加签start") + 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) + + log.Println("加密后sign值: ", sign) + log.Println("招行加签end") + 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") + + log.Println("header信息", headerData) + + return headerData +} + +// 接口验签--各客户端请求我们接口验签 +func CheckSign(appid, app_secret, timeStamp, nonce, sign string, body map[string]interface{}) error { + log.Println("请求验签start") + + /*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 + log.Println("加密前数据: ", str) + sign_str := crypter.Md5Str(str) + log.Println("加密后数据: ", sign_str) + + if sign_str != sign { + return errors.New("签名不正确") + } + + log.Println("请求验签end") + + 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 + } + + log.Println(bytes.NewBuffer(params)) + + 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) + log.Println("拼接后的数据: ", string(ss)) + 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 +} diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b851017 --- /dev/null +++ b/go.sum @@ -0,0 +1,28 @@ +git.tetele.net/tgo/crypter v0.2.2 h1:YMQJh2Gj5Po4ZfelJUmXBKi01UbmtiSy3bmqRfnYQMo= +git.tetele.net/tgo/crypter v0.2.2/go.mod h1:vfvRLZA8+lHNgNXneOcgvVhDyuv25ZRb+C6xHOmXNx0= +github.com/ZZMarquis/gm v1.3.2 h1:lFtpzg5zeeVMZ/gKi0gtYcKLBEo9XTqsZDHDz6s3Gow= +github.com/ZZMarquis/gm v1.3.2/go.mod h1:wWbjZYgruQVd7Bb8UkSN8ujU931kx2XUW6nZLCiDE0Q= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=