zoukankan      html  css  js  c++  java
  • 公钥私钥的那点事儿

    支付宝介绍

    支付宝开发网址:

    支付宝开放平台: https://open.alipay.com/platform/home.htm
    支付宝沙箱环境: https://openhome.alipay.com/platform/appDaily.htm?tab=info
    支付宝开发者文档:https://openhome.alipay.com/developmentDocument.htm
    电脑网站支付流程:https://docs.open.alipay.com/270
    生成签名:https://docs.open.alipay.com/291/106103/
    python-alipay-adk : https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

     使用python对接支付宝:

    # 访问"支付宝开发平台"登录,可以访问开发者中心
    https://open.alipay.com/platform/home.htm
    # 可以参考"电脑网站支付" 熟悉电脑支付整体流程
    https://docs.open.alipay.com/270/105899/

    创建应用(这里使用沙箱环境测试)

     沙箱环境说明:

     

    按照官方要求生成秘钥:

    生成秘钥参考官网:https://docs.open.alipay.com/291/105971 

          

     把生成的app公钥粘贴到沙箱环境的app中:

           

    •  支付宝公钥一会再程序中要使用

     使用 python-alipay-sdk 支付&查询支付结果

    • 说明

          1. 阿里官方没有提供python对接支付的sdk,但是python库中有非官方的sdk包可以使用

          2. python-alipay-adk : https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

          3. 使用起来非常简单,只要传入几个必要的参数就可以完成

          4. 由于支付对安全要求很高,所以要理解对接支付宝安全的流程:公钥加密、私钥解密  这八个字

    • 上代码
    from alipay import AliPay
    
    # 沙箱环境中 app 私钥
    app_private_key_string = open('app_private_key.pem').read()
    # 支付宝公钥
    alipay_public_key_string = open( 'alipay_public_key.pem').read()
    
    
    def get_alipay_url():
        alipay = AliPay(
            appid="2016101800716047",  # 沙箱appid
            app_notify_url=None,  # 默认回调url
            app_private_key_string=app_private_key_string,
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_string=alipay_public_key_string,
            sign_type="RSA",  # RSA 或者 RSA2
            debug=True,  # 默认False,我们是沙箱,所以改成True(让访问沙箱环境支付宝地址)
        )
        # 调用支付接口
        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no="201611124",  # 订单id,应该从前端获取
            total_amount=str(0.01),  # 订单总金额
            subject="测试阿里云付款",  # 付款标题信息
            return_url=None,  # 付款成功回调地址(可以为空)
            notify_url=None  # 付款成功后异步通知地址(可以为空)
        )
        pay_url = "https://openapi.alipaydev.com/gateway.do?" + order_string
        print(pay_url)  # 将这个url复制到浏览器,就会打开支付宝支付页面
    
    
    def query_pay():
        alipay = AliPay(
            appid="2016101800716047",  # 沙箱appid
            app_notify_url=None,  # 默认回调url
            app_private_key_string=app_private_key_string,
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
            alipay_public_key_string=alipay_public_key_string,
            sign_type="RSA",  # RSA 或者 RSA2
            debug=True,  # 默认False,我们是沙箱,所以改成True(让访问沙箱环境支付宝地址)
        )
        trade_query = alipay.api_alipay_trade_query(
            out_trade_no=20161112,  # 上面生成支付码页面时传入的商品订单号
            trade_no=None
        )
        print(trade_query)
    
    
    if __name__ == '__main__':
        get_alipay_url()
        query_pay()
    
    
    '''
    trade_query 查询用户付款信息
    返回信息详情请看官方:https://docs.open.alipay.com/api_1/alipay.trade.query
    {
        "code": "10000",    # code=10000代表支付成功
        "msg": "Success",
        "buyer_logon_id": "foq***@sandbox.com",
        "buyer_pay_amount": "0.00",
        "buyer_user_id": "2088102180228981",
        "buyer_user_type": "PRIVATE",
        "invoice_amount": "0.00",
        "out_trade_no": "20161112",
        "point_amount": "0.00",
        "receipt_amount": "0.00",
        "send_pay_date": "2020-02-20 21:20:48",
        "total_amount": "0.01",
        "trade_no": "2020022022001428981000087565",
        "trade_status": "TRADE_SUCCESS"  
    }
    '''
    
    alipay_test.py
    alipay_test.py
    -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB
    -----END PUBLIC KEY-----
    alipay_public_key.pem 支付宝公钥
    -----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQCqHFHARYve67Nt/xADRjFsRVTuwOaUiE6GBoQD6JnDOeD07TF1L7cqL4c4zxqEWlcb5p7ZO5Czyw7mpQ7W1jtI9smBxEPEC0ZvlsOuh8zAiVng4gj54eDHarLhfkMk+/pzq44AT/B1CQgtUJRGZrppTO/4SEQZYMO5RcesnPO32QIDAQABAoGAZXlbMuzJkQLdGAKM1McseD+reHjNk1D0NnLwkTxRbOm7zZ8oX5VFF/8eFL9GPCl/yP77Ru6HVRkOHHvdrD5DQjkJ4VipmDQ8xfZlpMmdc2HLtezqR9GG3xso/cAdpCCRjR+vHfr/BTltEiZD3OJapGojyURo/QG5qe7r5lPGBJkCQQD0iZy2+xTfgS58vlD8wtYOXVYkQ4aGtejKx0wgvyws3ahLmv5t9lBB6YfKCVuOHv1ZvwAngWpTsoh4vxeLT8sbAkEAshWaaRyER9SJaeMIBuRf24Bmlyk6piDLTpOOE4lduvjR6j+IgJjY5/N4QTwy9G1vMVwWVzOX/wyinrY48wSkGwJBALcncudiO4oDuYvZB9OpIqZzphdXNCUljoyCpTRJu46HHowyNKqruAUGIpxiUd6qu1tCwZzVQ4wYxjqZ9cp0I00CQEJJ7ldQCSFfyOLEYrmGCvR/ADEmZLFwJZ2TKWC2UlCFzEWg7qPHwX3d0BagXOtFTQ2XlK1TBDIpNuNTOzGiuZ8CQAY/vcaxPm3VyjfevHpFmmwqHuNmRTyKdc3bd8NXTj/MJk7en/gK86PUHdixvudojGTbQOdSSWM/QQoVu8d+DFk=
    -----END RSA PRIVATE KEY-----
    app_private_key.pem app私钥

    支付宝支付流程:

     

    前言

    • 加密技术是个好东西,可以有效地保证我们在沟通和存储过程中的信息安全。但我敢打赌,没有多少程序员喜欢加解密算法,更多人则是一想到公钥私钥、数字证书就开始头晕,一见到 SSL/TLS 协议就额头冒汗。其实呢,作为应用层面的程序员,我们不用担心算法问题,只需要了解加解密的应用场景,就可以轻松应对了。

    对称加密和非对称加密

    通常,对信息加密时需要遵循某种规则,而解密也需要这个规则,所以,这个规则——我们称之为密钥,就需要妥善保管,不能让除参与方之外的其他人知道。加密和解密使用同一个密钥,这就是对称加密。对称加密的最大弊端就是密钥的分发比较困难,因为在对称加密算法中,加密方和解密方都需要知道这个密钥,如果把密钥一起发送,则就存在泄露的风险。对称加密算法的优势是加密速度非常快。常见的对称加密算法有DES, AES等。

    在非对称加密算法中,加密和解密分别使用不同的两个密钥。这两个密钥,我们称之为公钥和私钥,其使用规则如下:

    • 公钥和私钥总是成对使用的
    • 用公钥加密的数据只有对应的私钥可以解密
    • 用私钥加密的数据只有对应的公钥可以解密

    据此,我们可以得到另外两条规则:

    • 如果可以用公钥解密,则必然是对应的私钥加密的(逆反命题)
    • 如果可以用私钥解密,则必然是对应的公钥加密的(逆反命题)

    RSA算法

    RSA算法是使用最广泛的非对称加密算法,Elgamal是另一种常用的非对称加密算法。RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。1987年首次公布,当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

    以下关于算法的描述,程序员可以直接跳过。

    • 随机选取两个大素数p和q,使得p != q
    • 计算 n = p*q
    • 选取一个与Θ互质的小奇数e,其中Θ(n)=(p-1)*(q-1)
    • 对模Θ(n),计算出e的乘法逆元d的值(e 关于模 r 的模反元素)
    • 将P = (e,n)公开,作为参与者的RSA公钥
    • 使对S = (d, n)保密,并作为参与者的RSA密钥

    下面的代码才是程序员喜欢的方式。如果提示找不到 rsa 模块,请先使用 pip install rsa 安装。

    import rsa
    public_key, private_key = rsa.newkeys(1024)

    只用一行代码,我们就得到了一对相互关联的公钥和私钥。先让我们看看它们长什么样。

    >>> import rsa
    >>> public_key, private_key = rsa.newkeys(1024)
    >>> public_key
    PublicKey(92152531277928235614668640293101435876412015819908668333258279694358387533076492868175407616659584948932430613207379632111064020593475841213537368715710670332858863680927621109917543626036905695973634958908960528780365668290457205838209776167934441340612604626115727114825218239811197485327788703960807389731, 65537)
    >>> public_key.save_pkcs1()
    b'-----BEGIN RSA PUBLIC KEY-----
    MIGJAoGBAIM6xHq+WggTLGNbUMBSAPEvFuWcv3pdtJmoLidVHG17jKyS4rCo+7vj
    41eqFkJNU1zEoxDWsUii2+yxNbgbkY16MdE+JnIVBfy7TfjeFeNfHuzB70kO5Ks3
    twcdkZTGD55xCtwVvxW8PndLiR+62T9gcljGHmYP42cHtQILujojAgMBAAE=
    -----END RSA PUBLIC KEY-----
    '

    私钥和公钥长得模样差不多。对私钥和公钥调用save_pkcs1()函数,就得到了私钥和公钥的字节码。

    加密通讯

    程序员小林受命参与一项商业谈判,临行前,业务经理浩克交给他一个U盘,里面保存了一个公钥,对应的私钥则保存在浩克的电脑里。

    import rsa
    
    # 生成公钥、私钥
    public_key, private_key = rsa.newkeys(1024)
    
    # 保存公钥到U盘
    with open('public_hulk.pem', 'w') as fp:
    fp.write(public_key.save_pkcs1().decode()) # decode():字节码转字符串
    
    # 保存私钥到浩克的电脑上
    with open('private_hulk.pem', 'w') as fp:
    fp.write(private_key.save_pkcs1().decode()) # decode():字节码转字符串

    谈判过程中,小林获得一条绝密信息,需要安全发给浩克。信息内容是:绝不能让第三者知道!小林使用存储在U盘上的浩克的公钥加密该信息,然后通过电子邮件发送给了浩克。

    message = '绝不能让第三者知道!'
    
    # 导入公钥
    with open('public_hulk.pem', 'r') as f:
    pubkey = rsa.PublicKey.load_pkcs1(f.read().encode()) # encode():字符串转字节码
    
    # 使用公钥加密信息
    crypto = rsa.encrypt(message.encode('utf-8'), pubkey)

    浩克收到邮件后,邮件内容显示为:

    {P`ᄋxモᄌýôᄍ}VEᄋヘgÛDkᄁjᄎüWÉd.W$ヌᄇNkÙʞÑwフpi_ユᄌèqヨõG

    没关系,浩克只需要用自己的私钥解密就可以正确显示信息了。

    # 导入私钥
    with open('private_hulk.pem', 'r') as f:
    privkey= rsa.PrivateKey.load_pkcs1(f.read().encode()) # encode():字符串转字节码
    
    message = rsa.decrypt(crypto, privkey).decode('utf-8')

    解密后,邮件内容显示为:

    绝不能让第三者知道!

    数字签名

    商务谈判进行到第二天,浩克决定先不着急答应对方提出的条件。他需要发邮件通知小林,邮件内容为:按兵不动,不露声色。浩克写完信息后,先用Hash函数,生成信件的摘要(digest),然后,使用私钥对这个摘要加密,生成"数字签名"(signature),最终把加密的信息和数字签名一同发给了小林。

     

    message = '按兵不动,不露声色。'
    
    # 导入私钥
    with open('private_hulk.pem', 'r') as f:
    privkey= rsa.PrivateKey.load_pkcs1(f.read().encode())
    
    # 生成摘要
    digest = str(hash(message.encode('utf-8')))
    
    # 生成签名
    signature = rsa.encrypt(digest, privkey)
    
    # 加密邮件内容
    crypto = rsa.encrypt(message.encode('utf-8'), privkey)

    正在会议室谈判的小林收到了一封“浩克”的邮件,除了内容,还附有“浩克”的数字签名。他先解密邮件内容,得到“接受条件,立即签约”的指示,然后使用hash函数,生成信件的摘要(digest1),再使用浩克的公钥解密数字签名,得到原始的信件摘要(digest0)。比较两个摘要,完全一致,很显然,这封邮件的确是“浩克”本人发出的。但是,敏感的小林总觉得有些奇怪,因为现在签约,明显有损于公司的利益。他冷静地思考了一分钟,终于发现了问题:有人冒充浩克给他发了这封邮件,并且他U盘里的公钥被人掉包了!

    果然,几分钟之后,小林又收到了浩克的邮件。这次,他使用自己备份的公钥解密,并验证了浩克的数字签名,正确解读了浩克发出的指令:按兵不动,不露声色。

     

    # 导入公钥
    with open('public_hulk.pem', 'r') as f:
    pubkey = rsa.PublicKey.load_pkcs1(f.read().encode())
    
    # 解密邮件内容:按兵不动,不露声色。
    message = rsa.decrypt(crypto, pubkey ).decode('utf-8')
    
    # 生成摘要1
    digest1 = str(hash(message.encode('utf-8')))
    
    # 解密数字签名,得到生成摘要0
    digest0 = rsa.decrypt(signature , pubkey)
    
    if digest0 == digest1:
    print(message) 

    数字证书

    事实上,我们有比U盘更好的方法保存公钥,这就是certificate authority,简称CA。浩克可以去CA证书中心为自己的公钥做认证。CA证书中心用自己的私钥,对浩克的公钥和一些相关信息一起加密,生成"数字证书"(Digital Certificate)。

     

    有了数字证书,在需要数字签名的场合,浩克只要在签名的同时,再附上数字证书就行了。

    收件人收到浩克签名的邮件后,用CA的公钥解开数字证书,就可以拿到浩克真实的公钥了,然后就能证明"数字签名"是否真的是浩克签署的。

  • 相关阅读:
    List数据去重的五种有效方法
    select 1 from ... sql语句中的1代表什么意思?
    gitlab 创建一个空的分支 将本地代码推到特定分支
    Error running 'dt-assets-monitor [clean]': Cannot run program "C:Program Files (x86)Javajdk1.8.0_73injava.exe" (in directory "E:codedt-assets-monitor")
    Git SSH Key 生成步骤
    Git,GitHub与GitLab的区别
    聊聊TCP Keepalive、Netty和Docker
    centos7设置非图形界面
    PHP mysqli 使用预处理语句防注入
    用传纸条讲 HTTPS
  • 原文地址:https://www.cnblogs.com/yangmaosen/p/12402968.html
Copyright © 2011-2022 走看看