近来在使用python写项目,特此记录一下项目中遇到的文件加解密问题。
关于python版本的加密算法,随便搜一搜还是可以检索出来很多的,不过大都是同一篇文章在不同的平台来回发布,或者就是转载,而且例举的都是最简单的情况,那么,实际项目中使用的话,肯定会比这个要稍微复杂一些,比如我的需求就是要加密一个使用mysqldump出来的数据库脚本文件,直接拿网上的例子过来调用肯定是不行的,所以不得不自己研究了一番,特此记录。
项目选型的算法是RSA非对称加密算法,关于这个算法不做过多的解释,咱们划重点:
因为我们使用的是PKCS1Padding占用了11个byte,那么它能加密的明文长度就必须减去这11个byte
基于以上三点,我们大概可以知道要完成文件加解密,我们可能会遇到什么问题?
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
#?伪随机数生成器
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)‘‘‘
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_rsa_encrypt:
????????????result.append(default_rsa_encrypt(
????????????????cipher,?message[offset:offset+default_rsa_encrypt]))
????????else:
????????????result.append(default_rsa_encrypt(cipher,?message[offset:]))
????????offset?+=?default_rsa_encrypt
????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()
直到我看到了这句话不完整的多字节序列(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方法之后,之前写的加解密方法中不需要再做切分,但是代码保留。
原文:https://www.cnblogs.com/dwBurning/p/python_rsa.html