【问题标题】:Python PyCrypto encrypt/decrypt text files with AESPython PyCrypto 使用 AES 加密/解密文本文件
【发布时间】:2014-01-18 02:43:14
【问题描述】:

我已经有一个工作程序,但唯一不能工作的是我拥有的decrypt_file() 函数。我仍然可以从文件中复制加密文本并将其放入我的decrypt() 函数并让它工作,但是当我尝试使用我应该很方便的decrypt_file() 函数时它会引发错误。现在我知道 99.999% 确定我的 encrypt()decrypt() 函数没问题,但是当我读取和编码文本文件时,字节和字符串转换会引发错误;我只是找不到挂断。请帮忙!

我的程序:

from Crypto import Random
from Crypto.Cipher import AES

def encrypt(message, key=None, key_size=256):
    def pad(s):
        x = AES.block_size - len(s) % AES.block_size
        return s + ((bytes([x])) * x)

    padded_message = pad(message)

    if key is None:
        key = Random.new().read(key_size // 8)

    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)

    return iv + cipher.encrypt(padded_message)

def decrypt(ciphertext, key):
    unpad = lambda s: s[:-s[-1]]
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = unpad(cipher.decrypt(ciphertext))[AES.block_size:]

    return plaintext

def encrypt_file(file_name, key):
    f = open(file_name, 'r')
    plaintext = f.read()
    plaintext = plaintext.encode('utf-8')
    enc = encrypt(plaintext, key)
    f.close()
    f = open(file_name, 'w')
    f.write(str(enc))
    f.close()

def decrypt_file(file_name, key):
    def pad(s):
        x = AES.block_size - len(s) % AES.block_size
        return s + ((str(bytes([x]))) * x)

    f = open(file_name, 'r')
    plaintext = f.read()
    x = AES.block_size - len(plaintext) % AES.block_size
    plaintext += ((bytes([x]))) * x
    dec = decrypt(plaintext, key)
    f.close()
    f = open(file_name, 'w')
    f.write(str(dec))
    f.close()



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('to_enc.txt', key)

我加密的文本文件:

b';c\xb0\xe6Wv5!\xa3\xdd\xf0\xb1\xfd2\x90B\x10\xdf\x00\x82\x83\x9d\xbc2\x91\xa7i M\x13\xdc\xa7'

我在尝试 decrypt_file 时出错:

    Traceback (most recent call last):
  File "C:\Python33\testing\test\crypto.py", line 56, in <module>
    decrypt_file('to_enc.txt', key)
  File "C:\Python33\testing\test\crypto.py", line 45, in decrypt_file
    plaintext += ((bytes([x]))) * x
TypeError: Can't convert 'bytes' object to str implicitly
[Finished in 1.5s]

当我将第 45 行替换为:plaintext += ((str(bytes([x])))) * x,这是我得到的错误:

Traceback (most recent call last):
  File "C:\Python33\testing\test\crypto.py", line 56, in <module>
    decrypt_file('to_enc.txt', key)
  File "C:\Python33\testing\test\crypto.py", line 46, in decrypt_file
    dec = decrypt(plaintext, key)
  File "C:\Python33\testing\test\crypto.py", line 23, in decrypt
    plaintext = unpad(cipher.decrypt(ciphertext))[AES.block_size:]
  File "C:\Python33\lib\site-packages\Crypto\Cipher\blockalgo.py", line 295, in decrypt
    return self._cipher.decrypt(ciphertext)
ValueError: Input strings must be a multiple of 16 in length
[Finished in 1.4s with exit code 1]

【问题讨论】:

  • 它是bytes 对象。那是一种内置类型。您没有在程序中定义它,因此它使用的是内置对象。我想你的意思是plaintext
  • 你是说问题出在第 46 行吗?抱歉,我对这个有点累了,没有真正想清楚。
  • 它在堆栈跟踪中显示了行和代码。
  • 抱歉,第 45 行和第 46 行是我自己的调试类型的东西,所以我删除了这些并用我的实际错误替换了错误。
  • 好的,我还添加了另一个在尝试修复字节时遇到的重要错误,除非我的“修复”不正确。

标签: python file encryption aes pycrypto


【解决方案1】:

我仔细查看了您的代码,发现其中有几个问题。第一个是加密功能使用字节,而不是文本。因此,最好将数据保留为字节字符串。这只需在模式中放置一个“b”字符即可完成。这样你就可以摆脱你试图做的所有编码和字节转换。

我还使用较新的 Python 习语重写了整个代码。在这里。

#!/usr/bin/python3

from Crypto import Random
from Crypto.Cipher import AES

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('to_enc.txt', key)
#decrypt_file('to_enc.txt.enc', key)

【讨论】:

  • 太棒了!谢谢基思。我知道我的代码很草率,但我一直在拼凑其他人的工作部分并尝试自己制作——但无济于事。不过,这很有效!我将继续全面审查代码并对其进行注释,这样我就不会忘记它是如何工作的。
  • 您是否尝试过使用 .docx 或除 .txt 文件之外的任何类型的文件?
  • 我对@9​​87654323@ 函数中的plaintext.rstrip(b"\0") 持怀疑态度。如果明文以空字节结尾怎么办?如果我使用此代码加密文件,解密后文件是否存在损坏的风险?
  • @keith 有没有办法让文件的解密速度更快?我有 mp3 文件要加密和解密。
  • @yajantb 您可以分块阅读源代码,而不是像这样吸入整个内容。这是针对小文本文件的。
【解决方案2】:

在 Python 3(您显然正在使用)中,您打开文件的默认模式是文本,而不是二进制。当您从文件中读取时,您会得到字符串而不是字节数组。这与加密不符。

在您的代码中,您应该替换:

open(file_name, 'r')

与:

open(file_name, 'rb')

打开文件进行写入时也是如此。到那时,您就可以摆脱从字符串转换为二进制的各种情况,反之亦然。

例如,这可以消失:

plaintext = plaintext.encode('utf-8')

【讨论】:

  • 好的,我试过了;我分别用 rb 和 wb 替换了我的所有 open() 模式,现在我遇到了这个问题: encrypt_file 函数可以工作,但是当我尝试使用 decrypt_file() 时它运行良好,但是当我打开它时文件是空的......很抱歉,如果我错过了某些内容,例如 Line x: , Line y: 等,请提供编辑。我对使用字节和加密真的很陌生,所以这一切都“有点”令人困惑——是的是双关语……哈哈
猜你喜欢
  • 2013-12-06
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多