Open qingwg opened 5 years ago
这部分工作还没有完成吗? 我这边实现过支付宝api接口的response的签名验证。
@xluohome 大部分接口签名验证是可以的,但是碰到多维结构的,就无法验证了。就像上面的结构, JsApiResponse 包含一个 JsApi ,然后就验证失败了。
可否贴出 response 的 json 字符串 ?
提供 “通信密钥"、 "PayJS的商户号”之类必要的东东我来帮你调吧,保证能解!
@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 字符串
PHPOSS Team!
------------------ 原始邮件 ------------------ 发件人: "卿务国"notifications@github.com; 发送时间: 2019年6月26日(星期三) 上午10:40 收件人: "qingwg/payjs"payjs@noreply.github.com; 抄送: ""phposs@qq.com; "Mention"mention@noreply.github.com; 主题: Re: [qingwg/payjs] 求助:SDK签名验证算法Bug (#2)
@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 字符串
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
@xluohome 我发你这个邮箱了:phposs@qq.com
校验响应得到的bytes数据可以试着调用动态语言吗, 我发现golang很难完成这个工作
你可以用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
刚才用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
求助:此SDK签名验证算法与微信相同。但是如果碰到[]string等,或多维结构体,例如JSAPI支付接口的Response,则无法正确签名验证。该如何处理呢?
目前有存在多为结构签名验证的地方都取消了验证判断
分别是: JSAPI支付接口
获取用户详情
异步通知
签名验证代码则如下:
签名验证代码
代码有点乱,一直在调试。这段代码无法正确验证多维结构,例如无法验证下面结构:
官方给出的回复为: