说明
- 接口适用于 所有接入中国银联全渠道平台的商户
- 商户版银联在线支付网关产品及银联手机支付—银联手机支付—手机控件支付
- 官方文档如下
签名说明
-
银联需要验证商户上送的签名是否正确;商户收到应答,也需要验证签名是否正确,如果商户未正确验证签名,存在潜在的风险,商户自行承担因此而产生的所有损失。
-
首先,对报文中出现签名域(signature)之外的所有数据元采用key=value的形式按照名称排序,然后以&作为连接符拼接成待签名串。其次,对待签名串使用SHA-256算法做摘要,再使用银联颁发给商户的签名私钥证书中的私钥对摘要做签名操作(签名时算法选择SHA-256)。最后,对签名做Base64编码,将编码后的签名串放在签名(signature)表单域里和其他表单域一起通过HTTP Post的方式传输给银联全渠道支付平台。
-
生成签名串方法
@staticmethod
def get_sign_data(data: dict):
"""
获取签名数据
按照ASCII 字母排序 键值
{'a':1,'b':2} => 'a=1&b=1'
@param data:
@return:
"""
sort_param = sorted(
[(key, value) for key, value in data.items() if key not in ['signature']])
return "&".join("{}={}".format(k, v) for k, v in sort_param)
- 生成签名的方法
@staticmethod
def gen_sha1_rsa_sign(sing_data,
pri_key):
"""
银联RSA签名
:param sing_data: 签名字符串
:param pri_key: 签名私钥
:return:
"""
pri_key = fill_private_key_marker(pri_key) # 状态 rsa 私钥
private_key_obj = RSA.import_key(pri_key)
sign_digest = SHA1.new(sing_data.encode()) # 第一次SHA1计算
hex_sign_digest = sign_digest.hexdigest()
sign_digest = SHA1.new(hex_sign_digest.encode()) # 第二次SHA1计算
signature = pkcs1_15.new(private_key_obj).sign(sign_digest)
return base64.b64encode(signature).decode('utf-8')
验签说明
- 对于报文的验签处理机制如下:
- 首先,对报文中出现签名域(signature)之外的所有数据元采用key=value的形式按照名称排序,然后以&作为连接符拼接成待签名串。其次,对待签名串使用SHA-256算法做摘要,再使用商户入网时银联提供的验签公钥证书中的公钥对摘要和报文中的签名信息做签名验证操作。
@staticmethod
def verify_rsa_sign(sing_data,
sign,
pub_key):
"""
签名验证
:param sing_data: 签名数据
:param sign: 签名
:param pub_key: 公钥
:return:
"""
pub_key = fill_public_key_marker(pub_key)
public_key_obj = RSA.import_key(pub_key)
sign_digest = SHA1.new(sing_data.encode())
hex_sign_digest = sign_digest.hexdigest()
sign_digest = SHA1.new(hex_sign_digest.encode())
pad_num = 4 - len(sign) % 4
sign = sign + '=' * pad_num
try:
pkcs1_15.new(public_key_obj).verify(sign_digest,
base64.b64decode(sign))
return True
except (ValueError, TypeError):
return False
其他uitils函数
def fill_private_key_marker(private_key):
return add_start_end(private_key, "-----BEGIN RSA PRIVATE KEY-----\n", "\n-----END RSA PRIVATE KEY-----")
def fill_public_key_marker(public_key):
return add_start_end(public_key, "-----BEGIN PUBLIC KEY-----\n", "\n-----END PUBLIC KEY-----")
# -----BEGIN RSA PUBLIC KEY-----
def fill_public_key_marker_pem(public_key):
return add_start_end(public_key, "-----BEGIN RSA PUBLIC KEY-----\n", "\n-----END RSA PUBLIC KEY-----")
@staticmethod
def get_rsa_pri_key_from_pfx(pfx_path, pfx_password):
"""
获取 xxxxx.pfx 证书的 证书序列号
:param pfx_path:
:param pfx_password:
:return:
"""
pfx = crypto.load_pkcs12(open(pfx_path, 'rb').read(), bytes(pfx_password, encoding="utf8"))
_info = pfx.get_certificate()
serial_number = _info.get_serial_number()
res = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey())
return serial_number, res.strip()