python 工行e企付、工行签名生成、工行私钥PKCS8转PKCS1例子

工行有java、python、C#的SDK,项目是用python写的,遇到的坑先列举起来

1.工行给的私钥是PKCS8格式,这个是java专用的,其他语言需要转为PKCS1格式,推荐网上是链接: 在线PKCS8转PKCS1
2.工行的签名是需要加上接口的URL,不仅仅是对参数进行加密
3.参数名与参数值都要做urlEncode,sign签名也是需要的

签名生成

签名原文构造
①筛选

获取所有请求参数,不包括字节型参数,如文件、字节流,剔除sign字段。

②排序

将筛选的参数按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。

③拼接

将排序后的参数与其对.值,组合成“参数=参数值”的格式,并且把这些参数用&字符连接来,此时生成的字符串为待签名字符串。

④调用签名函数

现将拼接后的参数,按照编码类型处理为byte数组,使用各自语言对应的RSA签名函数利用商户私钥对待签名字符串进行签名,并进行Base64编码。

# -*- coding: utf-8 -*- SignatureUtils.py

import base64
import json
import rsa




def get_sign_content(all_params):
    sign_content = ""
    for (k, v) in sorted(all_params.items()):
        value = v
        if not isinstance(value, str):
            value = json.dumps(value, ensure_ascii=False)
        sign_content += ("&" + k + "=" + value)
    sign_content = sign_content[1:]
    return sign_content


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-----")


def sign_with_rsa(private_key, sign_content, charset):
    sign_content = sign_content.encode(charset)
    private_key = fill_private_key_marker(private_key)
    signature = rsa.sign(sign_content, rsa.PrivateKey.load_pkcs1(private_key, format='PEM'), 'SHA-1')
    sign = base64.b64encode(signature)
    sign = str(sign, encoding=charset)

    return sign


def sign_with_rsa2(private_key, sign_content, charset):
    sign_content = sign_content.encode(charset)

    private_key = fill_private_key_marker(private_key)
    signature = rsa.sign(sign_content, rsa.PrivateKey.load_pkcs1(private_key, format='PEM'), 'SHA-256')
    sign = base64.b64encode(signature)
    sign = str(sign, encoding=charset)
    return sign


def verify_with_rsa(public_key, message, sign):
    public_key = fill_public_key_marker(public_key)
    sign = base64.b64decode(sign)
    return bool(rsa.verify(message, sign, rsa.PublicKey.load_pkcs1_openssl_pem(public_key)))

def add_start_end(key, startMarker, endMarker):
    if key.find(startMarker) < 0:
        key = startMarker + key
    if key.find(endMarker) < 0:
        key = key + endMarker
    return key

使用

from urllib.parse import urlencode, quote_plus, quote
import json
import requests
import random
import time

from SignatureUtils import get_sign_content, sign_with_rsa2

ICBC_PAY_URL = "https://apipcs3.dccnet.com.cn/api/mybank/pay/cpay/cppayapply/V2"  # 工银e企付支付申请服务
ICBC_CLOSE_PAY_URL = "https://api.weixin.qq.com/pay/orderquery"  # 工银e企付订单关闭

ICBC_Config = {
    "AgreeCode": "",  # 合作方协议编号 正式环境由工行提供给贵司
    "PayeeAccno1": "",  # 收款账号 平台的工行账号,可作为收款账户上送
    "PayeeCompanyName1": "",  # 收款账户名称 平台的工行账号,可作为收款账户上送
    "PayeeAccno2": "",  # 收款账号2 商户的工行账号,可作为收款账户上送
    "PayeeCompanyName2": "",  # 收款账户名称2 商户的工行账号,可作为收款账户上送
    "APP_ID": "",
    # 网关公钥 正式环境由工行提供给贵司
    "APIGW_PUBLIC_KEY": "",
    # 签名公钥 正式环境请贵司提供给工行,生成方法请参考《9-联测指引(客户)》公私钥生成方法
    "MY_PUBLIC_KEY": "",

    # 签名私钥 正式环境请贵司保存好,切勿给工行或他人,生成方法请参考《9-联测指引(客户)》公私钥生成方法需要https://www.ssleye.com/ssltool/pkcs.html将这个pkcs8转换格式转为pkcs1
    # "MY_PRIVATE_KEY": "",
    "MY_PRIVATE_KEY": ",

    # 登陆用户名 对账文件服务器登陆用户名
    "username": "eQiFu_2022",
    # 登录公钥
    "publicKey": "",
    # 登录私钥
    "privateKey": """""",
}


def createNoncestr(length=32):
    """产生随机字符串,不长于32位"""
    chars = "abcdefghijklmnopqrstuvwxyz0123456789"
    strs = []
    for x in range(length):
        strs.append(chars[random.randrange(0, len(chars))])
    return "".join(strs)


def pay():
    body = {'app_id': '工行提供',
            'format': 'json',
            'charset': 'UTF-8',
            'sign_type': 'RSA2',
            'biz_content': json.dumps(
                {"agreeCode": "0020000033060410006041000000001464", "asynFlag": "0", "callbackUrl": "www.baidu.com",
                 "goodsList": [{"goodsAmt": "100", "goodsName": "倚天剑", "goodsNumber": "1", "goodsSubId": "1",
                                "goodsUnit": "个", "payeeCompanyName": "饮鹿投茵技拿褪辛乓忘嘴易"}],
                 "internationalFlag": "1", "orderAmount": "100", "orderCode": "2019062730023", "orderCurr": "001",
                 "orderRemark": "订单备注-直接支付-境内", "partnerSeq": "98a2d815bb134a118fdb4513",
                 "payChannel": "1", "payEntitys": "1", "payMemno": "19173980", "payMode": "1", "payeeList": [
                    {"mallCode": "PIPWF2202207158", "mallName": "华为技术有限公司", "payAmount": "100",
                     "payeeAccno": "0200023309200006066", "payeeCompanyName": "饮鹿投茵技拿褪辛乓忘嘴易",
                     "payeeSysflag": "1"}], "rceiptRemark": "回单补充信息备注", "returnUrl": "www.baidu.com",
                 "submitTime": "工行提供有专门的测试时间", "sumPayamt": "100"}),
            'timestamp': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),  # 交易发生时间戳,yyyy-MM-dd HH:mm:ss格式
            'msg_id': createNoncestr(),  # 消息通讯唯一编号,每次调用独立生成,APP级唯一
            }

    # 筛选并排序拼接
    sign_content = '/api/mybank/pay/cpay/cppayapply/V2?' + get_sign_content(body)
    sign = sign_with_rsa2(ICBC_Config["MY_PRIVATE_KEY"], sign_content, "UTF-8")
    body['sign'] = sign
    payload = urlencode(body)
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    response = requests.request("POST", ICBC_PAY_URL, headers=headers, data=payload)

    print("res_data:", response.json())


if __name__ == "__main__":
    pay()

签名规则
工行接口

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值