Python + PHP + RSA 實現加密(解決Python-RSA無法解密一般字串的問題)
阿新 • • 發佈:2019-01-27
摘要
微信校園卡需要一個能夠加密的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實現
加密資訊,並生成二維碼
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編碼的問題
但是,在將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))