Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

求助:SDK签名验证算法Bug #2

Open
qingwg opened this issue Feb 20, 2019 · 10 comments
Open

求助:SDK签名验证算法Bug #2

qingwg opened this issue Feb 20, 2019 · 10 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@qingwg
Copy link
Owner

qingwg commented Feb 20, 2019

求助:此SDK签名验证算法与微信相同。但是如果碰到[]string等,或多维结构体,例如JSAPI支付接口的Response,则无法正确签名验证。该如何处理呢?

目前有存在多为结构签名验证的地方都取消了验证判断

分别是:
JSAPI支付接口

获取用户详情

异步通知

签名验证代码则如下:

签名验证代码

代码有点乱,一直在调试。这段代码无法正确验证多维结构,例如无法验证下面结构:

// JsApiResponse
type JsApiResponse struct {
	ReturnCode   int    `json:"return_code"`    //Y	0:失败 1:成功
	ReturnMsg    string `json:"return_msg"`     //Y	失败原因
	PayJSOrderID string `json:"payjs_order_id"` //Y	PAYJS 侧订单号
	JsApi        JsApi  `json:"jsapi"`          //N	用于发起支付的支付参数
	Sign         string `json:"sign"`           //Y	数据签名
}

// JsApi
type JsApi struct {
	AppID     string `json:"appId"`
	TimeStamp string `json:"timeStamp"`
	NonceStr  string `json:"nonceStr"`
	Package   string `json:"package"`
	SignType  string `json:"signType"`
	PaySign   string `json:"paySign"`
}

官方给出的回复为:

拼接方法形如:
user[name]=&user[age]=test2&user[sex]=test3,
在php中直接使用http_build_query实现的,
如其他语言请参照该方法:http://php.net/manual/zh/function.http-build-query.php
@qingwg qingwg changed the title SDK签名验证算法Bug 求助:SDK签名验证算法Bug Feb 20, 2019
@qingwg qingwg added the bug Something isn't working label Feb 20, 2019
@qingwg qingwg added the help wanted Extra attention is needed label May 10, 2019
@xluohome
Copy link

这部分工作还没有完成吗? 我这边实现过支付宝api接口的response的签名验证。

@qingwg
Copy link
Owner Author

qingwg commented May 23, 2019

@xluohome 大部分接口签名验证是可以的,但是碰到多维结构的,就无法验证了。就像上面的结构, JsApiResponse 包含一个 JsApi ,然后就验证失败了。

@xluohome
Copy link

可否贴出 response 的 json 字符串 ?

@a7a2
Copy link

a7a2 commented May 30, 2019

提供 “通信密钥"、 "PayJS的商户号”之类必要的东东我来帮你调吧,保证能解!

@qingwg
Copy link
Owner Author

qingwg commented Jun 26, 2019

@xluohome
{"return_code":1,"return_msg":"SUCCESS","user":{"subscribe":1,"openid":"o7LFAwXrAJip_0GEhcHWKqM2LPxw","nickname":"卿务国","sex":1,"city":"长沙","country":"中国","province":"湖南","language":"zh_CN","headimgurl":"http://thirdwx.qlogo .cn/mmopen/Z7gAdqBjbbFMvGpwQvsVSVdpxjalBfg6V9cm2svg7yUtFgQjSVOsnYFSBG7401bZJPNoc9ZNfNEC0LgtBtQO8DaCtcu1icOnL/132","subscribe_time":1544522310,"remark":"","groupid":0,"tagid_list":[],"subscribe_scene":"ADD_SCENE_SEARCH","qr_scene":0, "qr_scene_str":""},"sign":"3C0427D9D501D856F29274B9EF11F995"}
不好意思,这么晚回复。
上面就是用户获取详情接口的 response 的 json 字符串

@xluohome
Copy link

xluohome commented Jun 26, 2019 via email

@qingwg
Copy link
Owner Author

qingwg commented Jun 26, 2019

@xluohome 我发你这个邮箱了:phposs@qq.com

@markity
Copy link

markity commented Jan 28, 2020

校验响应得到的bytes数据可以试着调用动态语言吗, 我发现golang很难完成这个工作

@markity
Copy link

markity commented Jan 28, 2020

你可以用go调用python, 我刚才实现了一下, 应该能用

import json
import hashlib
import importlib
import sys

json_str = '{"name":"markity","tags":["golang","python"],"detail":{"age":16,"phone":"11011011011", "birthday":"88888888"},"sign":"3123123"}'
mchKey = "demoMchKey"

try:
    json_obj = json.loads(json_str)
except:
    sys.exit(1)

keys = list(json_obj.keys())

# 对于某些特殊情况, 如重复退款, 即使return_code为1也无sign
if "sign" not in keys:
    print("true")
    sys.exit(0)

origin_sign = json_obj["sign"]
keys.remove("sign")
keys.sort()


def walk(perfix, l, keys, struct):
    # walk 迭代遍历, 将key=val字符串追加入l列表中
    for k in keys:
        v = struct[k]
        if v == None:
            continue
        if type(v) == dict:
            _keys = list(v.keys())
            _keys.sort()
            walk(k, kvList, _keys, v)
            continue
        if type(v) == list:
            walk(k, kvList, range(len(v)), v)
            continue
        if perfix == "":
            kvList.append("{}={}".format(k, v))
        if perfix != "":
            kvList.append("{}[{}]={}".format(perfix, k, v))


kvList = []
walk("", kvList, keys, json_obj)
kvChain = '&'.join(kvList)
print(kvChain)

realSign = hashlib.md5(kvChain.encode("utf-8")).hexdigest().upper()
print(realSign)

输出

detail[age]=16&detail[birthday]=88888888&detail[phone]=11011011011&name=markity&tags[0]=golang&tags[1]=python
782F7639850D47C4C79758B412D51B89

@markity
Copy link

markity commented Jan 29, 2020

刚才用go重写了一遍, 你看看

package main

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"sort"
	"strings"
)

var jsonStr = `
{
	"name":"Markity",
	"age":16,
	"interests":["python","golang"],
	"friends":[
		{"name":"Jack","age":17,"interests":["lua"]},
		{"name":"Mary","age":15,"interests":["java"]}
	],
	"compary":
	{
		"name":"Microsoft",
		"workders":[
			{"name":"Peter","age":22},
			{"name":"Eric","age":23}
		]
	},
	"sign":"XXX"
}
`

var mchKey = "xxxxx"

func main() {
	jsonObj := make(map[string]interface{})
	json.Unmarshal([]byte(jsonStr), &jsonObj)

	_, ok := jsonObj["sign"]
	// 某些特殊情况如重复退款, 即使return_code为1, 也没有sign, 这时直接return true
	if !ok {
		fmt.Println("true")
		return
	}
	originSign := jsonObj["sign"].(string)
	delete(jsonObj, "sign")

	// 将key=val追加入kvList中
	kvList := make([]string, 0)
	var walkMap func(string, interface{}, interface{}) = nil
	walkMap = func(perfix string, obj interface{}, ks interface{}) {
		// obj为[]interface{}或者map[string]interface{}类型, ks为[]string或者[]int类型
		switch keys := ks.(type) {
		case []string:
			// 当ks为[]string类型时, obj为map[string]interface{}类型
			object := obj.(map[string]interface{})
			for _, key := range keys {
				v := object[key]
				// 根据签名算法, 值为null的不计入
				if v == nil {
					continue
				}
				switch value := v.(type) {
				// 若v为复合类型, 进入继续遍历
				case []interface{}:
					_perfix := ""
					if perfix == "" {
						_perfix = key
					} else {
						_perfix = fmt.Sprintf("%v[%v]", perfix, key)
					}
					_keys := make([]int, len(value))
					for i := 0; i < len(value); i++ {
						_keys = append(_keys, i)
					}
					walkMap(_perfix, value, _keys)
					continue
				case map[string]interface{}:
					_perfix := ""
					if perfix == "" {
						_perfix = key
					} else {
						_perfix = fmt.Sprintf("%v[%v]", perfix, key)
					}
					_keys := make([]string, 0)
					for __key, _ := range value {
						_keys = append(_keys, __key)
					}
					sort.Strings(_keys)
					walkMap(_perfix, value, _keys)
					continue
				}
				if perfix == "" {
					kvList = append(kvList, fmt.Sprintf("%v=%v", key, v))
				} else {
					kvList = append(kvList, fmt.Sprintf("%v[%v]=%v", perfix, key, v))
				}
			}
		case []int:
			// 当ks为[]int, obj为[]interface{}
			object := obj.([]interface{})
			for key := 0; key < len(object); key++ {
				v := object[key]
				if v == nil {
					continue
				}
				switch value := v.(type) {
				// 若v为复合类型, 进入继续遍历
				case []interface{}:
					_perfix := fmt.Sprintf("%v[%v]", perfix, key)
					_keys := make([]int, len(value))
					for i := 0; i < len(value); i++ {
						_keys = append(_keys, i)
					}
					walkMap(_perfix, value, _keys)
					continue
				case map[string]interface{}:
					_perfix := fmt.Sprintf("%v[%v]", perfix, key)
					_keys := make([]string, 0)
					for __key, _ := range value {
						_keys = append(_keys, __key)
					}
					walkMap(_perfix, value, _keys)
					continue
				}
			}
		}
	}
	keys := make([]string, 0)
	for key, _ := range jsonObj {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	walkMap("", jsonObj, keys)
	kvList = append(kvList, "key="+mchKey)
	kvChain := strings.Join(kvList, "&")
	fmt.Println(kvChain)
	realSign := fmt.Sprintf("%x", md5.Sum([]byte(kvChain)))
	if realSign == originSign {
		fmt.Println("true")
	} else {
		fmt.Println("false")
	}
}

输出

age=16&compary[name]=Microsoft&compary[workders][0][name]=Peter&compary[workders][0][age]=22&compary[workders][1][name]=Eric&compary[workders][1][age]=23&friends[0][name]=Jack&friends[0][age]=17&friends[1][name]=Mary&friends[1][age]=15&name=Markity&key=xxxxx
false

Repository owner deleted a comment from roneyfraga Mar 18, 2024
@github-staff github-staff deleted a comment from YogaDarma182 Sep 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants
@qingwg @a7a2 @xluohome @markity and others