Python 读取文件进行 AES 加密,相同文本出现不同结果

问题描述

计划使用 Python 读取文本文件,按每一行进行 AES 加密,再按行输出到另一个文件。结果发现,第一行加密结果与使用其它工具加密的结果不同。于是将两个加密后的结果再进行一次解密,发现二者 print 出来的字符串是一样的。但是使用 == 进行比较,发现结果为 False

下图中 aes 是封装后的一个 AES 类的实例,aes.encrypt 是加密函数,返回 bytesaes.decrypt 是解密函数,同样返回 bytesbase64_to_bytesbytes_to_base64 是 base64 编码和解码的函数。

使用 Python 的 f-string 进行格式化输出,发现第一行加密后,文本开头被添加了 \ufeff 字符。

BOM

BOM(Byte Order Mark)是一种用于标识 Unicode 编码文件的字节顺序的标记。在 Unicode 编码中,有两种字节顺序:小端序(Little Endian)和大端序(Big Endian)。

BOM 的作用是在文件开头添加一个特殊的字节序列,用于标识文件的字节顺序。这个字节序列的长度可以是 0 到 3 个字节。

BOM 的常见形式有以下几种:

字节顺序 标记 描述
小端序 FF FE 用于标识小端序编码的文件
大端序 FE FF 用于标识大端序编码的文件
UTF-8 EF BB BF 用于标识 UTF-8 编码的文件

BOM 的存在是为了告诉接收者文件的字节顺序,以便正确地解析文件内容。在 Python 中,默认情况下,文件是以当前系统的默认编码方式打开的,在中文 Windows 上会是 GBK 编码。

常见方式是指定 utf-8 编码,但是这样如果遇到 BOM,会把 BOM 头一起读入,变成开头的 \ufeff 不可见字符。

解决方法

不使用 utf-8 编码,而是使用 utf-8-sig 编码,这样就可以跳过 BOM 了。

1
2
3
# 读取文件
with open(input_file, 'r', encoding='utf-8-sig') as f:
lines = f.readlines()

注意:写出文件时,不要继续指定 utf-8-sig 编码,否则会在文件开头添加 BOM。使用 utf-8 编码即可。

附:AES 类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os
import base64

class AES:
def __init__(self):
self.key: bytes = None
self.iv: bytes = None

self.encryptor = None
self.decryptor = None
self.padder = None
self.unpadder = None

self.initialized = False

def generate_key(self):
self.key = os.urandom(32) # 256 位密钥
self.iv = os.urandom(16) # 128 位 IV
return self

def save_key_iv(self, key_file: str, iv_file: str):
with open(key_file, "wb") as f:
f.write(self.key)
with open(iv_file, "wb") as f:
f.write(self.iv)
return self

def load_key_iv(self, key_file: str, iv_file: str):
with open(key_file, "rb") as f:
self.key = f.read()
with open(iv_file, "rb") as f:
self.iv = f.read()
return self

def save_key_hex(self, key_file: str, iv_file: str):
with open(key_file, "w", encoding='utf-8-sig') as f:
f.write(self.byte_to_hex(self.key))
with open(iv_file, "w", encoding='utf-8-sig') as f:
f.write(self.byte_to_hex(self.iv))
return self

def load_key_hex(self, key_file: str, iv_file: str):
with open(key_file, "r", encoding='utf-8-sig') as f:
self.key = self.hex_to_byte(f.read())
with open(iv_file, "r", encoding='utf-8-sig') as f:
self.iv = self.hex_to_byte(f.read())
return self

def init(self):
self.cipher = Cipher(algorithms.AES256(self.key), modes.CBC(self.iv), backend=default_backend())
self.encryptor = self.cipher.encryptor()
self.decryptor = self.cipher.decryptor()
self.padder = padding.PKCS7(128).padder()
self.unpadder = padding.PKCS7(128).unpadder()
self.initialized = True
return self

def encrypt(self, data: bytes) -> bytes:
self.init()
padded_data = self.padder.update(data) + self.padder.finalize()
ciphertext = self.encryptor.update(padded_data) + self.encryptor.finalize()
return ciphertext

def decrypt(self, ciphertext: bytes) -> bytes:
self.init()
decrypted_padded_data = self.decryptor.update(ciphertext) + self.decryptor.finalize()
decrypted_data = self.unpadder.update(decrypted_padded_data) + self.unpadder.finalize()
return decrypted_data

def byte_to_base64(self, data: bytes) -> str:
return base64.b64encode(data).decode("utf-8")

def base64_to_byte(self, data: str) -> bytes:
return base64.b64decode(data)

def byte_to_hex(self, data: bytes) -> str:
return data.hex()

def hex_to_byte(self, data: str) -> bytes:
return bytes.fromhex(data)

Python 读取文件进行 AES 加密,相同文本出现不同结果
https://taylorandtony.github.io/2025/05/03/python-读取文件进行-AES-加密相同文本出现不同结果/
作者
TaylorAndTony
发布于
2025年5月3日
许可协议