如何在Python中解密使用OpenSSL AES加密的文件?

60

OpenSSL提供了一个流行的(但不安全 - 请参见下文!)AES加密命令行界面:

openssl aes-256-cbc -salt -in filename -out filename.enc

Python通过PyCrypto包支持AES,但只提供了工具。如何使用Python/PyCrypto解密使用OpenSSL加密的文件?

注意事项

这个问题曾经也涉及到使用相同方案在Python中进行加密。我已经删除了那部分内容,以阻止任何人再次使用它。不要再使用这种方式加密任何数据,因为按照今天的标准,它是不安全的。你只应该出于向后兼容性的原因(即当没有其他选择时),才仅使用解密。想加密?尽可能使用NaCl/libsodium。


3
感谢您对自身的跟进,但这并不适合成为一个良好的标准,因为基于密码的密钥派生仅基于单次MD5迭代(尽管使用盐)。至少应该使用更多迭代的PBKDF2 / scrypt。 - SquareRootOfTwentyThree
@SquareRootOfTwentyThree 谢谢,我稍微调查了一下那个特定的主题,确实是个好观点。 - Thijs van Dien
@SquareRootOfTwentyThree 提出了一个非常好的观点,apps/enc.c 使用迭代次数为1的 EVP_BytesToKey。对于普通密码来说,这是完全不适合的,因为可以很容易地进行暴力破解。该手册建议使用更适当的解决方案 PBKDF2。既然这段代码被 用于 Ansible Vault,那么是否考虑从一个明确的警告开始,不要使用它,除非是为了向后兼容? - Lekensteyn
@ThijsvanDien 现在它仅用于遗留目的,但在2014年的最初实现中(当时他们完全可以自由选择原语),他们仍然设法选择了这个 :-( 感谢编辑! - Lekensteyn
1
@Lekensteyn 我一直收到有关如何在其他语言中解密的问题,建议人们无论如何都使用加密代码。截至今天,它只能在编辑历史记录中找到。 - Thijs van Dien
显示剩余3条评论
7个回答

97

鉴于 Python 的流行,起初我很失望没有找到完整的答案。为了弄清楚这个问题,我阅读了本论坛上不同的回答和其他资源,花费了相当多的时间。我想分享一下我得出的结论,供将来参考和审查;我绝对不是密码学专家!然而,下面的代码似乎可以完美地工作:

from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random

def derive_key_and_iv(password, salt, key_length, iv_length):
    d = d_i = ''
    while len(d) < key_length + iv_length:
        d_i = md5(d_i + password + salt).digest()
        d += d_i
    return d[:key_length], d[key_length:key_length+iv_length]

def decrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size
    salt = in_file.read(bs)[len('Salted__'):]
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    next_chunk = ''
    finished = False
    while not finished:
        chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
        if len(next_chunk) == 0:
            padding_length = ord(chunk[-1])
            chunk = chunk[:-padding_length]
            finished = True
        out_file.write(chunk)

使用方法:

with open(in_filename, 'rb') as in_file, open(out_filename, 'wb') as out_file:
    decrypt(in_file, out_file, password)

如果你看到有机会改进或扩展这个方法,使其更加灵活(例如,让它在没有salt的情况下工作,或提供Python 3兼容性),请随意这样做。

注意

这个答案曾经也涉及使用相同方案在Python中进行加密。我已经删除了那部分来阻止任何人使用它。不要再以这种方式加密数据,因为它在今天的标准下不安全。你应该只使用解密,理由只有向后兼容,即当你没有其他选择时才使用。想加密吗?尽可能使用NaCl/libsodium。


1
这个实现与这个相比如何?有什么相对优势或劣势吗? - rattray
3
主要区别在于你的例子是关于Python中AES的一般使用方法,而我的则是关于与OpenSSL实现的兼容性,这样您就可以使用一个众所周知的命令行工具来解密使用上述Python代码加密的文件,反之亦然。 - Thijs van Dien
1
@KennyPowers 我认为你无法做到这一点,否则将会破坏 OpenSSL 的兼容性,而这也是此问题的主要目标。如果您不需要它,有更好的方法来执行加密,这也将为您带来所需的灵活性。 - Thijs van Dien
1
@SteveWalsh 我的代码需要二进制,而你的 file.enc 是经过 base64 编码的(使用了 -a 参数)。请删除该参数或在解密之前对文件进行解码。如需进一步支持,请发起您自己的问题。 - Thijs van Dien
1
@SaketKumarSingh 我认为该命令并没有执行你想要的操作。看起来你是在使用密码“symmetric_keyfile.key”加密文件,而不是使用该文件中的内容。 - Thijs van Dien
显示剩余11条评论

22

我稍微更正了一下你的代码(我不想掩盖你的版本)。虽然你的代码可以运行,但它无法检测到一些与填充有关的错误。特别是,如果提供的解密密钥不正确,你的填充逻辑可能会出现异常情况。如果你同意我的更改,你可以更新你的解决方案。

from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random

def derive_key_and_iv(password, salt, key_length, iv_length):
    d = d_i = ''
    while len(d) < key_length + iv_length:
        d_i = md5(d_i + password + salt).digest()
        d += d_i
    return d[:key_length], d[key_length:key_length+iv_length]

# This encryption mode is no longer secure by today's standards.
# See note in original question above.
def obsolete_encrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size
    salt = Random.new().read(bs - len('Salted__'))
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    out_file.write('Salted__' + salt)
    finished = False
    while not finished:
        chunk = in_file.read(1024 * bs)
        if len(chunk) == 0 or len(chunk) % bs != 0:
            padding_length = bs - (len(chunk) % bs)
            chunk += padding_length * chr(padding_length)
            finished = True
        out_file.write(cipher.encrypt(chunk))

def decrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size
    salt = in_file.read(bs)[len('Salted__'):]
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    next_chunk = ''
    finished = False
    while not finished:
        chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
        if len(next_chunk) == 0:
            padding_length = ord(chunk[-1])
            if padding_length < 1 or padding_length > bs:
               raise ValueError("bad decrypt pad (%d)" % padding_length)
            # all the pad-bytes must be the same
            if chunk[-padding_length:] != (padding_length * chr(padding_length)):
               # this is similar to the bad decrypt:evp_enc.c from openssl program
               raise ValueError("bad decrypt")
            chunk = chunk[:-padding_length]
            finished = True
        out_file.write(chunk)

4
请编辑我的帖子。因为它已经过同行评审了。我通常认为进行一些错误检查是有必要的,不过“缺少填充”这个词有点误导人,实际上填充太多了。这和OpenSSL报告的错误一样吗? - Thijs van Dien
已进行更正以更接近于evp_enc.c中openssl输出的内容,该输出对两种情况都会输出相同的“解密失败”信息。 - Gregor
太好了!我也想在.NET中进行解密。有人可以帮我转换成这种语言吗? - Eduardo
2
我已经从我的答案中删除了“encrypt”函数,并鼓励你也这样做。 - Thijs van Dien

13

下面的代码应该与Python 3兼容,其中包含文档记录的小更改。此外,想要使用os.urandom代替Crypto.Random。 'Salted__'被替换为salt_header,如果需要,可以定制或保留为空。

from os import urandom
from hashlib import md5

from Crypto.Cipher import AES

def derive_key_and_iv(password, salt, key_length, iv_length):
    d = d_i = b''  # changed '' to b''
    while len(d) < key_length + iv_length:
        # changed password to str.encode(password)
        d_i = md5(d_i + str.encode(password) + salt).digest()
        d += d_i
    return d[:key_length], d[key_length:key_length+iv_length]

def encrypt(in_file, out_file, password, salt_header='', key_length=32):
    # added salt_header=''
    bs = AES.block_size
    # replaced Crypt.Random with os.urandom
    salt = urandom(bs - len(salt_header))
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    # changed 'Salted__' to str.encode(salt_header)
    out_file.write(str.encode(salt_header) + salt)
    finished = False
    while not finished:
        chunk = in_file.read(1024 * bs) 
        if len(chunk) == 0 or len(chunk) % bs != 0:
            padding_length = (bs - len(chunk) % bs) or bs
            # changed right side to str.encode(...)
            chunk += str.encode(
                padding_length * chr(padding_length))
            finished = True
        out_file.write(cipher.encrypt(chunk))

def decrypt(in_file, out_file, password, salt_header='', key_length=32):
    # added salt_header=''
    bs = AES.block_size
    # changed 'Salted__' to salt_header
    salt = in_file.read(bs)[len(salt_header):]
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    next_chunk = ''
    finished = False
    while not finished:
        chunk, next_chunk = next_chunk, cipher.decrypt(
            in_file.read(1024 * bs))
        if len(next_chunk) == 0:
            padding_length = chunk[-1]  # removed ord(...) as unnecessary
            chunk = chunk[:-padding_length]
            finished = True 
        out_file.write(bytes(x for x in chunk))  # changed chunk to bytes(...)

1
这段代码显然没有经过测试,不能直接运行。 - Chris Arndt
1
@ChrisArndt 在我的Python 3上运行良好。 - Stephen Fuhry
1
对不起,我已经记不清了,之前有什么问题困扰了我。但是,我实现了自己的脚本来使用AES加密一个文件:https://gist.github.com/SpotlightKid/53e1eb408267315de620 - Chris Arndt
1
@StephenFuhry 我知道这是一篇旧帖子,但代码中有一个微妙的错误,你可能需要修复它 - "out_file.write(bytes(x for x in chunk))" 这行应该向外移动一层,否则你只能解密最后一个块。 - Vineet Bansal
1
我已经从我的答案中删除了 encrypt 函数,并鼓励你也这样做。 - Thijs van Dien
显示剩余5条评论

3
这个答案基于openssl v1.1.1,它支持比先前版本的openssl更强的AES加密密钥派生过程。
这个答案基于以下命令:
echo -n 'Hello World!' | openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000 

这个命令使用 aes-256-cbc 加密明文 'Hello World!'。密钥是使用 pbkdf2 从密码和随机盐派生出来的,使用 10,000 次 sha256 哈希迭代。当提示输入密码时,我输入了密码 'p4$$w0rd'。命令产生的密文输出为:
U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=

上述 OpenSSL 生成的密文的解密过程如下:
  1. 对 OpenSSL 输出进行 base64 解码,并使用 utf-8 解码密码,以便我们拥有这两者的基础字节。
  2. 盐是 base64 解码后的 openssl 输出的第 8 到 15 字节。
  3. 使用 pbkdf2 密码派生出一个 48 字节的密钥,迭代 10,000 次 sha256 哈希。
  4. 密钥是派生密钥的前 32 字节,iv 是派生密钥的第 32 到 47 字节。
  5. 密文是 base64 解码后的 openssl 输出的第 16 字节到末尾。
  6. 使用 aes-256-cbc 解密密文,给定密钥、iv 和密文。
  7. 从明文中删除 PKCS#7 填充。明文的最后一个字节指示附加到明文末尾的填充字节数。这是要删除的字节数。
以下是上述过程的 Python3 实现:
import binascii
import base64
import hashlib
from Crypto.Cipher import AES       #requires pycrypto

#inputs
openssloutputb64='U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE='
password='p4$$w0rd'
pbkdf2iterations=10000

#convert inputs to bytes
openssloutputbytes=base64.b64decode(openssloutputb64)
passwordbytes=password.encode('utf-8')

#salt is bytes 8 through 15 of openssloutputbytes
salt=openssloutputbytes[8:16]

#derive a 48-byte key using pbkdf2 given the password and salt with 10,000 iterations of sha256 hashing
derivedkey=hashlib.pbkdf2_hmac('sha256', passwordbytes, salt, pbkdf2iterations, 48)

#key is bytes 0-31 of derivedkey, iv is bytes 32-47 of derivedkey 
key=derivedkey[0:32]
iv=derivedkey[32:48]

#ciphertext is bytes 16-end of openssloutputbytes
ciphertext=openssloutputbytes[16:]

#decrypt ciphertext using aes-cbc, given key, iv, and ciphertext
decryptor=AES.new(key, AES.MODE_CBC, iv)
plaintext=decryptor.decrypt(ciphertext)

#remove PKCS#7 padding. 
#Last byte of plaintext indicates the number of padding bytes appended to end of plaintext.  This is the number of bytes to be removed.
plaintext = plaintext[:-plaintext[-1]]

#output results
print('openssloutputb64:', openssloutputb64)
print('password:', password)
print('salt:', salt.hex())
print('key: ', key.hex())
print('iv: ', iv.hex())
print('ciphertext: ', ciphertext.hex())
print('plaintext: ', plaintext.decode('utf-8'))

如预期,上述 Python3 脚本会产生以下输出:
openssloutputb64: U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=
password: p4$$w0rd
salt: ca7fc628e898c187
key:  444ab886d5721fc87e58f86f3e7734659007bea7fbe790541d9e73c481d9d983
iv:  7f4597a18096715d7f9830f0125be8fd
ciphertext:  ea842d6862ac05ebefcf9b6cf4239711
plaintext:  Hello World!

注意:可在 https://github.com/meixler/web-browser-based-file-encryption-decryption 找到用 JavaScript 实现的等效/兼容版本(使用 Web Crypto API)。

有趣的补充! - Thijs van Dien

0

尝试了上面的所有方法以及其他线程中的一些方法,这是对我有效的openssl等效方法:

不是最好的加密方式,但这是要求

解密:openssl enc -d -aes256 -md md5 -in {->path_in} -out {->path_out} -pass pass:{->pass}

加密:openssl enc -e -aes256 -md md5 -in {->path_in} -out {->path_out} -pass pass:{->pass}

Python:

from os import urandom
from hashlib import md5
from Crypto.Cipher import AES
import typer

def filecrypto(in_file, out_file, password, decrypt: bool = True):
    salt_header = 'Salted__'

    def derive_key_and_iv(password, salt, key_length, iv_length):
        d = d_i = b''  # changed '' to b''
        while len(d) < key_length + iv_length:
            # changed password to str.encode(password)
            d_i = md5(d_i + str.encode(password) + salt).digest()
            d += d_i

        return d[:key_length], d[key_length:key_length+iv_length]

    def encrypt_f(in_file, out_file, password, salt_header=salt_header, key_length=32):
        bs = AES.block_size
        salt = urandom(bs - len(salt_header))
        key, iv = derive_key_and_iv(password, salt, key_length, bs)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        with open(out_file, 'wb') as f_out:
            # write the first line or the salted header
            f_out.write(str.encode(salt_header) + salt)
            with open(in_file, 'rb') as f_in:
                f_out.write(cipher.encrypt(f_in.read()))

    def decrypt_f(in_file, out_file, password, salt_header=salt_header, key_length=32):
        bs = AES.block_size
        with open(in_file, 'rb') as f_in:
            # retrieve the salted header
            salt = f_in.read(bs)[len(salt_header):]
            key, iv = derive_key_and_iv(password, salt, key_length, bs)
            cipher = AES.new(key, AES.MODE_CBC, iv)
            with open(out_file, 'wb') as f_out:
                f_out.write(cipher.decrypt(f_in.read()))

    return decrypt_f(in_file, out_file, password) if decrypt else encrypt_f(in_file, out_file, password)

if __name__ == "__filecrypto__":
    typer.run(filecrypto)

0

我知道有点晚了,但是这里是一个解决方案,我在2013年写的博客中介绍了如何使用Python pycrypto包以openssl兼容的方式进行加密/解密。它已经在Python2.7和Python3.x上进行了测试。源代码和测试脚本可以在这里找到。

这个解决方案与上面提出的优秀解决方案之间的一个关键区别是,它区分了管道和文件I/O,这可能会在某些应用程序中引起问题。

该博客中的关键函数如下所示。

# ================================================================
# get_key_and_iv
# ================================================================
def get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst='md5'):
    '''
    Derive the key and the IV from the given password and salt.

    This is a niftier implementation than my direct transliteration of
    the C++ code although I modified to support different digests.

    CITATION: https://dev59.com/ZWzXa4cB1Zd3GeqPQiiS

    @param password  The password to use as the seed.
    @param salt      The salt.
    @param klen      The key length.
    @param ilen      The initialization vector length.
    @param msgdgst   The message digest algorithm to use.
    '''
    # equivalent to:
    #   from hashlib import <mdi> as mdf
    #   from hashlib import md5 as mdf
    #   from hashlib import sha512 as mdf
    mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
    password = password.encode('ascii', 'ignore')  # convert to ASCII

    try:
        maxlen = klen + ilen
        keyiv = mdf(password + salt).digest()
        tmp = [keyiv]
        while len(tmp) < maxlen:
            tmp.append( mdf(tmp[-1] + password + salt).digest() )
            keyiv += tmp[-1]  # append the last byte
        key = keyiv[:klen]
        iv = keyiv[klen:klen+ilen]
        return key, iv
    except UnicodeDecodeError:
        return None, None


# ================================================================
# encrypt
# ================================================================
def encrypt(password, plaintext, chunkit=True, msgdgst='md5'):
    '''
    Encrypt the plaintext using the password using an openssl
    compatible encryption algorithm. It is the same as creating a file
    with plaintext contents and running openssl like this:

    $ cat plaintext
    <plaintext>
    $ openssl enc -e -aes-256-cbc -base64 -salt \\
        -pass pass:<password> -n plaintext

    @param password  The password.
    @param plaintext The plaintext to encrypt.
    @param chunkit   Flag that tells encrypt to split the ciphertext
                     into 64 character (MIME encoded) lines.
                     This does not affect the decrypt operation.
    @param msgdgst   The message digest algorithm.
    '''
    salt = os.urandom(8)
    key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
    if key is None:
        return None

    # PKCS#7 padding
    padding_len = 16 - (len(plaintext) % 16)
    if isinstance(plaintext, str):
        padded_plaintext = plaintext + (chr(padding_len) * padding_len)
    else: # assume bytes
        padded_plaintext = plaintext + (bytearray([padding_len] * padding_len))

    # Encrypt
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(padded_plaintext)

    # Make openssl compatible.
    # I first discovered this when I wrote the C++ Cipher class.
    # CITATION: http://projects.joelinoff.com/cipher-1.1/doxydocs/html/
    openssl_ciphertext = b'Salted__' + salt + ciphertext
    b64 = base64.b64encode(openssl_ciphertext)
    if not chunkit:
        return b64

    LINELEN = 64
    chunk = lambda s: b'\n'.join(s[i:min(i+LINELEN, len(s))]
                                for i in range(0, len(s), LINELEN))
    return chunk(b64)


# ================================================================
# decrypt
# ================================================================
def decrypt(password, ciphertext, msgdgst='md5'):
    '''
    Decrypt the ciphertext using the password using an openssl
    compatible decryption algorithm. It is the same as creating a file
    with ciphertext contents and running openssl like this:

    $ cat ciphertext
    # ENCRYPTED
    <ciphertext>
    $ egrep -v '^#|^$' | \\
        openssl enc -d -aes-256-cbc -base64 -salt -pass pass:<password> -in ciphertext
    @param password   The password.
    @param ciphertext The ciphertext to decrypt.
    @param msgdgst    The message digest algorithm.
    @returns the decrypted data.
    '''

    # unfilter -- ignore blank lines and comments
    if isinstance(ciphertext, str):
        filtered = ''
        nl = '\n'
        re1 = r'^\s*$'
        re2 = r'^\s*#'
    else:
        filtered = b''
        nl = b'\n'
        re1 = b'^\\s*$'
        re2 = b'^\\s*#'

    for line in ciphertext.split(nl):
        line = line.strip()
        if re.search(re1,line) or re.search(re2, line):
            continue
        filtered += line + nl

    # Base64 decode
    raw = base64.b64decode(filtered)
    assert(raw[:8] == b'Salted__' )
    salt = raw[8:16]  # get the salt

    # Now create the key and iv.
    key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
    if key is None:
        return None

    # The original ciphertext
    ciphertext = raw[16:]

    # Decrypt
    cipher = AES.new(key, AES.MODE_CBC, iv)
    padded_plaintext = cipher.decrypt(ciphertext)

    if isinstance(padded_plaintext, str):
        padding_len = ord(padded_plaintext[-1])
    else:
        padding_len = padded_plaintext[-1]
    plaintext = padded_plaintext[:-padding_len]
    return plaintext

我在Python 3.9中无法让这个解决方案工作。当我将这些函数放入我的代码中时,我得到了TypeError: Object type <class 'str'> cannot be passed to C code。博客链接已损坏。而且我也无法让github链接的脚本工作。它似乎在大多数事情上都停滞不前。当我使用-V选项时,它会输出"b'mcrypt.py' 1.2"。肯定有可能是我没有做正确的事情。 - Michael K
哇,很抱歉你遇到了问题,我已经有一段时间没有看过这个了,我会去看看的,同时,你可以尝试使用 https://github.com/jlinoff/lock_files ,它应该还能正常工作。这是你遇到问题的博客网址吗:https://joelinoff.com/blog/?p=885? - Joe Linoff
看起来pycrypto包发生了一些变化。我通过将安装包名称从crypto更改为Crypto来解决了这个问题,但这种方法太过hacky。我将删除这个要点以避免混淆其他人。这可能会有所帮助:https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption。 - Joe Linoff
我决定保留摘要并更新它以反映本次对话,同时详细描述所需的解决方法以使其正常工作。感谢您报告此问题。摘要链接:https://gist.github.com/jlinoff/412752f1ecb6b27762539c0f6b6d667b - Joe Linoff
没关系,我知道这是2017年的内容,而且我也遇到了很多其他问题,试图让Python支持OpenSSL兼容的解密。最终我使用子进程运行了OpenSSL代码。顺便说一下,博客链接实际上并没有失效,但除了“提供openssl-aes-256-cbc兼容加密/解密的简单Python函数”(看起来只有标题和侧边栏)之外,没有其他内容。我稍微阅读了一下你的lock_files项目,非常棒。 - Michael K
谢谢您的澄清,我会解决博客问题。 - Joe Linoff

-1

注意:此方法不兼容OpenSSL

但如果您只想加密和解密文件,它是适用的。

这是我从这里复制的自问自答。我认为这可能是一个更简单、更安全的选项。虽然我很想听听专家对其安全性的意见。

我使用Python 3.6和SimpleCrypt来加密文件,然后上传它。

认为这是我用来加密文件的代码:

from simplecrypt import encrypt, decrypt
f = open('file.csv','r').read()
ciphertext = encrypt('USERPASSWORD',f.encode('utf8')) # I am not certain of whether I used the .encode('utf8')
e = open('file.enc','wb') # file.enc doesn't need to exist, python will create it
e.write(ciphertext)
e.close

这是我在运行时解密的代码,我将getpass("password: ")用作参数,以便我不必在内存中存储password变量。
from simplecrypt import encrypt, decrypt
from getpass import getpass

# opens the file
f = open('file.enc','rb').read()

print('Please enter the password and press the enter key \n Decryption may take some time')

# Decrypts the data, requires a user-input password
plaintext = decrypt(getpass("password: "), f).decode('utf8')
print('Data have been Decrypted')

请注意,在Python 2.7中,UTF-8编码的行为与其他版本不同,因此代码会稍有不同。

请注意,这个问题特别涉及与OpenSSL的兼容性;而不是关于在Python中执行加密的好方法(OpenSSL的方式肯定不是)。因此,你的回答并不适合这个问题,因此我会给你点个踩。 - Thijs van Dien
@ThijsvanDien 感谢您指出这一点。我没有意识到,因为我的帖子 在Python 3中导入加密的csv文件 被标记为潜在的重复帖子。我已经编辑了帖子以进行澄清。 - Harvs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接