前段时间写了一个python程序,涉及http请求和数据的加密解密,终于完成了,虽然经历很长的时间,填了很多坑,但是值得记录一下,分享出来。由于是在简书的第一篇文章,理应是满满的干货。
这个需求的背景是这样的,需要把公司的用户加以区分,分为内部用户和外部用户,通过什么来区分的,当然是通过手机号比较简单容易了,所以就定下来用手机号区分。但是获取手机号对于公司的高层来说,可能觉得有点安全隐患,为了保险就对数据进行加密解密,方法就是请求方发送请求的时候,把公钥发过去,同时也有其他的一些参数,对方根据公钥加密,返回数据,请求方再根据私钥解密。【后来和其他同事说起这个事情的时候,他说这样好像并没有什么卵用,因为是自己发送公钥,收到数据自己再解密,如果其他人知道了这个方法和传输的参数,他也可以获取到数据,我感觉真的是这么回事啊。】
现在来说一下这个曲折的过程。
1、调用java接口
2、确定使用加密的算法
3、分段解密
具体过程是这样的:
1、对于http请求来说,相对简单一些,在网上可以找到现成的例子,需要注意的就是把header里面的内容写对,Content-type,Accept要注意,我这边接收的是json格式的数据,所以accept是application/json,下面是我在网上搜的一个例子。
#!/usr/bin/env python
#coding=utf8
import httplib, urllib
httpClient = None
try:
params = urllib.urlencode({'name': 'tom', 'age': 22})
headers = {"Content-type": "application/x-www-form-urlencoded"
, "Accept": "text/plain"}
httpClient = httplib.HTTPConnection("localhost", 80, timeout=30)
httpClient.request("POST", "/test.php", params, headers)
response = httpClient.getresponse()
print response.status
print response.reason
print response.read()
print response.getheaders() #获取头信息
except Exception, e:
print e
finally:
if httpClient:
httpClient.close()
如果接口可以调通,那么status应该是200,reason是OK,如果出现其他的状态码,就要具体分析了。
2、一般很快就可以调通了,之后就是处理返回的数据。对于加密解密有的说了,我在网上搜了一下,对于python有很多的模块可以操作,但是最后选择了M2Crypto进行操作。【由于服务器python版本的限制吧,本地可以使用的,在服务器上却不可以使用】之前尝试使用RSA来操作,但是由于我给接口传的PublicKey的类型、长度有问题,导致一直无法返回数据,接口接受的PublicKey是一串字符串,而我曾经把RSA生成的Publickey整体传给了对方,后来一起调的时候,各自测试发现了参数类型不一致,以及PublicKey的长度不一致,这个时候我觉得可能是参数key的算法不一致导致的长度不一致,中间也换了其他的模块,不过失败了,最后选择用openssl产生PublicKey,PrivateKey,这个时候的PublicKey和对方java产生的长度一样了,终于可以看到返回的数据了。
下面是在python中使用openssl产生公钥和私钥
os.system('rm ./privatekey.pem ./publickey.pem')
os.system('openssl genrsa -out ./privatekey.pem')
os.system('openssl rsa -pubout -in ./privatekey.pem -out ./publickey.pem')
不过,我还是高兴的太早了,数据是返回了,但是还没有解密啊。解密也是很头疼的,本以为用函数就可以解密了,结果还是调了半天,中间还有一个环节,就是用base64进行编码,这又涉及了解码的过程。所以,我拿到数据之后先进行解码,解码之后再解密。
当然获取的数据需要转换一下格式,json字符串转换为python可以处理的字典
data = = json.loads(response.read())
base64这个库不需要安装,引入就可以了,我最开始还傻乎乎的安装
data = base64.b64decode(data)
你以为现在就可以解密了么,too young too simple,你知道对方是根据什么模式进行填充加密块么,也就是padding参数是什么,这个参数有四个值:no_padding,pkcs1_padding,sslv23_padding,pkcs1_oaep_padding,当我问对方的时候,对方一脸懵B的看着我,我只能挨个试一下了,反正也就四个么,试下来是pkcs1_padding,可能默认的就是这个吧,具体没有深究。
3、确定好这个参数之后,就是激动人心的时刻了,运行之后终于可以看到人类可以看懂的数据了,可是依然有一个问题,当我修改了一个参数之后,竟然又报错了,错误没有记下来,反正就是数据比较大的意思,这其实是超过解密算法的能力了,对于1024位的秘钥,加密字节最长为117,解密字节最长为128,所以需要分段解密才可以。接下来就是分段解密了,这个网上也可以搜到。
def decodeSplit(message,clientPriKey):
"""
消息在传送过来之前是经过base64加密的,所以要先进行base64解密后才进行RSA分段解密
64为key.size/8,密钥长度为512
"""
message = base64.b64decode(base64.b64decode(message))
count = len(message)
if count>64:
jiemi=""
index=0
while index<count:
i = 64 if count-index>64 else count-index
submsg = message[index:index+i]
index+=i
j = rsa.decrypt(submsg,clientPriKey)
jiemi+=j
return jiemi
else:
jiemi = rsa.decrypt(message,clientPriKey)
return jiemi
这个弄好了就可以看到完整的数据了,终于大功告成。
以下为参考资料,可能不完整,如果遗漏请谅解,如有侵权,请联系。
参考资料:
1、http://hgoldfish.com/blogs/article/57/
2、http://betazk.github.io/2014/10/python%E8%BF%9E%E6%8E%A5.net%E7%AB%AF%E9%81%87%E5%88%B0%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98%E5%9B%9E%E9%A1%BE/
3、http://blog.csdn.net/nyist327/article/details/48352253