1. 程式人生 > >Python + PHP + RSA 實現加密(解決Python-RSA無法解密一般字串的問題)

Python + PHP + RSA 實現加密(解決Python-RSA無法解密一般字串的問題)

摘要

微信校園卡需要一個能夠加密的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檔案讀取失敗:

這裡寫圖片描述

需要注意的問題

  1. url轉碼(urlencode)
  2. RSA加密等級對系統速度的影響
  3. 最好用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))

實現效果

這裡寫圖片描述

這裡寫圖片描述