python3 實現RSA算法分段加密解密


參考博客地址:

https://blog.csdn.net/qq_33414271/article/details/78424951

https://www.cnblogs.com/piperck/p/7257043.html

最近由於工作的需要,需要寫mock,寫mock就需要接觸到加密解密簽名驗簽的問題,下面是一些總結(只針對加密解密):

加密解密概念

關於加解密和加簽驗簽的概念參考上面的第二個博主內容,解釋的很到位,這里直接摘取過來了:

  加密和加簽完全不是同樣一件事情。

  加密使用的是公鑰對數據進行加密,而且當你使用一把1024bit的rsa公鑰的時候,你一次只能加密最多117byte的數據,

如果數據量超過這個數,可能會涉及到對數據進行分段加密的問題。而且現在rsa 1024bit長度的鑰匙已經被證明了不夠安全,

應該盡量使用2048bit長度的鑰匙。2048bit長度的鑰匙一次可以加密245byte長度的數據。這個計算方法是 2048bit/8 =

256byte - 11byte = 245byte長數據。就是鑰匙長度減去11byte得到的自己最大能一次加密多長的數據。如果超過了就會報錯,

所以很多平台要求對數據用公鑰進行加密,就可能涉及到分段加密的問題。同時要注意的是,解密的時候不存在這11byte的

減少。就是說一把1024bit的鑰匙可以解密128byte長的數據而2048bit的可以解密256byte的數據。

  而加簽是使用自己的私鑰對需要加簽的字符串進行簽名。而對方需要拿着你給的公鑰來驗證這個數據是不是由你發出的,

需要使用公鑰對數據進行驗簽。如果成功驗簽才能說明你是你。

代碼實現

這里公鑰私鑰是找開發要的,不是自己生成的,因為是做mock,需要和開發的公鑰私鑰一致。 

實現加密代碼:

from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.PublicKey import RSA
import base64


'''
單次加密串的長度最大為(key_size/8 - 11)
加密的 plaintext 最大長度是 證書key位數/8 - 11, 例如1024 bit的證書,被加密的串最長 1024/8 - 11=117,
解決辦法是 分塊 加密,然后分塊解密就行了,
因為 證書key固定的情況下,加密出來的串長度是固定的。
'''


def rsa_long_encrypt(pub_key_str, msg):
    msg = msg.encode('utf-8')
    length = len(msg)
    default_length = 117
    #公鑰加密
    pubobj = Cipher_pkcs1_v1_5.new(RSA.importKey(pub_key_str))
    #長度不用分段
    if length < default_length:
        return base64.b64encode(pubobj.encrypt(msg))
    #需要分段
    offset = 0
    res = []
    while length - offset > 0:
        if length - offset > default_length:
            res.append(pubobj.encrypt(msg[offset:offset+default_length]))
        else:
            res.append(pubobj.encrypt(msg[offset:]))
        offset += default_length
    byte_data = b''.join(res)

    return base64.b64encode(byte_data)

這里有個問題,我怎么知道證書key的位數呢?開始的時候嘗試過用print(len(key))來查看key的位數,但是輸出的結果不對,

最后是通過監測Crypto模塊中計算key位數的代碼來查看的:

上面的modBits值就是key的位數。

根據需要,可以使用base64算法將二進制流轉換成字符串。這里加密的時候還出現了一個問題,就是分段加密后最后拼接成一

個加密串的時候報錯了:

錯誤大概的意思是不可以用byte類型進行拼接,其實''.join(res)是將列表中的各個字符串元素拼接成一個字符串,但是這里加密后

返回的結果類型是byte類型的,不是str,所以才報錯了。解決方法也簡單,就是做bytes拼接,在''.join(res)前加b就可以了,修改

后的為b''.join(res)。

實現解密代碼:

def rsa_long_decrypt(priv_key_str, msg):
    msg = base64.b64decode(msg)
    length = len(msg)
    default_length = 128
    #私鑰解密
    priobj = Cipher_pkcs1_v1_5.new(RSA.importKey(priv_key_str))
    #長度不用分段
    if length < default_length:
        return b''.join(priobj.decrypt(msg, b'xyz'))
    #需要分段
    offset = 0
    res = []
    while length - offset > 0:
        if length - offset > default_length:
            res.append(priobj.decrypt(msg[offset:offset+default_length], b'xyz'))
        else:
            res.append(priobj.decrypt(msg[offset:], b'xyz'))
        offset += default_length

    return b''.join(res)

這里需要注意的是decrypt方法,這個方法有兩個參數,第一個參數是解密的內容,第二個參數是解密錯誤后返回的結果,這個結

果也需要轉換成byte類型。

對加密解密代碼進行測試:

msg = '{"ApplyId":"20180504002","ChannelId":"O00420000001","LoanId":"9996401-5077812265125477811","LoanAmount":"5000.00""LoanPeriod":6,"LoanCardNo":"6225768313133272","BankId":"BANKLIST/ZHAOHANG"}'


with open('rsa-public.pem') as f:
    '''讀取公鑰並加密'''
    key = f.read()
    result = rsa_long_encrypt(key, msg)
    print(result.decode('utf-8'))

with open('rsa-private.pem') as f:
    '''讀取私鑰並解密'''
    key1 = f.read()
    result1 = rsa_long_decrypt(key1, data)
    print(result1.decode('utf-8'))

這里可能會出現讀取密鑰格式錯誤的問題,這時需要在密鑰的前后加上以下內容,如果是自動生成的.pem文件不會有這個問題的。

公鑰:

私鑰:

 以上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM