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
相关产品推荐
相关产品推荐

