python自行实现支付宝证书签名&验签全流程[通俗易懂]
2023-06-13 09:11:29 时间
大家好,又见面了,我是你们的朋友全栈君。
在网站使用支付宝python sdk接入支付后成功后,需要实现用户提现功能,在支付宝沙箱环境下使用sdk顺利实现提现,结果转成正式环境后报错,咨询支付宝客服后告知python sdk不支持提现,原因是提现接口需要证书签名,当时就傻眼了,感觉被支付宝沙箱玩弄了,关键是之前写的支付接口也得重写,最后无奈只能舍弃python sdk自行实现签名和验签请求支付宝接口。下面是自行实现签名和验签全流程:
证书签名需要新加alipay_root_cert_sn和app_cert_sn两个参数,这两个参数需要解析支付宝根证书(alipay_root_cert_sn)和应用公钥证书(appCerPublicKey_"app_id".crt)得到:
import OpenSSL
import re
import rsa
import base64
def sn_string():
root_file_li = open(alipayRootCert.crt', 'r').read().split('\n\n')
# 支付宝根证书中有4套证书需要一一解析
alipay_root_cert_li = []
for root_str in root_file_li:
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, root_str)
certIssue = cert.get_issuer()
try:
if not re.match(b'sha.+WithRSAEncryption', cert.get_signature_algorithm()):
# 只有符合条件的是需要的,4套证书中只有两个有用(具体为啥咱也不太清楚)
continue
except:
continue
if certIssue.OU:
sn_string = 'CN=' + certIssue.CN + ',' + 'OU=' + certIssue.OU + ',' + 'O=' + certIssue.O + ',' + \
'C=' + certIssue.C + str(cert.get_serial_number())
# 将机构名name和序列号serialNumber按照这样的顺序拼接
else:
sn_string = 'CN=' + certIssue.CN + ',' + 'O=' + certIssue.O + ',' + 'C=' + certIssue.C + str(
cert.get_serial_number())
alipay_root_cert_sn = hashlib.md5(sn_string.encode('utf-8')).hexdigest()
alipay_root_cert_li.append(alipay_root_cert_sn)
alipay_root_cert_sn = '_'.join(alipay_root_cert_li)
app_file_str = open(appCertPublicKey_xxxxxxxx.crt', 'r').read()
app_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, app_file_str)
certIssue = app_cert.get_issuer()
app_sn_string = 'CN=' + certIssue.CN + ',' + 'OU=' + certIssue.OU + ',' + 'O=' + certIssue.O + ',' + \
'C=' + certIssue.C + str(app_cert.get_serial_number())
app_cert_sn = hashlib.md5(app_sn_string.encode('utf-8')).hexdigest()
return alipay_root_cert_sn, app_cert_sn
得到alipay_root_cert_sn,app_cert_sn后,加到需要请求接口的参数中进行签名;
def alipay_sign(data_dict):
__pem_begin = '-----BEGIN RSA PRIVATE KEY-----\n'
__pem_end = '\n-----END RSA PRIVATE KEY-----'
def rsa_sign(data_dict, private_key_path):
"""SHAWithRSA
:param content: 签名内容
:type content: str
:param private_key: 私钥
:type private_key: str
:param _hash: hash算法,如:SHA-1,SHA-256
:type _hash: str
:return: 签名内容
:rtype: str
"""
private_key = _format_private_key(open(private_key_path, 'r').read())
pri_key = rsa.PrivateKey.load_pkcs1(private_key.encode('utf-8'))
params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False) # 参数字典倒排序为列表
params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) # 待签名字符串
sign_result = rsa.sign(params_str.encode('utf-8'), pri_key, 'SHA-256')
return base64.b64encode(sign_result)
def _format_private_key(private_key):
"""对私进行格式化,缺少"-----BEGIN RSA PRIVATE KEY-----"和"-----END RSA PRIVATE KEY-----"部分需要加上
:param private_key: 私钥
:return: pem私钥字符串
:rtype: str
"""
if not private_key.startswith(__pem_begin):
private_key = __pem_begin + private_key
if not private_key.endswith(__pem_end):
private_key = private_key + __pem_end
return private_key
sign = rsa_sign(data_dict, "域名"_私钥.txt')
return sign
到这里就已经完成了签名这部分,得到签名sign,将其加到请求接口中的参数“sign”中,这里需要注意的是上面得到的alipay_root_cert_sn,app_cert_sn也要加到请求接口中的参数中
支付宝网页支付需要异步验签,异步验签需要用支付宝公钥来验证支付宝传过来的参数和签名。证书签名的情况下支付宝公钥需要解析支付宝公钥证书得到(此处坑太深),下面是实现异步验签流程(这里是在Django下的接口):
def load_alipay_public_key_string():
alipay_public_key_cert_string = open(alipayCertPublicKey_RSA2.crt').read()
cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, alipay_public_key_cert_string
)
public_key = OpenSSL.crypto.dump_publickey(
OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey()
).decode("utf-8")
return public_key
def check_alipay(request):
alipay_req_dict = request.POST.dict() # 将传过来的支付宝参数转为普通字典
sign = alipay_req_dict.pop('sign') # 取出传过来的公钥
alipay_req_dict.pop('sign_type') # 去除传过来的sign_type
params = sorted(alipay_req_dict.items(), key=lambda e: e[0], reverse=False) # 取出字典元素按key的字母升序排序形成列表
message = "&".join(u"{}={}".format(k, v) for k, v in params).encode() # 将列表转为二进制参数字符串
public_key = load_alipay_public_key_string()
sign = base64.b64decode(sign)
status = bool(rsa.verify(message, sign, rsa.PublicKey.load_pkcs1_openssl_pem(public_key))) # 或者使用sdk下的verify_with_rsa(public_key, message, sign) # 验签
验签支付宝密钥签名和证书签名都一样,只是证书签名下支付宝公钥需要解析证书得到
这里不得不吐槽下支付宝文档,自行实现签名全给的是参考java sdk实现,网上证书签名和验签文章也没几篇。这里给大家分享下自己实现的全过程,希望能够避免大家少踩点坑。 最后ps:若是支付宝官方觉得我这篇python自行实现支付宝证书签名和验签可以给用户当做参考的话,拿走不谢,哈哈!!!
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/143892.html原文链接:https://javaforall.cn
相关文章
- 快速入门Python机器学习(34)
- python jieba库_Python jieba库的使用说明「建议收藏」
- python删除首行_Python删除文件第一行
- Python入门系列(六)一篇学会python函数
- Python入门系列(十一)一篇搞定python操作MySQL数据库
- python中替换字符串中字符_Python replace()函数:替换字符串中的某个字符「建议收藏」
- 编程语言与Python介绍
- java和python哪个值得学-学java好还是Python好?
- Python抓取数据_python抓取游戏数据
- python在线代码编辑器-5种最佳Python IDE和代码编辑器
- Python抛出异常_python抛出异常的作用
- python win32api sendmessage_Python win32api.SendMessage方法代码示例[通俗易懂]
- 跟着Nature ecology&evolution学python:toytree模块画进化树
- Python Redis 客户端连接池解析
- Python之ruamel.yaml模块详解(三)| ruamel.yaml与pyyaml的区别
- Python 小型项目大全 71~75
- 网络工程师学Python-5-Python 字典
- python-Python与MongoDB数据库-处理MongoDB查询结果
- python进阶Garbage collection垃圾回收2详解编程语言
- Python学习:6.python内置函数详解编程语言
- Python基础(二):列表和元组详解编程语言
- 用Python仿写MSSQL 编程体验更有趣(python仿mssql)
- python基础教程之获取本机ip数据包示例
- 一个计算身份证号码校验位的Python小程序