首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Python加密大文件?

如何使用Python加密大文件?
EN

Stack Overflow用户
提问于 2021-09-24 09:46:37
回答 4查看 2.4K关注 0票数 2

我试图加密大于1GB的文件。我不想把这些都读到记忆里。我选择了Fernet (cryptography.fernet)来完成这个任务,因为它是最推荐的(比异步解决方案更快)。

钥匙是我造的。然后我创建了一个用于加密的脚本:

代码语言:javascript
复制
    key = Fernet(read_key())

    with open(source, "rb") as src, open(destination, "wb") as dest:
        for chunk in iter(lambda: src.read(4096), b""):
            encrypted = key.encrypt(chunk)
            dest.write(encrypted)

为了解密:

代码语言:javascript
复制
    key = Fernet(read_key())

    with open(source, "rb") as src, open(destination, "wb") as dest:
        for chunk in iter(lambda: src.read(4096), b""):
            decrypted = key.decrypt(chunk)
            dest.write(decrypted)

加密工作-不奇怪,但解密不是。首先,我认为它可能有用,但它不是。我猜加密后块大小会增加,然后当我读取4096字节时,它并不是一个完整的加密块。我想解密时出错了:

代码语言:javascript
复制
Traceback (most recent call last):
  File "/redacted/path/venv/lib/python3.7/site-packages/cryptography/fernet.py", line 119, in _verify_signature
    h.verify(data[-32:])
  File "/redacted/path/venv/lib/python3.7/site-packages/cryptography/hazmat/primitives/hmac.py", line 74, in verify
    ctx.verify(signature)
  File "/redacted/path/venv/lib/python3.7/site-packages/cryptography/hazmat/backends/openssl/hmac.py", line 75, in verify
    raise InvalidSignature("Signature did not match digest.")
cryptography.exceptions.InvalidSignature: Signature did not match digest.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/redacted/path/main.py", line 63, in <module>
    decrypted = key.decrypt(chunk)
  File "/redacted/path/venv/lib/python3.7/site-packages/cryptography/fernet.py", line 80, in decrypt
    return self._decrypt_data(data, timestamp, time_info)
  File "/redacted/path/venv/lib/python3.7/site-packages/cryptography/fernet.py", line 137, in _decrypt_data
    self._verify_signature(data)
  File "/redacted/path/venv/lib/python3.7/site-packages/cryptography/fernet.py", line 121, in _verify_signature
    raise InvalidToken
cryptography.fernet.InvalidToken

有办法解决这个问题吗?也许有一种比fernet更好(更简单)的解决方案?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2021-09-24 10:34:25

Fernet不应该以流媒体的方式使用。他们在文件中解释说:

从文件(最后一节):

限制 Fernet是加密数据的理想选择,它可以很好地存储在内存中。作为设计特性,它不公开未经身份验证的字节。这意味着完整的消息内容必须在内存中可用,这使得Fernet目前通常不适合处理非常大的文件。

票数 3
EN

Stack Overflow用户

发布于 2022-02-10 16:03:58

我只是遇到了同样的问题-我感觉到你的痛苦兄弟。

Fernet有一些问题使其与您的方法不兼容:

  1. Fernet吐出urlsafe_base64编码的数据。这意味着,每消耗3字节未加密的数据,Fernet就会吐出4字节的加密数据。

这防止您在加密和解密时使用相同的“块大小”,因为解密块大小必须更大。不幸的是,使用urlsafe_b64decode/urlsafe_b64encode处理数据也不起作用,因为:

  1. Fernet似乎在加密的数据中添加了某种摘要/校验和/元数据。

也许有一种简单的方法可以计算出这个摘要有多大,并调整解密块的大小以适应这种情况--但我想避免使用“魔术常量”,因为这感觉很恶心。

我所确定的解决方案实际上是相当优雅的。它的工作如下:

加密:

  1. 读取n字节的数据(raw_chunk)
  2. 用Fernet加密n字节以创建m字节块(enc_chunk)。
  3. 使用len(enc_chunk).to_bytes(4, "big")将加密块的大小写入文件
  4. 将加密的块写入文件
  5. 当我读b""的时候

解密:

  1. 读取4字节的数据(size)
  2. 如果数据是b"",则中断
  3. 使用int.from_bytes(size, "big") (num_bytes)将这4个字节转换为整数
  4. 读取加密数据的num_bytes
  5. 用Fernet解密这些数据,没有问题。
票数 3
EN

Stack Overflow用户

发布于 2022-02-23 19:48:01

只要将输入数据分割成块并将块长度存储在加密文件中,就可以轻松地将任何非流算法(如Fernet)转化为流算法,这已经是@tlonny的建议了。只有当您能够提供任何格式的加密数据文件时,才有可能这样做。

可以通过不同的方式将块大小转换为字节。其中之一是使用struct.pack()struct.unpack(),就像我在下面的代码中所做的那样。另一种方法是使用int(size).to_bytes(4, 'little')size = int().from_bytes(size_bytes, 'little')

下面的代码已经完全实现了encrypt()decrypt()以及使用示例(加密2MB的随机数据切片成64 KB块)。

在网上试试!

代码语言:javascript
复制
def encrypt(key, fin, fout, *, block = 1 << 16):
    import cryptography.fernet, struct
    fernet = cryptography.fernet.Fernet(key)
    with open(fin, 'rb') as fi, open(fout, 'wb') as fo:
        while True:
            chunk = fi.read(block)
            if len(chunk) == 0:
                break
            enc = fernet.encrypt(chunk)
            fo.write(struct.pack('<I', len(enc)))
            fo.write(enc)
            if len(chunk) < block:
                break

def decrypt(key, fin, fout):
    import cryptography.fernet, struct
    fernet = cryptography.fernet.Fernet(key)
    with open(fin, 'rb') as fi, open(fout, 'wb') as fo:
        while True:
            size_data = fi.read(4)
            if len(size_data) == 0:
                break
            chunk = fi.read(struct.unpack('<I', size_data)[0])
            dec = fernet.decrypt(chunk)
            fo.write(dec)

def test():
    import cryptography.fernet, secrets
    key = cryptography.fernet.Fernet.generate_key()
    with open('data.in', 'wb') as f:
        data = secrets.token_bytes(1 << 21)
        f.write(data)
    encrypt(key, 'data.in', 'data.enc')
    decrypt(key, 'data.enc', 'data.out')
    with open('data.out', 'rb') as f:
        assert data == f.read()

if __name__ == '__main__':
    test()
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69312922

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档