近来在使用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"htmlcode">
pip install pycryptodomex -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install pycryptodomex -i https://pypi.tuna.tsinghua.edu.cn/simple/
导入模块:
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
# 伪随机数生成器 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
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
解密
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
加解密文件,考虑开头我们提出的问题,采用了逐行读取,逐行加密,加密后密文也逐行写入
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,最后一个字符就不累计上去,代码如下:
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
加密方法需要重新调整一下:
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分钟,这个实在是太难受了,所以调整了策略,先压缩再加密,所以就涉及到二进制文件的读取与写入,最后的实现代码如下:
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)
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加解密的资料请关注其它相关文章!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 中国武警男声合唱团《辉煌之声1天路》[DTS-WAV分轨]
- 紫薇《旧曲新韵》[320K/MP3][175.29MB]
- 紫薇《旧曲新韵》[FLAC/分轨][550.18MB]
- 周深《反深代词》[先听版][320K/MP3][72.71MB]
- 李佳薇.2024-会发光的【黑籁音乐】【FLAC分轨】
- 后弦.2012-很有爱【天浩盛世】【WAV+CUE】
- 林俊吉.2012-将你惜命命【美华】【WAV+CUE】
- 晓雅《分享》DTS-WAV
- 黑鸭子2008-飞歌[首版][WAV+CUE]
- 黄乙玲1989-水泼落地难收回[日本天龙版][WAV+CUE]
- 周深《反深代词》[先听版][FLAC/分轨][310.97MB]
- 姜育恒1984《什么时候·串起又散落》台湾复刻版[WAV+CUE][1G]
- 那英《如今》引进版[WAV+CUE][1G]
- 蔡幸娟.1991-真的让我爱你吗【飞碟】【WAV+CUE】
- 群星.2024-好团圆电视剧原声带【TME】【FLAC分轨】