摘要
微信校园卡需要一个能够加密的Python中继和PHP后台,由于是对短字符串的加密,并且考虑到效率,所以想要使用一种非对称加密的方法进行加密,RSA就是其中一种
最终实现的效果是:PHP后台对数据进行加密后生成二维码,通过扫码枪输入到Python中继后,通过与后台通信判定当前二维码合法性(当然,这只是个DEMO,生产环境下,私钥应该是放在Python端的)
严重的问题
(1) RSA模块使用base64转码后的密文进行解密操作
问题在于在PHP中用了base64进行解码 T.T
具体参考下文中的:《Python base64编码的问题》(2) Python-RSA 模块使用的public key 开头是—–BEGIN RSA PUBLIC KEY—–,而有些模块,如PHP的,文件开头就不是这个,这会导致public key文件读取失败:
需要注意的问题
- url转码(urlencode)
- RSA加密等级对系统速度的影响
- 最好用POST传递哦,安全又方便
PHP实现
参考: http://blog.csdn.net/wsliangjian/article/details/45867351
加密信息,并生成二维码
public function gen_qr(){
$uid = "201621060701";
$time = $this->get_time();
$text = $uid.$time;
$text = $this->rsa->encrypt_public($text);
if(FALSE == $text){
echoJSON(500,"加密失败");
}
$data = [
'en_text' => $text,
'text' => $uid.$time
];
/* 生成二维码并显示 */
show_QR();
}
RSA封装
<?php
/* 使用openssl实现非对称加密 */
class RSA{
private $private_key = '-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgQC8bNv2gSyf60muXcXkrOsAo3DkONaDSCFccu6WsvKrHHuXTEmZ
UdfvyrTFj1oLEK9yqgBOPTb9aVpejPAyhYDwECUF/KHOWjaPMHhgSqfrfLEag/V+
dfasdffasdfdfefadvBTsNmxnNPa/CCFC11Lbch2GvEE1aWx8VsQx+f91x3sq3sq
vKHJjqQD+ZOLKaoh6zWeQhHn9nNPNUVsQx+f91x3sqVsQx+f91x3sqVsQx+f91xs
BTsNmxnNPa/CCFC11Lbch2GvEE1aWx8VsQx+f91x3sqwefgsdgadg234g234gega
vKHJjqQD+ZOLKaoh6zWeQhHn9nNPNUJBTsNmxnNPa/CCFC11Lbch2GvEE1aWx8fd
vKHJjqQD+ZOLKaoh6zf91x3sqBTsNmxnNPa/CCsdscssFC11Lbch2GvEE1aWx8Vs
vKHJjqQD+ZOLKaoh6zWeQhHn9nNPNUgT47jtJGL+EFuHmQI/LBTsNmxnNPa/CCFC
vKHJjqQD+ZOLKaoh6zWeQhHn9nNPNUR8+ZuqJzgo
-----END RSA PRIVATE KEY-----';
private $public_key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8bNv2gSyf60muXcXkrOsAo3Dk
ONaDSCFccu6WsvKrHHuXTEmZUdfvyrTFj1oLEK9yqgBOPTb9aVpejPAyhYDwECUF
/KHOWjaPMHhgSqfrfLEag/V+rT86qdpj0HVgMDPw4ctfpHBe6rTMJnC+mUbfaWLO
S8fsEDAfIwWEf5/jGQIDAQAB
-----END PUBLIC KEY-----
';
/* 使用公钥加密 */
public function encrypt_public($data){
/* 判断公钥是否是可用的 */
$pu_key = openssl_pkey_get_public($this->public_key);
if($pu_key==FALSE) {
return FALSE;
}else{
$encrypted = "";
openssl_public_encrypt($data,$encrypted,$pu_key);//公钥加密
$encrypted = base64_encode($encrypted);
return $encrypted;
}
}
/* 使用密钥解密 */
public function decrypt_private($encrypted_data){
/* 判断私钥是否是可用的 */
$pi_key = openssl_pkey_get_private($this->private_key);
if($pi_key==FALSE) {
return FALSE;
}else{
$decrypted = "";
openssl_private_decrypt(base64_decode($encrypted_data),$decrypted,$pi_key);//私钥解密
return $decrypted;
}
}
}
?>
Python base64编码的问题
Python环境下RSA编解码请参考:
https://stuvel.eu/files/python-rsa-doc/usage.html
http://www.cnblogs.com/renfanzi/p/6062261.html但是,在将PHP中所生成的加密字符串传入Python中时,出现了字符串冲突的问题:
在网上搜索了以下资料,发现并没用什么可用的讯息。 然后,通过debug RSA模块的加密函数的返回值后发现,输出的大概是这么个东西:
b"%k\xd9\xb6\xc5\xde\x19\x86\xf00#\x80s\x17\x9b\xb5\x04]\x96\x93\xf9\xd6\x96\xb5j\\\xbb\xbf\xbd\x8f\x07^+\xdfL8=\xee\xf .......
那么答案就很明显了:这TM是个base64 encode过的bytes字符串
因此,我们需要对从PHP处生成的字符串做一些些转码,整个实现的代码如下:
import rsa
import base64
# Warning : 这里必须用'rb'模式打开,或者在f.read()后使用encode()函数进行转码
with open('./rsa_key/rsa_private_key.pem','rb') as f:
privkey = rsa.PrivateKey.load_pkcs1(f.read())
# ALTER :
# privkey = rsa.PrivateKey.load_pkcs1(f.read().encode())
while(1):
try:
encrypted_str = input(">>")
# 将str转码成bytes
encrypted_str = encrypted_str.encode()
# 将bytes进行base64编码
encrypted_str = base64.b64decode(encrypted_str)
# 进行RSA解码
decrypted_str = rsa.decrypt(encrypted_str, privkey)
except Exception as e:
print("FAIL, Try Again PLS")
continue
# 将bytes转为str
decrypted_str = decrypted_str.decode()
print(decrypted_str)
Python 通过POST与web后台交互
import web # 参考web.py
import time
while(1):
text = input('>>')
url = "http://www.wunyungsumu.cn/WechatCard/u"
values = {
'text' : text
}
result = web.post(url=url, values=values)
result_str = str(result, encoding="utf-8")
if(result_str=='0'):
print("Failed, Try Again Please !")
continue
uid = result_str[:12]
tim = result_str[12:]
print("uid:",uid," time:",tim)
Python 网络web封装
web.py
import urllib.parse as up
import urllib.request as ur
def get(url):
req = ur.Request(url)
response = ur.urlopen(req)
return response.read()
def post(url,values):
data = up.urlencode(values).encode(encoding='UTF8')
req = ur.Request(url, data)
response = ur.urlopen(req)
return response.read()
# url = 'http://umbra.nascom.nasa.gov/cgi-bin/eit-catalog.cgi'
# values = {
# 'obs_year': '2011',
# 'obs_month': 'March'
# }
#
# print(post(url=url, values=values))