You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python cryptography库加密数据追加写入文件及解密读取问题求助

问题与解决方案

问题描述

使用Python的cryptography库实现加密字符串追加写入文件,并能解密读取原始内容。当前使用Fernet的示例代码运行时抛出InvalidToken错误,核心需求不受加密模式限制,仅需支持追加写入与正确解密。

错误原因

示例代码中读取行时的line = line[:-2]是错误操作:

  • readlines()读取的行末尾换行符在不同系统中长度不同:Windows是\r\n(2个字符),Linux/macOS是\n(1个字符)
  • 硬截断最后2个字符会破坏Fernet Token的完整性(Fernet Token是URL安全Base64编码,长度固定且要求严格格式),导致Base64解码时出现Incorrect padding,最终触发InvalidToken异常

修复方案

将行处理逻辑改为移除换行符而非硬截断,使用line.rstrip('\n')或line.strip()(若加密内容不含换行符,strip()更安全,可同时去掉首尾空白和换行)。

修正后的代码

from cryptography import fernet

# 注意:实际使用中请确保密钥安全存储,不要硬编码
key = 'WqSAOfEoOdSP0c6i1CiyoOpTH2Gma3ff_G3BpDx52sE='
crypt_obj = fernet.Fernet(key)

# 追加加密内容
with open('test.txt', 'a') as f:
    data = 'Hello1'
    encrypted = crypt_obj.encrypt(data.encode())
    f.write(encrypted.decode() + '\n')

with open('test.txt', 'a') as f:
    data_two = 'Hello2'
    encrypted_two = crypt_obj.encrypt(data_two.encode())
    f.write(encrypted_two.decode() + '\n')

# 解密读取
with open('test.txt', 'r') as f:
    for line in f:
        # 移除末尾换行符,保留完整Token
        cleaned_line = line.rstrip('\n')
        # 跳过空行(如果文件末尾有空白行)
        if not cleaned_line:
            continue
        decrypted = crypt_obj.decrypt(cleaned_line.encode()).decode()
        print(decrypted)

可选优化:使用hazmat API实现轻量加密(减少元数据)

如果需要更少的元数据,可以直接使用cryptography的hazmat层API(以AES-GCM为例,安全且元数据少),实现追加写入与读取:

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

# 生成256位AES密钥(实际使用中请保存密钥,不要每次生成)
key = os.urandom(32)

def encrypt_data(key, plaintext):
    # AES-GCM需要随机nonce(12字节推荐)
    nonce = os.urandom(12)
    cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plaintext.encode()) + encryptor.finalize()
    # 保存nonce + 密文 + 标签(GCM需要标签验证完整性)
    return nonce + ciphertext + encryptor.tag

def decrypt_data(key, encrypted_data):
    nonce = encrypted_data[:12]
    tag = encrypted_data[-16:]
    ciphertext = encrypted_data[12:-16]
    cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag), backend=default_backend())
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()

# 追加写入:将加密后的数据转为十六进制字符串(避免二进制写入的换行问题)
with open('test_hazmat.txt', 'a') as f:
    encrypted = encrypt_data(key, 'Hello1')
    f.write(encrypted.hex() + '\n')
    encrypted_two = encrypt_data(key, 'Hello2')
    f.write(encrypted_two.hex() + '\n')

# 解密读取
with open('test_hazmat.txt', 'r') as f:
    for line in f:
        cleaned_line = line.rstrip('\n')
        if not cleaned_line:
            continue
        encrypted_data = bytes.fromhex(cleaned_line)
        decrypted = decrypt_data(key, encrypted_data).decode()
        print(decrypted)
  • 说明:AES-GCM的元数据仅包含12字节nonce和16字节标签,比Fernet的元数据更少;使用十六进制字符串写入,避免二进制数据与换行符冲突。

内容的提问来源于stack exchange,提问作者Leo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 09:33:20