如何使用AES和pycrypto加密.docx文件而不损坏文件

3 投票
2 回答
2438 浏览
提问于 2025-04-18 07:45

我有一段Python代码,想用它来用AES 256加密各种文件。我使用的是pycrypto模块。对于大多数文件(比如exe、deb、jpg、pdf、txt),它运行得很好,但当我处理办公文件(如docx、xlsx、ppt等)时,解密后文件就坏掉了,无法在LibreOffice中打开(也无法修复)。我使用的是Linux mint,Python 2.7.6,pycrypto 2.6.1。我还是个新手,所以如果你能给我一些你推荐的代码示例来修正这个问题,我会很感激。

谢谢

from Crypto import Random
from Crypto.Cipher import AES
import os

def pad(s):
    return s + b"\0" * (AES.block_size - len(s) % AES.block_size)

def encrypt(message, key, key_size=256):
    message = pad(message)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(message)

def decrypt(ciphertext, key):
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext[AES.block_size:])
    return plaintext.rstrip(b"\0")

def encrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        plaintext = fo.read()
    enc = encrypt(plaintext, key)
    with open(file_name + ".enc", 'wb') as fo:
        fo.write(enc)

def decrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        ciphertext = fo.read()
    dec = decrypt(ciphertext, key)
    with open(file_name[:-4], 'wb') as fo:
        fo.write(dec)

key = b'\xbf\xc0\x85)\x10nc\x94\x02)j\xdf\xcb\xc4\x94\x9d(\x9e[EX\xc8\xd5\xbfI{\xa2$\x05(\xd5\x18'

encrypt_file('file.docx', key)

2 个回答

0

如果你需要给明文添加填充,以便让它的长度变成16个字节的倍数,那么在你写入解密后的数据之前,必须去掉这些多出来的字节。这就意味着在你加密之前,得想办法把添加的填充字节的数量包含进去。你可以参考PKCS#7,这是一种可能的处理方法。其实还有其他几种方案可以选择。

2

问题出在这里

plaintext.rstrip(b"\0")

我运行了这个程序,发现原因是:

这里有个错误,如果原始文件的最后几个字节和填充字节的值相同,就会导致这些字节被丢弃!

要解决这个问题,我们需要记录在加密过程中使用了多少个填充字节,然后在解密时把它们去掉。下面是我的代码,运行得很好(我测试过Word和Excel 2013文件、PDF和JPG)。如果还有其他问题,请告诉我。

from Crypto import Random
from Crypto.Cipher import AES

import hashlib

def pad(s):
    padding_size = AES.block_size - len(s) % AES.block_size
    return s + b"\0" * padding_size, padding_size

def encrypt(message, key, key_size=256):
    message, padding_size = pad(message)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CFB, iv)
    enc_bytes = iv + cipher.encrypt(message) + bytes([padding_size])    
    return enc_bytes

def decrypt(ciphertext, key):
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CFB, iv)
    plaintext = cipher.decrypt(ciphertext[AES.block_size:-1])
    padding_size = ciphertext[-1] * (-1)
    return plaintext[:padding_size]

def encrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        plaintext = fo.read()    
    enc = encrypt(plaintext, key)
    with open(file_name + ".enc", 'wb') as fo:
        fo.write(enc)

def decrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        ciphertext = fo.read()
    dec = decrypt(ciphertext, key)
    with open('processed_' + file_name[:-4], 'wb') as fo:
        fo.write(dec)

key = 'Quan'
hash_object = hashlib.md5(key.encode())

while True:
    filename = input('File: ')
    en_de = input('En or De?')
    if en_de.upper() == 'EN':
        encrypt_file(filename, hash_object.hexdigest())
    elif en_de.upper() == 'DE':
        decrypt_file(filename, hash_object.hexdigest())
    else:
        print('Did not pick either en or de!')

    cont = input('Continue?')
    if cont.upper() == 'N':
        break

撰写回答