gpt4 book ai didi

Python 实现RSA加解密文本文件

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章Python 实现RSA加解密文本文件由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

近来在使用python写项目,特此记录一下项目中遇到的文件加解密问题。 关于python版本的加密算法,随便搜一搜还是可以检索出来很多的,不过大都是同一篇文章在不同的平台来回发布,或者就是转载,而且例举的都是最简单的情况,那么,实际项目中使用的话,肯定会比这个要稍微复杂一些,比如我的需求就是要加密一个使用mysqldump出来的数据库脚本文件,直接拿网上的例子过来调用肯定是不行的,所以不得不自己研究了一番,特此记录.

RSA算法

什么是RSA算法?

项目选型的算法是RSA非对称加密算法,关于这个算法不做过多的解释,咱们划重点:

  • 公钥用于加密
  • 私钥用于解密
  • len_in_byte(raw_data) = len_in_bit(key)/8 -11,如 1024bit 的密钥,一次能加密的内容长度为 1024/8 -11 = 117 byte

为何要减去11个byte?

因为我们使用的是PKCS1Padding占用了11个byte,那么它能加密的明文长度就必须减去这11个byte 。

可能会遇到什么问题?

基于以上三点,我们大概可以知道要完成文件加解密,我们可能会遇到什么问题?

一次性加密明文的长度是和密钥长度有关系的,那么我们要加密一个文件,不能一次性将文本内容读取出来,然后加密 如果文件很大,我们也不可能将文件内容一次性读取到内存当中,可能会直接导致服务器无法响应其他请求,这肯定是不合理的 文本被加密之后,回头解密,如果读取的长度有差异势必导致解密失败,那么这个数据库备份文件就废了,这个就比较危险了 。

Do It

安装依赖,python版本3.7.4 。

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

导入模块:

?
1
2
3
4
5
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

生成公钥+私钥,注意这里我们生成的公钥长度是1024bit 。

?
1
2
3
4
5
6
7
8
9
10
11
# 伪随机数生成器
random_generator  =  Random.new().read
# rsa算法生成实例
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)'''

加密,这里对传入的明文长度做了切分,因为我们生成的密钥长度为1024bit,所以我们一次加密的明文长度不能超过117个byte 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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为密钥长度
     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

解密 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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  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

加解密文件,考虑开头我们提出的问题,采用了逐行读取,逐行加密,加密后密文也逐行写入 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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()

测试,一开始我使用的是自己随便输入的一行很长的数字文本,亲测没有问题,但是当我直接使用我的数据库脚本文件的时候,加密可以成功,但是会遇到解密后解码失败的情况,当时百思不得其解,我以为是字符集的问题,于是我将utf-8,换成了gb2312,加解密成功了,当时心花怒放,直到我重新加解密了另一个备份文件,又遇到解码失败,当时就睡不着觉了~ 。

直到我看到了这句话不完整的多字节序列(incomplete multibyte sequence)我瞬间明白了,因为我的脚本文件中含有中文,utf8 编码一个汉字是3个byte,gb2312编码一个汉字是2个byte,只要是多字节,那么做切割的时候,就有可能一个汉字被切割成了两部分,那么自然会导致无法解码成正确的汉字了,问题已经明了,就看怎么解决了.

因为是脚本文件,处理不好就有可能导致脚本执行失败,最终导致数据库还原失败,这就违背项目初衷了~ 。

所以我想了一个办法,先对每一行文本做字符编码判断,超过了117,最后一个字符就不累计上去,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def  cut_string(message,length  =  117 ):
     result  =  []
     temp_char  =  []
     for  msg  in  message: #遍历每一个字符
         msg_encode  =  msg.encode( "utf-8" ) #对每一个字符编码
         temp_encode  =  " ".join(temp_char).encode(" utf - 8 ") #累计编码之后的字节数
         if  len (temp_encode)  +  len (msg_encode) < =  length: #如果小于约定的长度,加添加入结果集
             temp_char.append(msg)
         else : #如果已经超过了约定的长度,就添加入下一个结果集
             result.append("".join(temp_char))
             temp_char.clear()
             temp_char.append(msg)
     result.append("".join(temp_char))
     return  result

加密方法需要重新调整一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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()

到此问题就已经解决了,其实有了这个cut_string方法之后,之前写的加解密方法中不需要再做切分,但是代码保留.

上面的方法,加解密的效率非常的低,因为是逐行加解密,一个300M的脚本文件,加密完成耗时40分钟,这个实在是太难受了,所以调整了策略,先压缩再加密,所以就涉及到二进制文件的读取与写入,最后的实现代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def rsa_encrypt_binfile(file_path,save_path,pub_key):
   '''
   rsa 加密二进制文件
   :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为密钥长度
   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)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def rsa_decrypt_binfile(file_path,save_path,priv_key):
   '''
   rsa 解密二进制文件
   :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 实现RSA加解密文本文件的详细内容,更多关于python rsa加解密的资料请关注我其它相关文章! 。

原文链接:https://www.cnblogs.com/dwBurning/p/python_rsa.html 。

最后此篇关于Python 实现RSA加解密文本文件的文章就讲到这里了,如果你想了解更多关于Python 实现RSA加解密文本文件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com