用python实现调用接口的示例代码,过程涉及到很多的加密算法,值得分享一下。
首先公钥和私钥如何生成,并且能兼容java平台,尝试了很多方法。最终决定用openssl命令
前提,需要安装openssl,Crypto库
生成公钥私钥对过程:
生成私钥:
根据私钥生成公钥:
这时候的私钥还不能直接被使用,需要进行PKCS#8编码
命令中指明了输入私钥文件为rsa_private_key.pem,输出私钥文件为pkcs8_rsa_private_key.pem,不采用任何二次加密(-nocrypt)
这时候就获得了一对公钥和私钥,只要拿到对方的公钥,用自己的公钥的格式替换就可以使用啦~~
我们最好把全局变量给提出来,便于管理。这样子就不用改代码都改一遍了
文件Gl.py
知识点:
调试代码的时候也遇到了一些小问题和技巧
import的时候,如果有同名的类可以起个别名。不然会有报错,告诉这个类找不到某个方法from Crypto.Cipher import PKCS1_v1_5,from Crypto.Signature import PKCS1_v1_5 as pk,这个需要注意一下
另外,如果想将字典内的单引号都变为双引号,可以用json.dumps方法
首先公钥和私钥如何生成,并且能兼容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
- 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__
知识点:
调试代码的时候也遇到了一些小问题和技巧
import的时候,如果有同名的类可以起个别名。不然会有报错,告诉这个类找不到某个方法from Crypto.Cipher import PKCS1_v1_5,from Crypto.Signature import PKCS1_v1_5 as pk,这个需要注意一下
另外,如果想将字典内的单引号都变为双引号,可以用json.dumps方法