最近工作需要,用python实现调用接口的示例代码,过程涉及到很多的加密算法,值得分享一下。
首先公钥和私钥如何生成,并且能兼容java平台,尝试了很多方法。最终决定用openssl命令
前提,需要安装openssl,Crypto库
生成公钥私钥对过程:
生成私钥:

openssl genrsa -out rsa_private_key.pem 1024



根据私钥生成公钥:

openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout



这时候的私钥还不能直接被使用,需要进行PKCS#8编码

openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt



命令中指明了输入私钥文件为rsa_private_key.pem,输出私钥文件为pkcs8_rsa_private_key.pem,不采用任何二次加密(-nocrypt)

这时候就获得了一对公钥和私钥,只要拿到对方的公钥,用自己的公钥的格式替换就可以使用啦~~


我们最好把全局变量给提出来,便于管理。这样子就不用改代码都改一遍了


文件Gl.py

#!-*- coding:utf-8 -*-  
'''  
Created on 2013-6-15  
  
@author: shangwei  
'''  
'''  
全局变量  
'''  
from Crypto.PublicKey import RSA  
'''  
publickey为对方的公钥  
privatekey为商户自己的私钥  
'''  
publickey = RSA.importKey(open('rsa_public_key.pem','r').read())  
privatekey=RSA.importKey(open('pkcs8_rsa_private_key.pem','r').read())  
merchantaccount='YB010000000xx'  
URL='xxx.xxx.com'



#!-*- coding:utf-8 -*-  
    '''  
    Created on 2013-5-24  
      
    @author: shangwei  
    '''  
    from Crypto import Random  
    from Crypto.Cipher import PKCS1_v1_5  
    from Crypto.Hash import SHA  
    from hashlib import sha1  
    from rsa import key, common, encrypt  
    from urllib import urlencode   
    import base64  
    import hmac  
    from Crypto.PublicKey import RSA  
    import urllib  
    import urllib2  
    import time    
    import json  
    from Crypto.Signature import PKCS1_v1_5 as pk  
    import Gl  
    class MerchantAPI:  
          
        def doPost(self,url,values):  
            '''  
            post请求  
            参数URL  
            字典类型的参数  
            '''  
            req = urllib2.Request(url)  
            data = urllib.urlencode(values)  
            res = urllib2.urlopen(req, data)  
            ret = res.read()  
            return ret  
      
          
        def doGet(self,url,values):  
            '''  
            get请求  
            参数URL  
            字典类型的参数  
            '''  
            REQUEST = url + "?" + urllib.urlencode(values)  
            ret = urllib2.urlopen(REQUEST).read()    
            return ret  
            
        @staticmethod  
        def _pkcs7padding(data):  
            """  
            对齐块  
            size 16  
            999999999=>9999999997777777  
            """  
            size = AES.block_size  
            count = size - len(data)%size  
            if count:  
                data+=(chr(count)*count)  
            return data  
          
          
      
          
          
        @staticmethod  
        def _depkcs7padding(data):  
            """  
            反对齐  
            """  
            newdata = ''   
            for c in data:  
                if ord(c) > AES.block_size:  
                    newdata+=c  
            return newdata  
          
          
        '''  
        aes加密base64编码  
        '''  
        def aes_base64_encrypt(self,data,key):  
              
            """  
            @summary:   
                1. pkcs7padding  
                2. aes encrypt  
                3. base64 encrypt  
            @return:   
                string  
            """  
            cipher = AES.new(key)  
            return base64.b64encode(cipher.encrypt(self._pkcs7padding(data)))  
      
      
        def base64_aes_decrypt(self,data,key):  
            """  
            1. base64 decode  
            2. aes decode  
            3. dpkcs7padding  
            """  
            cipher = AES.new(key)  
            return self._depkcs7padding(cipher.decrypt(base64.b64decode(data)))          
              
        '''  
        rsa加密  
        '''  
        def rsa_base64_encrypt(self,data,key):  
            '''  
            1. rsa encrypt  
            2. base64 encrypt  
            '''  
            cipher = PKCS1_v1_5.new(key)   
            return base64.b64encode(cipher.encrypt(data))  
          
        '''  
        rsa解密  
        '''  
        def rsa_base64_decrypt(self,data,key):  
            '''  
            1. base64 decrypt  
            2. rsa decrypt  
            示例代码  
              
           key = RSA.importKey(open('privkey.der').read())  
            >>>  
            >>> dsize = SHA.digest_size  
            >>> sentinel = Random.new().read(15+dsize)      # Let's assume that average data length is 15  
            >>>  
            >>> cipher = PKCS1_v1_5.new(key)  
            >>> message = cipher.decrypt(ciphertext, sentinel)  
            >>>  
            >>> digest = SHA.new(message[:-dsize]).digest()  
            >>> if digest==message[-dsize:]:                # Note how we DO NOT look for the sentinel  
            >>>     print "Encryption was correct."  
            >>> else:  
            >>>     print "Encryption was not correct."  
            '''  
            cipher = PKCS1_v1_5.new(key)  
            return cipher.decrypt(base64.b64decode(data), Random.new().read(15+SHA.digest_size))  
              
        '''  
        RSA签名  
        '''  
        def sign(self,signdata):  
            '''  
            @param signdata: 需要签名的字符串  
            '''  
              
            h=SHA.new(signdata)  
            signer = pk.new(Gl.privatekey)  
            signn=signer.sign(h)  
            signn=base64.b64encode(signn)  
            return  signn  
             
        '''  
        RSA验签  
        结果:如果验签通过,则返回The signature is authentic  
             如果验签不通过,则返回"The signature is not authentic."  
        '''  
        def checksign(self,rdata):  
              
            signn=base64.b64decode(rdata.pop('sign'))  
            signdata=self.sort(rdata)  
            verifier = pk.new(Gl.publickey)  
            if verifier.verify(SHA.new(signdata), signn):  
                print "The signature is authentic."  
            else:  
                print "The signature is not authentic."  
               
          
              
              
          
        def sort(self,mes):  
            '''  
            作用类似与java的treemap,  
            取出key值,按照字母排序后将value拼接起来  
            返回字符串  
            '''  
            _par = []  
             
            keys=mes.keys()  
            keys.sort()  
            for v in keys:  
                _par.append(str(mes[v]))  
            sep=''  
            message=sep.join(_par)  
            return message  
          
        '''  
        请求接口前的加密过程  
        '''  
        def requestprocess(self,mesdata):  
            '''  
            加密过程:  
            1、将需要的参数mes取出key排序后取出value拼成字符串signdata  
            2、用signdata对商户私钥进行rsa签名,生成签名signn,并转base64格式  
            3、将签名signn插入到mesdata的最后生成新的data  
            4、用encryptkey16位常量对data进行AES加密后转BASE64,生成机密后的data  
            5、用对方公钥publickey对encryptkey16位常量进行RSA加密BASE64编码,生成加密后的encryptkey  
            '''  
            signdata=self.sort(mesdata)  
            print '需要签名的排序后的字符串为:'+signdata         
            signn=self.sign(signdata)  
      
                
            mesdata['sign']=signn  
            print mesdata  
            encryptkey = '1234567890123456'  
            data=self.aes_base64_encrypt(json.dumps(mesdata),encryptkey)  
          
            print '加密后的data='+data  
            values={}  
            values['merchantaccount']=Gl.merchantaccount  
            values['data']=data  
            values['encryptkey']=self.rsa_base64_encrypt(encryptkey,Gl.publickey)  
            return values  
          
          
        '''  
        对返回结果进行解密后输出  
        '''  
        def result_decrypt(self,result):  
            '''  
            1、返回的结果json传给data和encryptkey两部分,都为加密后的  
            2、用商户私钥对encryptkey进行RSA解密,生成解密后的encryptkey。参考方法:rsa_base64_decrypt  
            3、用解密后的encryptkey对data进行AES解密。参考方法:base64_aes_decrypt  
            '''  
            result=json.loads(result)  
            kdata=result['data']  
            kencryptkey=result['encryptkey']  
            print '返回的加密后的data='+kdata  
            print '返回的加密后的encryptkey='+kencryptkey  
            cryptkey=self.rsa_base64_decrypt(kencryptkey,Gl.privatekey)  
            print '解密后的encryptkey='+cryptkey  
            rdata=self.base64_aes_decrypt(kdata,cryptkey)  
            print '解密后的data='+rdata  
            return rdata  
         
        def testCreditPayAsync(self):  
            '''  
            生成公钥私钥对过程:  
            生成私钥:openssl genrsa -out rsa_private_key.pem 1024   
            根据私钥生成公钥: openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout  
            这时候的私钥还不能直接被使用,需要进行PKCS#8编码:   
            openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt  
            命令中指明了输入私钥文件为rsa_private_key.pem,输出私钥文件为pkcs8_rsa_private_key.pem,不采用任何二次加密(-nocrypt)   
            加密过程:  
            1、将需要的参数mes取出key排序后取出value拼成字符串signdata  
            2、用signdata对商户私钥进行rsa签名,生成签名signn,并转base64格式  
            3、将签名signn插入到mes的最后生成新的data  
            4、用encryptkey16位常量对data进行AES加密后转BASE64,生成机密后的data  
            5、用对方公钥publickey对encryptkey16位常量进行RSA加密BASE64编码,生成加密后的encryptkey  
            6、将merchantaccount,第四部加密后的data,第五步加密后的encryptkey作为参数post请求给URL  http://xxxx/xxx/api/xxx/xxx/xxx/xxx  
            7、返回的结果json传给data和encryptkey两部分,都为加密后的  
            8、用商户私钥对encryptkey进行RSA解密,生成解密后的encryptkey。参考方法:rsa_base64_decrypt  
            9、用解密后的encryptkey对data进行AES解密。参考方法:base64_aes_decrypt  
            '''  
            transtime=int(time.time())  
            od=str(random.randint(10, 100000))  
            mesdata={"merchantaccount":Gl.merchantaccount,"cardno":"xxxx758xxxx23xxxx","validthru":"04xx","cvv2":"200","phone":"1581xxxxxxx",  
    "orderid":"33hhkssseef3u"+od,"transtime":transtime,"currency":156,"amount":2,"productcatalog":"1","productname":"","productdesc":"",  
    "userip":"192.168.5.251","identityid":"ee","identitytype":6,"other":"","callbackurl":"http://IP/webtest/callback.do"}  
            values=self.requestprocess(mesdata)  
            url='http://'+Gl.URL+'/xxxx'  
            print url  
            result=self.doPost(url, values)  
            print result  
            rdata=json.loads(self.result_decrypt(result))  
            self.checksign(rdata)  
    if __name__=='__main__':  
        mer=MerchantAPI()  
        mer.testCreditPayAsync()



知识点:

调试代码的时候也遇到了一些小问题和技巧

import的时候,如果有同名的类可以起个别名。不然会有报错,告诉这个类找不到某个方法from Crypto.Cipher import PKCS1_v1_5,from Crypto.Signature import PKCS1_v1_5 as pk,这个需要注意一下

另外,如果想将字典内的单引号都变为双引号,可以用json.dumps方法