用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方法