接口规范
1、筛选并排序
获取所有请求参数,不包括字节类型参数,如文件、字节流,剔除 sign 字段,剔除值为空的参数,并按照第一个字符的键值 ASCII 码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值 ASCII 码递增排序,以此类推
2、拼接
将排序后的参数与其对应值,组合成“参数=参数值”的格式,并且把这些参数用 & 字符连接起来,此时生成的字符串为待签名字符串。
app_id=2021072300007148 request_body={"user_id":1} timestamp=1612776451123 version=1.0
待签名字符串
app_id=2021072300007148&request_body={"user_id":1}×tamp=1612776451123&version=1.0
3、调用签名函数
使用各自语言对应的 SHA256WithRSA签名函数利用商户私钥对待签名字符串进行签名,并进行 Base64 编码。
4、生成的签名复制给sign参数
接口Demo
需要插件:pycryptodome
接口文档格式,以下文件在同一个目录下
logo_demo目录 --- config.py 配置文件
logo_demo目录 --- rsa_crypt.py 加密文件
logo_demo目录 --- sign_demo.py sigh值文件
# coding=utf-8 import json import time from base64 import b64decode from urllib.parse import urlencode from config import APP_ID, VERSION, APP_PRIVATE_KEY, APP_PUBLIC_KEY, ZAN_PRIVATE_KEY, ZAN_PUBLIC_KEY from rsa_crypt import RSAPubCrypt, RSAPrvCrypt def main(): # 1. 非码签名验签测试 jump_page = 'https://test-group.51zan.com/into-channel' request_body = { "notify_url":"http://106.54.78.58:58082/", "partner_id": "40000000000000000000", "staff_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa24000" } test_data_dict = { "app_id": APP_ID, "request_body": json.dumps(request_body, sort_keys=True, separators=(',', ':')), "timestamp": int(time.time() * 1000), "version": VERSION, } print("test_data_dict:",test_data_dict) # 私钥签名 rsa_prv_crypt = RSAPrvCrypt(b64decode(APP_PRIVATE_KEY)) test_data_str = rsa_prv_crypt.sign_data(test_data_dict) print("test_data_str:",test_data_str) test_data_sign = rsa_prv_crypt.sign(test_data_str) print("test_data_sign:",test_data_sign) # 公钥验签 rsa_pub_crypt = RSAPubCrypt(b64decode(APP_PUBLIC_KEY)) print("公钥验签",rsa_pub_crypt.verify_sign(test_data_str, test_data_sign)) # 成功返回 True 或 失败返回 False test_data_dict['sign'] = test_data_sign query_string = urlencode(test_data_dict) print('{}?{}'.format(jump_page, query_string)) if __name__ == '__main__': main()
# coding=utf-8 import base64 import json from Crypto.Cipher import PKCS1_v1_5 from Crypto.Hash import SHA256 from Crypto.PublicKey import RSA from Crypto.Signature import pkcs1_15 # RSA加密类 class RSACrypt(object): def __init__(self, key): self.key = RSA.importKey(key) @staticmethod def sign_data(data_dict): tmp = [] print("data_dict:",data_dict) for key in sorted(data_dict.keys()): value = data_dict[key] # print("value:",value) if isinstance(value, dict) or isinstance(value, list): tmp.append('{}={}'.format(key, json.dumps(value, sort_keys=True, separators=(',', ':')))) else: tmp.append('{}={}'.format(key, value)) # print("tmp",tmp) return '&'.join(tmp) # RSA公钥 class RSAPubCrypt(RSACrypt): # RSA公钥加密 def encrypt(self, data, length=200): try: # 1024bit的证书用100,2048bit证书用200位 data = data.encode('utf-8') cipher = PKCS1_v1_5.new(self.key) res = [] for i in range(0, len(data), length): res.append(cipher.encrypt(data[i:i + length])) return str(base64.b64encode(b"".join(res)), encoding='utf-8') except: return False # RSA公钥验证签名 def verify_sign(self, data, signature): try: if isinstance(data, dict): data = self.sign_data(data) data = data.encode('utf-8') h = SHA256.new(data) pkcs1_15.new(self.key).verify(h, base64.b64decode(signature)) return True except (ValueError, TypeError): return False # RSA私钥 class RSAPrvCrypt(RSACrypt): # RSA私钥解密 def decrypt(self, encrypt_data, length=256): # 1024bit的证书用128,2048bit证书用256位 print("=====") try: cipher = PKCS1_v1_5.new(self.key) encrypt_data = base64.b64decode(encrypt_data) data = [] for i in range(0, len(encrypt_data), length): data.append(cipher.decrypt(encrypt_data[i:i + length], 'xyz')) return str(b"".join(data), encoding='utf-8') except: return False # RSA私钥生成签名 def sign(self, data): try: if isinstance(data, dict): data = self.sign_data(data) data = data.encode('utf-8') h = SHA256.new(data) signature = pkcs1_15.new(self.key).sign(h) return str(base64.b64encode(signature), encoding='utf-8') except: return False
# coding=utf-8 # 公钥 ZAN_PUBLIC_KEY = ('xxx',''sss) # 私钥 ZAN_PRIVATE_KEY = ('aa','bb') APP_ID = "20210800007148" # VERSION = "1.0" # 接口版本号,目前固定为1.0 # 测试私钥 APP_PRIVATE_KEY = "MITDDDD" # 测试公钥 APP_PUBLIC_KEY = " DDDDD"