1、PyCryptodome的padding方法
pad和unpad函数是从Crypto.Util复制的。填充并修改为只使用PKCS7填充。注意,当使用PKCS7填充时,填充最后一个块是很重要的,即使它的大小是块大小的倍数,否则您将无法正确地解填充。
try:
from Crypto.Util.Padding import pad, unpad
except ImportError:
from Crypto.Util.py3compat import bchr, bord
def pad(data_to_pad, block_size):
padding_len = block_size-len(data_to_pad)%block_size
padding = bchr(padding_len)*padding_len
return data_to_pad + padding
def unpad(padded_data, block_size):
pdata_len = len(padded_data)
if pdata_len % block_size:
raise ValueError("Input data is not padded")
padding_len = bord(padded_data[-1])
if padding_len<1 or padding_len>min(block_size, pdata_len):
raise ValueError("Padding is incorrect.")
if padded_data[-padding_len:]!=bchr(padding_len)*padding_len:
raise ValueError("PKCS#7 padding is incorrect.")
return padded_data[:-padding_len]
2、文件加密和解密方法
1)加密方法代码
def encrypt_file(key, in_filename, out_filename=None, chunksize=64*1024): if not out_filename: out_filename = in_filename + '.enc' iv = os.urandom(16) encryptor = AES.new(key, AES.MODE_CBC, iv) filesize = os.path.getsize(in_filename) with open(in_filename, 'rb') as infile: with open(out_filename, 'wb') as outfile: outfile.write(struct.pack('<Q', filesize)) outfile.write(iv) pos = 0 while pos < filesize: chunk = infile.read(chunksize) pos += len(chunk) if pos == filesize: chunk = pad(chunk, AES.block_size) outfile.write(encryptor.encrypt(chunk))
2)解密方法代码
def decrypt_file(key, in_filename, out_filename=None, chunksize=64*1024): if not out_filename: out_filename = in_filename + '.dec' with open(in_filename, 'rb') as infile: filesize = struct.unpack('<Q', infile.read(8))[0] iv = infile.read(16) encryptor = AES.new(key, AES.MODE_CBC, iv) with open(out_filename, 'wb') as outfile: encrypted_filesize = os.path.getsize(in_filename) pos = 8 + 16 # the filesize and IV. while pos < encrypted_filesize: chunk = infile.read(chunksize) pos += len(chunk) chunk = encryptor.decrypt(chunk) if pos == encrypted_filesize: chunk = unpad(chunk, AES.block_size) outfile.write(chunk)
注意:上面代码与Python2/Python3兼容,它应该可以与PyCryptodome或PyCrypto一起使用。
但是,虽然本文介绍的是PyCrypto,还是建议您更新到PyCryptodome。PyCryptodome是PyCrypto的一个分支,它公开了相同的API(因此您不需要过多地更改代码),还提供了一些额外的特性:填充函数、经过身份验证的加密算法、KDFs等。另一方面,PyCrypto不再被维护,而且一些版本还存在基于堆的缓冲区溢出漏洞。
安装PyCryptodome使用下面命令:
pip install PyCryptodome
3、完整代码
import os, random, struct from Crypto.Cipher import AES try: from Crypto.Util.Padding import pad, unpad except ImportError: from Crypto.Util.py3compat import bchr, bord def pad(data_to_pad, block_size): padding_len = block_size-len(data_to_pad)%block_size padding = bchr(padding_len)*padding_len return data_to_pad + padding def unpad(padded_data, block_size): pdata_len = len(padded_data) if pdata_len % block_size: raise ValueError("Input data is not padded") padding_len = bord(padded_data[-1]) if padding_len<1 or padding_len>min(block_size, pdata_len): raise ValueError("Padding is incorrect.") if padded_data[-padding_len:]!=bchr(padding_len)*padding_len: raise ValueError("PKCS#7 padding is incorrect.") return padded_data[:-padding_len] def encrypt_file(key, in_filename, out_filename=None, chunksize=64*1024): if not out_filename: out_filename = in_filename + '.enc' iv = os.urandom(16) encryptor = AES.new(key, AES.MODE_CBC, iv) filesize = os.path.getsize(in_filename) with open(in_filename, 'rb') as infile: with open(out_filename, 'wb') as outfile: outfile.write(struct.pack('<Q', filesize)) outfile.write(iv) pos = 0 while pos < filesize: chunk = infile.read(chunksize) pos += len(chunk) if pos == filesize: chunk = pad(chunk, AES.block_size) outfile.write(encryptor.encrypt(chunk)) def decrypt_file(key, in_filename, out_filename=None, chunksize=64*1024): if not out_filename: out_filename = in_filename + '.dec' with open(in_filename, 'rb') as infile: filesize = struct.unpack('<Q', infile.read(8))[0] iv = infile.read(16) encryptor = AES.new(key, AES.MODE_CBC, iv) with open(out_filename, 'wb') as outfile: encrypted_filesize = os.path.getsize(in_filename) pos = 8 + 16 # the filesize and IV. while pos < encrypted_filesize: chunk = infile.read(chunksize) pos += len(chunk) chunk = encryptor.decrypt(chunk) if pos == encrypted_filesize: chunk = unpad(chunk, AES.block_size) outfile.write(chunk) #测试代码 if __name__=='__main__': encrypt_file("keyskeyskeyskeys".encode('utf-8'),"/root/myfile") decrypt_file("keyskeyskeyskeys".encode('utf-8'),"/root/myfile.enc")