欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 實(shí)現(xiàn)RSA加解密文本文件

 更新時(shí)間:2020年12月30日 16:44:39   作者:dwBurning  
這篇文章主要介紹了Python 實(shí)現(xiàn)RSA加解密文本文件的方法,幫助大家更好的理解和使用python,感興趣的朋友可以了解下

近來(lái)在使用python寫項(xiàng)目,特此記錄一下項(xiàng)目中遇到的文件加解密問(wèn)題。
關(guān)于python版本的加密算法,隨便搜一搜還是可以檢索出來(lái)很多的,不過(guò)大都是同一篇文章在不同的平臺(tái)來(lái)回發(fā)布,或者就是轉(zhuǎn)載,而且例舉的都是最簡(jiǎn)單的情況,那么,實(shí)際項(xiàng)目中使用的話,肯定會(huì)比這個(gè)要稍微復(fù)雜一些,比如我的需求就是要加密一個(gè)使用mysqldump出來(lái)的數(shù)據(jù)庫(kù)腳本文件,直接拿網(wǎng)上的例子過(guò)來(lái)調(diào)用肯定是不行的,所以不得不自己研究了一番,特此記錄。

RSA算法

什么是RSA算法?

項(xiàng)目選型的算法是RSA非對(duì)稱加密算法,關(guān)于這個(gè)算法不做過(guò)多的解釋,咱們劃重點(diǎn):

  • 公鑰用于加密
  • 私鑰用于解密
  • len_in_byte(raw_data) = len_in_bit(key)/8 -11,如 1024bit 的密鑰,一次能加密的內(nèi)容長(zhǎng)度為 1024/8 -11 = 117 byte

為何要減去11個(gè)byte?

因?yàn)槲覀兪褂玫氖荘KCS1Padding占用了11個(gè)byte,那么它能加密的明文長(zhǎng)度就必須減去這11個(gè)byte

可能會(huì)遇到什么問(wèn)題?

基于以上三點(diǎn),我們大概可以知道要完成文件加解密,我們可能會(huì)遇到什么問(wèn)題?

一次性加密明文的長(zhǎng)度是和密鑰長(zhǎng)度有關(guān)系的,那么我們要加密一個(gè)文件,不能一次性將文本內(nèi)容讀取出來(lái),然后加密
如果文件很大,我們也不可能將文件內(nèi)容一次性讀取到內(nèi)存當(dāng)中,可能會(huì)直接導(dǎo)致服務(wù)器無(wú)法響應(yīng)其他請(qǐng)求,這肯定是不合理的
文本被加密之后,回頭解密,如果讀取的長(zhǎng)度有差異勢(shì)必導(dǎo)致解密失敗,那么這個(gè)數(shù)據(jù)庫(kù)備份文件就廢了,這個(gè)就比較危險(xiǎn)了

Do It

安裝依賴,python版本3.7.4

pip install pycryptodomex -i https://pypi.tuna.tsinghua.edu.cn/simple/

導(dǎo)入模塊:

import base64
from Cryptodome import Random
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Cryptodome.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5

生成公鑰+私鑰,注意這里我們生成的公鑰長(zhǎng)度是1024bit

# 偽隨機(jī)數(shù)生成器
random_generator = Random.new().read
# rsa算法生成實(shí)例
rsa = RSA.generate(1024, random_generator)
private_pem = str(rsa.exportKey(), encoding="utf-8")
with open("client-private.pem", "w") as f:
    f.write(private_pem)
  
public_pem = str(rsa.publickey().exportKey(), encoding="utf-8")
with open("client-public.pem", "w") as f:
    f.write(public_pem)'''

加密,這里對(duì)傳入的明文長(zhǎng)度做了切分,因?yàn)槲覀兩傻拿荑€長(zhǎng)度為1024bit,所以我們一次加密的明文長(zhǎng)度不能超過(guò)117個(gè)byte

def rsa_encrypt(plaintext, pub_key):
    '''
    rsa 加密
    :param plaintext: 明文
    :param pub_key:公鑰
    '''
    message = plaintext.encode("utf-8")
    length = len(message)
    default_length = 117  # 1024/8 - 11 1024為密鑰長(zhǎng)度
    rsakey = RSA.importKey(pub_key)
    cipher = Cipher_pkcs1_v1_5.new(rsakey)
    # 不需要切分
    if length <= default_length:
        return default_rsa_encrypt(cipher, message)
    # 需要切分
    offset = 0
    result = []
    while length - offset > 0:
        if length - offset > default_length:
            result.append(default_rsa_encrypt(
                cipher, message[offset:offset+default_length]))
        else:
            result.append(default_rsa_encrypt(cipher, message[offset:]))
        offset += default_length
    return "\n".join(result)
  
def default_rsa_encrypt(cipher, message):
    ciphertext = base64.b64encode(cipher.encrypt(message))
    # print(b"ciphertext:"+ciphertext)
    ciphertext_decode = ciphertext.decode("utf-8")
    # print("ciphertext_decode:"+ciphertext_decode)
    return ciphertext_decode

解密

def rsa_decrypt(ciphertext, priv_key):
    '''
    rsa 解密
    :param ciphertext:密文
    :param priv_key:私鑰
    '''
    message = base64.b64decode(ciphertext)
    length = len(message)
    default_length = 128
    rsakey = RSA.importKey(priv_key)
    cipher = Cipher_pkcs1_v1_5.new(rsakey)
    if length <= default_length:
        return default_rsa_decrypt(cipher, message)
    # 需要分段
    offset = 0
    result = []
    while length - offset > 0:
        if length - offset > default_length:
            result.append(rsa_decrypt(
                cipher, message[offset:offset+default_length]))
        else:
            result.append(rsa_decrypt(cipher, message[offset:]))
        offset += default_length
    decode_message = [x.decode("utf-8") for x in result]
    return "".join(decode_message)
  
def default_rsa_decrypt(cipher, message):
    plaintext = cipher.decrypt(message, random_generator)
    # print(b"plaintext:"+plaintext)
    plaintext_decode = plaintext.decode("utf-8")
    # print("plaintext_decode:"+plaintext_decode)
    return plaintext_decode

加解密文件,考慮開(kāi)頭我們提出的問(wèn)題,采用了逐行讀取,逐行加密,加密后密文也逐行寫入

def rsa_encrypt_file(file_path, save_path, pub_key):
    '''
    rsa 加密文件
    :param file_path:需要加密文件路徑
    :param save_path:加密之后存放的文件路徑
    :param pub_key:公鑰
    '''
    with open(file_path, "r", encoding="utf-8") as f:
        line = f.readline()  # 讀取一行
        while line:
            context = rsa_encrypt(line, pub_key)  # 加密切割后的字符
            with open(save_path, "a", encoding="utf-8") as w:
                w.write(context+"\n")
        line = f.readline()
def rsa_decrypt_file(file_path,save_path,priv_key):
    '''
    rsa 解密文件
    :file_path:需要解密的文件路徑
    :save_path:解密之后存放的文件路徑
    :priv_key:私鑰
    '''
    with open(file_path,"r",encoding="utf-8") as f:
        line = f.readline()
        while line:
            context = rsa_decrypt(line.strip("\n"),priv_key)
            with open(save_path,"a",encoding="utf-8") as w:
                w.write(context)
            line = f.readline()

測(cè)試,一開(kāi)始我使用的是自己隨便輸入的一行很長(zhǎng)的數(shù)字文本,親測(cè)沒(méi)有問(wèn)題,但是當(dāng)我直接使用我的數(shù)據(jù)庫(kù)腳本文件的時(shí)候,加密可以成功,但是會(huì)遇到解密后解碼失敗的情況,當(dāng)時(shí)百思不得其解,我以為是字符集的問(wèn)題,于是我將utf-8,換成了gb2312,加解密成功了,當(dāng)時(shí)心花怒放,直到我重新加解密了另一個(gè)備份文件,又遇到解碼失敗,當(dāng)時(shí)就睡不著覺(jué)了~

直到我看到了這句話不完整的多字節(jié)序列(incomplete multibyte sequence)我瞬間明白了,因?yàn)槲业哪_本文件中含有中文,utf8 編碼一個(gè)漢字是3個(gè)byte,gb2312編碼一個(gè)漢字是2個(gè)byte,只要是多字節(jié),那么做切割的時(shí)候,就有可能一個(gè)漢字被切割成了兩部分,那么自然會(huì)導(dǎo)致無(wú)法解碼成正確的漢字了,問(wèn)題已經(jīng)明了,就看怎么解決了。

因?yàn)槭悄_本文件,處理不好就有可能導(dǎo)致腳本執(zhí)行失敗,最終導(dǎo)致數(shù)據(jù)庫(kù)還原失敗,這就違背項(xiàng)目初衷了~

所以我想了一個(gè)辦法,先對(duì)每一行文本做字符編碼判斷,超過(guò)了117,最后一個(gè)字符就不累計(jì)上去,代碼如下:

def cut_string(message,length = 117):
    result = []
    temp_char = []
    for msg in message:#遍歷每一個(gè)字符
        msg_encode = msg.encode("utf-8")#對(duì)每一個(gè)字符編碼
        temp_encode = "".join(temp_char).encode("utf-8")#累計(jì)編碼之后的字節(jié)數(shù)
        if len(temp_encode) + len(msg_encode) <= length:#如果小于約定的長(zhǎng)度,加添加入結(jié)果集
            temp_char.append(msg)
        else:#如果已經(jīng)超過(guò)了約定的長(zhǎng)度,就添加入下一個(gè)結(jié)果集
            result.append("".join(temp_char))
            temp_char.clear()
            temp_char.append(msg)
    result.append("".join(temp_char))
    return result

加密方法需要重新調(diào)整一下:

def rsa_encrypt_file(file_path,save_path,pub_key):
    '''
    rsa 加密文件
    :param file_path:需要加密文件路徑
    :param save_path:加密之后存放的文件路徑
    :param pub_key:公鑰
    '''
    with open(file_path,"r",encoding="utf-8") as f:
        line = f.readline() #讀取一行
        while line:
            cut_lines = cut_string(line) # 切割字符 保證漢字不被切割
            for cut_line in cut_lines:
                context = rsa_encrypt(cut_line,pub_key) #加密切割后的字符
                with open(save_path,"a",encoding="utf-8") as w:
                    w.write(context+"\n")
            line = f.readline()

到此問(wèn)題就已經(jīng)解決了,其實(shí)有了這個(gè)cut_string方法之后,之前寫的加解密方法中不需要再做切分,但是代碼保留。

上面的方法,加解密的效率非常的低,因?yàn)槭侵鹦屑咏饷?,一個(gè)300M的腳本文件,加密完成耗時(shí)40分鐘,這個(gè)實(shí)在是太難受了,所以調(diào)整了策略,先壓縮再加密,所以就涉及到二進(jìn)制文件的讀取與寫入,最后的實(shí)現(xiàn)代碼如下:

def rsa_encrypt_binfile(file_path,save_path,pub_key):
  '''
  rsa 加密二進(jìn)制文件
  :param file_path:需要加密文件路徑
  :param save_path:加密之后存放的文件路徑
  :param pub_key:公鑰
  '''
  with open(file_path, 'rb') as f:
    message = f.read()
  length = len(message)
  default_length = 117 # 1024/8 - 11 1024為密鑰長(zhǎng)度
  rsakey = RSA.importKey(pub_key)
  cipher = Cipher_pkcs1_v1_5.new(rsakey)
  # 不需要切分
  result = []
  if length <= default_length:
    result.append(base64.b64encode(cipher.encrypt(message)))

  # 需要切分
  offset = 0
  while length - offset > 0:
    if length - offset > default_length:
      result.append(base64.b64encode(cipher.encrypt(message[offset:offset+default_length])))
    else:
      result.append(base64.b64encode(cipher.encrypt(message[offset:])))
    offset += default_length
  
  with open(save_path,"ab+") as w:
    for ciphertext in result:
      ciphertext += b"\n"
      w.write(ciphertext)
def rsa_decrypt_binfile(file_path,save_path,priv_key):
  '''
  rsa 解密二進(jìn)制文件
  :file_path:需要解密的文件路徑
  :save_path:解密之后存放的文件路徑
  :priv_key:私鑰
  '''
  with open(file_path,"rb") as f:
    line = f.readline()
    while line:
      message = base64.b64decode(line.strip(b"\n"))
      rsakey = RSA.importKey(priv_key)
      cipher = Cipher_pkcs1_v1_5.new(rsakey)
      plaintext = cipher.decrypt(message, random_generator)
      with open(save_path, 'ab+') as w: #追加寫入
        w.write(plaintext)
      line = f.readline()

以上就是Python 實(shí)現(xiàn)RSA加解密文本文件的詳細(xì)內(nèi)容,更多關(guān)于python rsa加解密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python文件路徑操作方法總結(jié)

    python文件路徑操作方法總結(jié)

    在本篇文章里小編給大家整理的是一篇關(guān)于python文件路徑操作方法總結(jié)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。
    2020-12-12
  • python之dlib包安裝失敗問(wèn)題及解決

    python之dlib包安裝失敗問(wèn)題及解決

    這篇文章主要介紹了python之dlib包安裝失敗問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Python+Tkinter實(shí)現(xiàn)經(jīng)典井字棋小游戲

    Python+Tkinter實(shí)現(xiàn)經(jīng)典井字棋小游戲

    Tkinter是內(nèi)置到Python安裝包中的,只要安裝好Python之后就能import?Tkinter,而且IDLE也是用Tkinter編寫而成的。本文將用Tkinter編寫經(jīng)典的井字棋小游戲,需要的可以參考一下
    2022-03-03
  • Python析構(gòu)函數(shù)__del__定義原理解析

    Python析構(gòu)函數(shù)__del__定義原理解析

    這篇文章主要介紹了Python析構(gòu)函數(shù)__del__定義原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Python基礎(chǔ) while循環(huán)與break、continue關(guān)鍵字

    Python基礎(chǔ) while循環(huán)與break、continue關(guān)鍵字

    今天再帶著大家講述一下while循環(huán)。那么for循環(huán)和while循環(huán),到底有什么區(qū)別呢?下面文章就來(lái)詳細(xì)介紹,感興趣的小伙伴可以參考一下
    2021-10-10
  • 詳解python使用pip安裝第三方庫(kù)(工具包)速度慢、超時(shí)、失敗的解決方案

    詳解python使用pip安裝第三方庫(kù)(工具包)速度慢、超時(shí)、失敗的解決方案

    這篇文章主要介紹了詳解python使用pip安裝第三方庫(kù)(工具包)速度慢、超時(shí)、失敗的解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • Django執(zhí)行python?manage.py?makemigrations報(bào)錯(cuò)的解決方案分享

    Django執(zhí)行python?manage.py?makemigrations報(bào)錯(cuò)的解決方案分享

    相信用過(guò)很多Django makemigrations的人都會(huì)遇到過(guò)makemigrations時(shí)會(huì)發(fā)生報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于Django執(zhí)行python?manage.py?makemigrations報(bào)錯(cuò)的解決方案,需要的朋友可以參考下
    2022-09-09
  • Python數(shù)字/字符串補(bǔ)零操作實(shí)例代碼

    Python數(shù)字/字符串補(bǔ)零操作實(shí)例代碼

    我們?cè)陂_(kāi)發(fā)中為了排版方便或者是輸出文件命名整潔,通常需要給數(shù)字前面補(bǔ)0來(lái)做統(tǒng)一,這篇文章主要給大家介紹了關(guān)于Python數(shù)字/字符串補(bǔ)零操作的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • SELENIUM自動(dòng)化模擬鍵盤快捷鍵操作實(shí)現(xiàn)解析

    SELENIUM自動(dòng)化模擬鍵盤快捷鍵操作實(shí)現(xiàn)解析

    這篇文章主要介紹了SELENIUM自動(dòng)化模擬鍵盤快捷鍵操作實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Python中的super用法詳解

    Python中的super用法詳解

    這篇文章主要介紹了Python中的super用法詳解,本文講解了關(guān)于super問(wèn)題的發(fā)現(xiàn)與提出、走進(jìn)Python的源碼世界分析super的實(shí)現(xiàn)、延續(xù)的討論super等內(nèi)容,需要的朋友可以參考下
    2015-05-05

最新評(píng)論