一,前序
今天在学习sign签名的的时候,了解了下常用的sign加密算法,突然心血来潮,想用python试着将签名生成的通用步骤用代码实现出来,虽然中间经历了一点小波折,请教了开发同事后,按照他给的思路建议,搞定了。我实现的是微信支付的签名算法规则。
二,加密业务规则
签名生成的通用步骤如下:
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
1.参数名ASCII码从小到大排序(字典序);
2.如果参数的值为空不参与签名;
3.参数名区分大小写;
4.验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
5.微信接口可能增加字段,验证签名时必须支持增加的扩展字段。
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
三,代码设计
3.1 参数类型确定
由微信支付签名生成规则描述可知,需要加密的参数数据是按照键值对的形式拼接,且参数名区分大小写,将参数数据定义为字典Dict类型特别合适,由于python本身区分大小写,字典键值也区分大小写。
#如代码所示:
>>> data={'q':'apple','Q':'orange'}
>>> data['q']
'apple'
>>> data['Q']
'orange'
3.2 参数设计
为了满足"参数的值为空不参与签名,传送的sign参数不参与签名,接口参数支持扩展”的签名算法规则,参数字典data在设计的时候除了要包含微信支付接口提供的必传参数外,另外我补充了参数值为空和参数名为sign的参数,如下:
#string1,string2用户扩展,增加参数名sign的参数
data={
'appid':'wxd930ea5d5a258f4f',
'mch_id':'10000100',
'device_info':'1000',
'body':'test',
'nonce_str':'ibuaiVcKdpRxkhJA',
'string1':'',
'string2':'',
'sign':'fdsfdhgjghjf'
}
3.3 自定义函数设计
参数data确定下来后,由于存在参数名为sign或者参数值为空的参数,这两种参数是不参与签名的。需要写一个函数,用于排除参数名为sign或者参数值为空的参数。另外参与签名的参数生成后,参数名要按照ASCII进行从小到大排序,然后跟参数值进行拼接,这里需要设计一个拼接函数。拼接函数需要实现参数名排序,python数据类型中,字典没有排序功能,但是列表有一个sort()函数可实现对象排序,因此要把字典的键值单独取出来存放到列表中进行排序,排序完成后再根据键名取字典对应的键值。
3.4 加密函数
需要加密的字符串生成后,可以利用python的hashlib模块对字符串进行加密。Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等,这里我用md5加密。加密后把字符串转换成大写,生成sign签名。
3.5 签名实现逻辑
1.将传的参数定义成字典类型数据A。
2.排除参数名为sign或者参数值为空的参数,另存为新的字典类型数据B。
3.将参数名用ASCII进行从小到大排序,参数名保存成列表对象。
4.用排序完成的参数名循环遍历字典数据B,跟参数值拼接。参数名1=参数值&参数名2=参数值&...
5.参数拼接完成,每个商户的key值也需要拼接,生成拼接API秘钥。
6.拼接完成的字符串,MD5加密,使用hashlib模块。
7.将加密得到的字符串所有字符转换为大写,得到sign值signValue。
四,代码实现
完整代码如下:
"""
接口名称:微信支付
实现目标:微信支付sign签名MD5加密
签名算法规则:
1.参数名ASCII码从小到大排序(字典序)
2.如果参数的值为空不参与签名
3.参数名区分大小写
4.验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验
5.微信接口可能增加字段,验证签名时必须支持增加的扩展字段
"""
#导入数据处理加密的包
import hashlib
keyString="192006250b4c09247ec02edce69f6a2d"
#所有发送或者接收到的数据定义为字典类型数据
data={
'appid':'wxd930ea5d5a258f4f',
'mch_id':'10000100',
'device_info':'1000',
'body':'test',
'nonce_str':'ibuaiVcKdpRxkhJA',
'string1':'',
'string2':'',
'sign':'fdsfdhgjghjf'
}
#定义函数作用:去除参数的值为空或者参数名为sign的数据,返回参与签名的字典类型数据
def GetSignData(data):
signData={}
for key, value in data.items():
if value != "" and key != "sign":
signData[key] = value
return signData
#对参数按照key=value的格式,并按照参数名ASCII字典序排序拼接成字符串stringA,最后拼接上key,返回拼接API密钥。
def SignString(signData,key):
#定义空列表
list=[]
# 定义空字符串
stringA=""
#循环遍历字典数据的键值,取出存放到列表中
for key in signData.keys():
list.append(key)
#对列表的对象进行排序,默认升序,即按照ASCII码从小到大排序
list.sort()
#循环遍历排序后的列表,根据键值取出字典键对应的值
for i in list:
stringA += i+"="+signData[i]+"&"
#参数拼接成需要加密的字符串
stringA += "key"+"="+keyString
return stringA
#调用GetSignData函数,获取参与签名的参数,返回新的字典数据
signData=GetSignData(data)
#调用函数,返回需要加密的字符串
signBody=SignString(signData,keyString)
print(signBody)
#创建对象md
md=hashlib.md5()
#对stringA字符串进行编码
md.update(signBody.encode('utf-8'))
#数据加密
signValue=md.hexdigest()
#把加密的结果,小写转换成大写,upper函数
signValue=signValue.upper()
print(signValue)
代码演示:
用python代码编写生成的sign签名与微信支付网页的签名一样,说明代码正确,如图: