Python实战:AES/DES五种加密模式原理与代码实现详解

发布时间:2026/7/21 5:22:28
Python实战:AES/DES五种加密模式原理与代码实现详解 1. 项目概述从“背”到“懂”的密码学模式学习每次看到AES、DES这些加密算法的名字再配上ECB、CBC、CFB、OFB、CTR这五种工作模式是不是感觉头都大了很多教程和教材喜欢把这些概念和流程图列出来让你背结果就是学了半天除了记住几个缩写真正动手时还是一头雾水连该用哪种模式都搞不清楚。我自己刚入门信息安全的时候也经历过这个阶段直到后来在项目中真正用代码去实现、去观察不同模式下密文的差异才恍然大悟。这个项目的核心就是彻底告别那种枯燥的死记硬背。我们不搞复杂的数学推导也不画那些让人眼花缭乱的流程图就用你最熟悉的Python一行行代码敲出来直观地“看”懂AES和DES这五种核心工作模式到底是怎么工作的以及它们之间最本质的区别。你会发现ECB模式下相同的明文块会生成相同的密文块而在CBC模式下一点点改动就会引发“雪崩效应”导致后续密文全部改变。这种视觉上的冲击比任何文字描述都来得深刻。无论你是正在学习密码学基础的学生还是需要在Web开发、数据安全、物联网设备通信中用到加密的开发者甚至是单纯对技术原理好奇的爱好者这篇文章都适合你。我们不需要高深的数学背景只需要基础的Python语法知识。我会带你从安装必要的库开始一步步实现每一种模式并通过对比实验让你真正理解为什么在某些场景下必须用CBC而不能用ECB为什么CTR模式天生就适合并行计算。最终你将获得一套可以直接复用的实战代码以及最重要的——对加密模式选择那种“了然于胸”的直觉。2. 核心概念与工具准备搭建你的密码学实验台在开始写代码之前我们必须把几个核心概念和要用的工具搞清楚。这就像做化学实验前得先认识仪器和了解安全规范一样。2.1 对称加密、分组密码与工作模式三位一体的关系首先AES和DES都属于对称加密算法意思是加密和解密用的是同一把钥匙。这把钥匙必须妥善保管一旦泄露加密就形同虚设。其次它们都是分组密码。想象一下你要加密一篇长文章算法不会一次性处理整篇文章而是像切香肠一样把它切成固定长度的小块称为“分组”或“块”然后一块一块地加密。AES的分组长度是128位16字节DES的分组长度是64位8字节。那么问题来了如果你的文章长度不是分组大小的整数倍怎么办如果每个分组都独立加密相同的明文块总会得到相同的密文块安全性岂不是很差这时候工作模式就登场了。工作模式定义了如何重复应用密码算法来安全地转换大于一个分组的的数据。它解决了三个核心问题1. 如何对任意长度的数据进行加密2. 如何让加密过程更随机、更安全避免相同明文产生相同密文3. 如何设计才能支持一些特殊功能比如并行加密或实时流加密。我们即将探讨的ECB、CBC、CFB、OFB、CTR这五种模式就是解决上述问题的五种不同“策略”或“配方”。理解它们是正确、安全使用AES/DES的关键。2.2 Python密码学库选型为什么是cryptographyPython里能做加密的库不少比如内置的hashlib主要用于哈希、ssl或者第三方库PyCryptodome。这里我强烈推荐使用cryptography库。原因有以下几点现代且安全cryptography是一个旨在提供“密码学原语”的库设计上更注重安全性和易用性社区活跃被认为是Python生态中密码学的标准选择之一。接口友好它的高级APIfernet和低级APIhazmat层次分明。对于我们的学习目的使用hazmat.primitives.ciphers模块可以让我们清晰地接触到模式、初始向量等底层概念。功能全面原生支持我们要实验的所有工作模式。安装非常简单打开你的终端或命令提示符用pip一键安装pip install cryptography注意如果你在公司的开发环境或某些受限系统中可能会遇到网络问题。请务必使用公司认可的软件源或方式安装确保所有操作符合所在环境的安全规定。安装完成后我们可以用下面这段代码快速验证一下库是否可用并认识一下核心的Cipher对象from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend import os # 生成一个随机的16字节128位密钥用于AES key os.urandom(16) # 生成一个随机的16字节初始向量IV某些模式需要 iv os.urandom(16) # 创建一个AES加密器使用CBC模式和上面生成的IV cipher Cipher(algorithms.AES(key), modes.CBC(iv), backenddefault_backend())这段代码虽然还没做任何加密但它展示了构造一个加密器的基本要素算法AES、密钥、工作模式CBC及其参数IV。这个cipher对象之后就可以用来创建加密或解密的上下文。3. 五种工作模式的原理与代码实现拆解现在让我们进入正题逐一拆解五种模式。我会为每一种模式先讲清楚它的核心工作原理用尽量形象的比喻然后给出完整的Python实现代码并展示运行结果。我们会用同一段明文在不同模式下观察其密文的差异。假设我们的明文是HelloWorld123456刚好16字节一个AES块。密钥固定为os.urandom(16)对于需要IV的模式IV固定为os.urandom(16)。这样便于对比。3.1 ECB模式最简单的电子密码本原理ECB是最直观的模式。它把明文分成独立的块然后用相同的密钥对每一块进行独立加密。就像一本密码本每个明文单词都对应一个密文单词查表即可。优点简单支持并行计算因为每块加密互不依赖。致命缺点相同的明文块总是产生相同的密文块。这意味着它不能隐藏数据模式。一张图片用ECB加密后虽然看起来是噪声但轮廓可能依然可见。因此ECB通常被认为是不安全的不应用于加密有意义的数据。代码实现from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend import os def encrypt_ecb(plaintext, key): ECB模式加密 # ECB模式不需要IV cipher Cipher(algorithms.AES(key), modes.ECB(), backenddefault_backend()) encryptor cipher.encryptor() # 加密器默认处理字节数据 ciphertext encryptor.update(plaintext) encryptor.finalize() return ciphertext def decrypt_ecb(ciphertext, key): ECB模式解密 cipher Cipher(algorithms.AES(key), modes.ECB(), backenddefault_backend()) decryptor cipher.decryptor() plaintext decryptor.update(ciphertext) decryptor.finalize() return plaintext # 测试 key os.urandom(16) plaintext bHelloWorld123456 # 16字节 print(【ECB模式演示】) print(f密钥: {key.hex()}) print(f明文: {plaintext}) ciphertext encrypt_ecb(plaintext, key) print(f密文: {ciphertext.hex()}) decrypted decrypt_ecb(ciphertext, key) print(f解密后: {decrypted}) print(- * 50)运行与观察多次运行这段代码只要密钥不变plaintext不变生成的ciphertext总是相同的。这就是ECB的特性。你可以尝试将明文改成HelloWorld123456HelloWorld123456两个相同块会发现密文的前16字节和后16字节也是一模一样的。3.2 CBC模式带反馈的链式加密原理CBC模式解决了ECB的缺陷。在加密第一个块时先将明文块与一个随机的**初始向量IV**进行异或XOR操作然后再加密。加密后的结果作为“反馈”与下一个明文块进行XOR再加密如此循环像一条链子把所有的块连接起来。优点相同的明文块在不同的位置或使用不同的IV会产生不同的密文块隐藏了数据模式。这是目前非常常用的模式。缺点加密过程是串行的无法并行。解密过程可以并行。一个比特的错误会影响当前块和下一个块的解密错误传播。代码实现def encrypt_cbc(plaintext, key, iv): CBC模式加密 cipher Cipher(algorithms.AES(key), modes.CBC(iv), backenddefault_backend()) encryptor cipher.encryptor() ciphertext encryptor.update(plaintext) encryptor.finalize() return ciphertext def decrypt_cbc(ciphertext, key, iv): CBC模式解密 cipher Cipher(algorithms.AES(key), modes.CBC(iv), backenddefault_backend()) decryptor cipher.decryptor() plaintext decryptor.update(ciphertext) decryptor.finalize() return plaintext # 测试 key os.urandom(16) iv os.urandom(16) # CBC需要IV plaintext bHelloWorld123456 print(【CBC模式演示】) print(f密钥: {key.hex()}) print(fIV : {iv.hex()}) print(f明文: {plaintext}) ciphertext encrypt_cbc(plaintext, key, iv) print(f密文: {ciphertext.hex()}) decrypted decrypt_cbc(ciphertext, key, iv) print(f解密后: {decrypted}) # 实验改变IV观察密文变化 print(\n--- 实验IV的作用 ---) iv2 os.urandom(16) ciphertext2 encrypt_cbc(plaintext, key, iv2) print(f相同密钥不同IV生成的密文: {ciphertext2.hex()}) print(f两次密文相同吗 {ciphertext ciphertext2}) print(- * 50)运行与观察你会发现即使密钥和明文完全相同只要IV不同产生的密文就完全不同。这极大地增强了安全性。IV不需要保密但必须是不可预测的通常随机生成且每次加密都应使用新的IV。3.3 CFB模式将分组密码转换为流密码原理CFB模式可以把一个分组密码如AES变成一个流密码。它先加密IV然后将加密后的结果与明文块进行XOR得到密文块。同时这个密文块会被反馈回去作为下一次加密的输入。注意这里加密的是IV/反馈值而不是明文本身。特点解密过程使用的是加密器encryptor而不是解密器。因为其核心操作是XOR。它也有错误传播特性。代码实现def encrypt_cfb(plaintext, key, iv): CFB模式加密 cipher Cipher(algorithms.AES(key), modes.CFB(iv), backenddefault_backend()) encryptor cipher.encryptor() ciphertext encryptor.update(plaintext) encryptor.finalize() return ciphertext def decrypt_cfb(ciphertext, key, iv): CFB模式解密 - 注意使用的也是encryptor cipher Cipher(algorithms.AES(key), modes.CFB(iv), backenddefault_backend()) # 解密时同样使用加密器这是CFB/OFB的特点 encryptor cipher.encryptor() plaintext encryptor.update(ciphertext) encryptor.finalize() return plaintext # 测试 key os.urandom(16) iv os.urandom(16) plaintext bHelloWorld123456 print(【CFB模式演示】) print(f密钥: {key.hex()}) print(fIV : {iv.hex()}) print(f明文: {plaintext}) ciphertext encrypt_cfb(plaintext, key, iv) print(f密文: {ciphertext.hex()}) decrypted decrypt_cfb(ciphertext, key, iv) print(f解密后: {decrypted}) print(- * 50)实操心得CFB和接下来要讲的OFB在代码实现上有一个非常容易混淆的点解密函数里我们实例化的是cipher.encryptor()而不是decryptor()。这是因为在这两种模式下解密过程本质上就是加密过程的逆运算而核心的XOR操作是对称的。如果你用了decryptor()反而会得到错误结果。这是初学者常踩的一个坑。3.4 OFB模式生成密钥流的另一种方式原理OFB模式和CFB很像也是将分组密码转换为流密码。区别在于反馈的内容。OFB是先加密IV得到一个输出块我们称之为“密钥流”用这个密钥流与明文XOR得到密文。然后将这个输出块密钥流反馈回去进行下一次加密生成下一个密钥流。注意反馈的不是密文。特点错误不会传播。传输过程中密文的一个比特错误只会影响解密后明文的对应比特不会影响其他比特。这在某些对错误零容忍的流式传输中可能有优势。代码实现def encrypt_ofb(plaintext, key, iv): OFB模式加密 cipher Cipher(algorithms.AES(key), modes.OFB(iv), backenddefault_backend()) encryptor cipher.encryptor() ciphertext encryptor.update(plaintext) encryptor.finalize() return ciphertext def decrypt_ofb(ciphertext, key, iv): OFB模式解密 - 同样使用encryptor cipher Cipher(algorithms.AES(key), modes.OFB(iv), backenddefault_backend()) encryptor cipher.encryptor() plaintext encryptor.update(ciphertext) encryptor.finalize() return plaintext # 测试 key os.urandom(16) iv os.urandom(16) plaintext bHelloWorld123456 print(【OFB模式演示】) print(f密钥: {key.hex()}) print(fIV : {iv.hex()}) print(f明文: {plaintext}) ciphertext encrypt_ofb(plaintext, key, iv) print(f密文: {ciphertext.hex()}) decrypted decrypt_ofb(ciphertext, key, iv) print(f解密后: {decrypted}) print(- * 50)3.5 CTR模式计数器的艺术原理CTR模式同样生成一个密钥流。它使用一个“计数器”块这个计数器块通常由一个随机数Nonce和一个计数部分拼接而成。加密器加密这个计数器块得到密钥流再与明文XOR。每处理一个块计数器就递增比如加1。优点1.支持并行加密和解密因为每个块的密钥流只依赖于“计数器值”可以预先计算。2. 只需要加密器不需要解密器。3. 错误不传播。缺点绝对不能让同一个密钥计数器对用来加密两个不同的明文否则会严重破坏安全性。这要求Nonce必须是唯一的。代码实现def encrypt_ctr(plaintext, key, nonce): CTR模式加密 # 在cryptography库中CTR模式需要提供一个完整的初始计数器块。 # 通常我们用一个随机nonce并指定计数器从0开始。 # 库会帮我们处理计数器的递增。这里我们简单地将nonce作为初始值。 cipher Cipher(algorithms.AES(key), modes.CTR(nonce), backenddefault_backend()) encryptor cipher.encryptor() ciphertext encryptor.update(plaintext) encryptor.finalize() return ciphertext def decrypt_ctr(ciphertext, key, nonce): CTR模式解密 - 与加密完全相同 cipher Cipher(algorithms.AES(key), modes.CTR(nonce), backenddefault_backend()) encryptor cipher.encryptor() # 解密也用encryptor plaintext encryptor.update(ciphertext) encryptor.finalize() return plaintext # 测试 key os.urandom(16) nonce os.urandom(16) # 对于AES-128CTR通常使用16字节的nonce plaintext bHelloWorld123456 print(【CTR模式演示】) print(f密钥 : {key.hex()}) print(fNonce : {nonce.hex()}) print(f明文: {plaintext}) ciphertext encrypt_ctr(plaintext, key, nonce) print(f密文: {ciphertext.hex()}) decrypted decrypt_ctr(ciphertext, key, nonce) print(f解密后: {decrypted}) print(- * 50)注意事项cryptography库的CTR模式构造器接受的参数是整个初始计数器块。在实际应用中更常见的做法是生成一个较短的随机Nonce例如8字节然后将其与一个从0开始的计数器拼接成16字节的块。库的文档和高级接口通常会处理这些细节。对于我们理解原理将其视为一个整体是没问题的。4. 模式对比与实战场景分析通过上面的代码我们已经亲手实现了五种模式。现在让我们把它们放在一起对比并讨论各自的应用场景。这是从“会用”到“懂用”的关键一步。4.1 五种模式特性对比表特性ECBCBCCFBOFBCTR是否需要IV/Nonce否是是是是加密并行性是否否否是解密并行性是是否否是错误传播无有影响当前及下一块有影响当前及后续部分无无是否将分组密码转为流密码否否是是是主要优点简单、并行安全性好、广泛使用可转为流密码、自同步错误不传播、可转为流密码并行、高效、错误不传播主要缺点/风险不安全暴露数据模式加密串行、需要填充错误传播、解密串行密钥流重复使用风险大Nonce必须唯一典型应用场景不推荐用于加密文件加密、SSL/TLS历史、数据库字段加密较少使用被CTR等取代较少使用现代协议如TLS 1.3、磁盘加密、无线通信4.2 实战场景选择指南现在假设你面临一个具体的开发任务该如何选择模式呢场景一加密一个存储在数据库里的用户手机号字段。分析数据长度固定例如11位数字需要保密。安全性要求高不能像ECB那样暴露模式。选择CBC模式是一个经典选择。你需要生成一个随机的IV并将IV和密文一起存储。解密时取出IV即可。也可以考虑CTR模式但需要确保Nonce的唯一性。场景二加密一个大型视频文件希望利用多核CPU加速。分析文件很大性能是关键。加密的并行性非常重要。选择CTR模式是首选。因为每个数据块的加密独立于其他块可以非常方便地分割文件并行加密。ECB虽然也并行但安全性太差绝对不可用。场景三在一个不可靠的网络流如UDP中加密实时语音数据。分析网络可能丢包或产生比特错误。如果错误传播导致一大段语音无法解密体验会很差。选择OFB或CTR模式。因为它们没有错误传播一个比特的错误只影响一个比特的语音人耳可能察觉不到。相比之下CBC或CFB模式下一个错误可能导致后续一段数据全部乱掉。场景四与一个遗留系统通信它只支持某种特定模式。分析没得选必须兼容。例如一些老的金融系统或硬件设备可能指定使用CBC模式。选择遵循系统规范。但务必确保IV的随机性和唯一性并正确实现填充方案如PKCS#7。实操心得在实际项目中永远不要使用ECB模式来加密任何有价值的数据。对于新系统CTR模式因其并行性和无错误传播的特性正变得越来越流行尤其是在TLS 1.3中GCM模式基于CTR已成为标准。而CBC模式虽然经典但需要特别注意防范“填充预言攻击”现代库通常会帮你处理这些问题但自己实现时要格外小心。5. 进阶实战封装一个安全的加密工具类理解了原理我们最后来点实用的封装一个简单的、更安全的加密工具类。这个类将采用目前推荐的做法使用AES-CTR模式并处理好Nonce的生成和关联存储。import os import base64 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding class SimpleAESCipher: 一个简单的AES加密工具类使用CTR模式。 注意此示例用于教学生产环境请使用更完善的安全库如cryptography的fernet。 def __init__(self, key: bytes None): 初始化加密器。 :param key: 密钥必须是16AES-128、24AES-192或32AES-256字节。 如果为None则生成一个随机的32字节密钥AES-256。 if key is None: self.key os.urandom(32) # 默认使用AES-256 else: if len(key) not in (16, 24, 32): raise ValueError(密钥长度必须为16、24或32字节) self.key key def encrypt(self, plaintext: bytes) - bytes: 加密明文。 :param plaintext: 明文字节串。 :return: 拼接了Nonce的密文Nonce 密文。Nonce固定为16字节。 # 生成一个随机的16字节Nonce nonce os.urandom(16) # 创建CTR模式的加密器 cipher Cipher(algorithms.AES(self.key), modes.CTR(nonce), backenddefault_backend()) encryptor cipher.encryptor() ciphertext encryptor.update(plaintext) encryptor.finalize() # 将Nonce和密文拼接在一起返回。解密时需要先分离。 return nonce ciphertext def decrypt(self, ciphertext_with_nonce: bytes) - bytes: 解密密文。 :param ciphertext_with_nonce: 由encrypt方法返回的字节串Nonce密文。 :return: 解密后的明文。 # 分离Nonce和密文 nonce ciphertext_with_nonce[:16] actual_ciphertext ciphertext_with_nonce[16:] # 创建CTR模式的加密器解密操作相同 cipher Cipher(algorithms.AES(self.key), modes.CTR(nonce), backenddefault_backend()) encryptor cipher.encryptor() plaintext encryptor.update(actual_ciphertext) encryptor.finalize() return plaintext def encrypt_to_base64(self, plaintext: bytes) - str: 加密并将结果转换为Base64字符串便于存储或传输。 ciphertext self.encrypt(plaintext) return base64.urlsafe_b64encode(ciphertext).decode(utf-8) def decrypt_from_base64(self, base64_ciphertext: str) - bytes: 从Base64字符串解密。 ciphertext_with_nonce base64.urlsafe_b64decode(base64_ciphertext) return self.decrypt(ciphertext_with_nonce) # 使用示例 if __name__ __main__: # 1. 使用随机密钥创建加密器 cipher SimpleAESCipher() print(f生成的随机密钥Hex: {cipher.key.hex()}) print(请妥善保存此密钥) # 2. 加密一段消息 secret_message bThis is a top secret message! encrypted_data cipher.encrypt(secret_message) print(f\n加密后的数据Hex: {encrypted_data.hex()}) print(f 前16字节是Nonce: {encrypted_data[:16].hex()}) print(f 后面是密文: {encrypted_data[16:].hex()}) # 3. 解密 decrypted_message cipher.decrypt(encrypted_data) print(f\n解密后的消息: {decrypted_message.decode(utf-8)}) # 4. 使用Base64接口 base64_str cipher.encrypt_to_base64(secret_message) print(f\nBase64编码的加密结果: {base64_str}) decrypted_from_b64 cipher.decrypt_from_base64(base64_str) print(f从Base64解密: {decrypted_from_b64.decode(utf-8)})这个工具类的关键点默认使用AES-256-CTR提供了较好的安全性和性能。Nonce管理每次加密随机生成Nonce并将其与密文捆绑在一起。这是处理Nonce的常见模式确保了解密方能够获取到它。Base64编码提供了转换为文本格式的接口方便存入数据库或JSON。密钥管理示例中密钥是随机生成并打印出来的。在实际应用中密钥必须通过安全的方式存储和管理例如使用密钥管理服务KMS或环境变量绝不能硬编码在代码中。重要警告这个SimpleAESCipher类是为了教学和演示目的而简化的。它缺少完整性验证MAC。在真实世界中仅保密性加密是不够的还必须防止攻击者篡改密文。因此现代应用应使用认证加密模式如AES-GCMGalois/Counter Mode它同时提供保密性和完整性。cryptography库的fernet模块或AES-GCM模式就是为此而设计的。在你自己的项目中如果安全要求高请直接使用这些更安全的构造块。6. 常见问题与排查技巧实录即使理解了原理在实际编码和调试中还是会遇到各种问题。下面是我总结的一些常见坑点和解决方法。6.1 “Invalid key length” 或 “Invalid IV length” 错误问题描述初始化Cipher对象时程序抛出类似ValueError: Invalid key size或Invalid IV size的异常。原因分析密钥长度AES标准密钥长度是128、192或256位即16、24、32字节。DES密钥是64位8字节但通常使用56位有效密钥8位奇偶校验。你提供的密钥字节数不对。IV长度对于CBC、CFB、OFB模式IV长度必须等于分组大小。AES是16字节DES是8字节。CTR模式的“nonce”长度通常也要求是16字节AES或8字节DES具体看库的实现。解决方案检查生成密钥和IV的代码。使用os.urandom(16)生成AES-128的密钥和IV。如果是读取的密钥文件或配置确认编码。mykey这样的字符串是5字节需要编码成字节串并确保长度正确例如key “my_32_byte_key_1234567890123456”.encode(‘utf-8’)。使用len(key)和len(iv)打印出来确认。6.2 解密时出现Invalid padding bytes或乱码问题描述加密过程正常但解密时要么直接报填充错误要么解出来的明文是乱码。原因分析分层排查密钥/IV不匹配这是最常见的原因。加密用的密钥和IV与解密时用的必须完全一样。确保它们被正确传递和存储。模式不匹配用CBC模式加密却试图用ECB模式解密。填充问题CBC模式常见CBC模式要求明文长度是分组的整数倍不足需要填充。cryptography库默认使用PKCS#7填充。如果你在加密时手动处理了填充或者数据来源本身有特殊结构解密时库可能会认为填充格式不对。数据损坏密文在传输或存储过程中被修改了一个字节。排查步骤打印并比对在加密和解密函数的最开始打印出传入的key和iv或nonce的十六进制字符串确保它们完全相同。检查模式确认代码中modes.CBC、modes.CTR等构造器使用正确。处理填充如果明文长度不确定让库自动处理填充是最稳妥的。如果你必须自己处理确保使用标准的PKCS#7填充并在解密后正确移除填充。验证数据完整性对于重要数据考虑使用认证加密模式如GCM它可以同时检测密文是否被篡改。6.3 CFB/OFB/CTR模式解密时用了decryptor()导致错误问题描述使用CFB、OFB或CTR模式时解密函数里错误地创建了decryptor对象导致解密失败。原因与解决牢记一个口诀“CFB、OFB、CTR解密也用加密器”。这是因为这些模式的核心是对密钥流进行XOR操作而加密和解密时的XOR操作是完全相同的。将decryptor cipher.decryptor()改为encryptor cipher.encryptor()即可。6.4 如何加密一个长度不是分组整数倍的字符串问题比如要加密Hello这个5字节的字符串而AES分组是16字节。解决方案使用填充CBC等模式库通常会自动处理。例如在创建Cipher对象时库内部会应用PKCS#7填充将Hello填充到16字节再加密。解密后会自动去掉填充。使用流密码模式CFB、OFB、CTR这些模式本质上是流密码可以逐字节或逐位加密不需要填充。你可以直接加密5字节的明文得到5字节的密文。代码示例CBC自动填充from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend import os key os.urandom(16) iv os.urandom(16) plaintext bHello # 只有5字节 cipher Cipher(algorithms.AES(key), modes.CBC(iv), backenddefault_backend()) encryptor cipher.encryptor() # 库会自动填充 ciphertext encryptor.update(plaintext) encryptor.finalize() print(f密文长度: {len(ciphertext)}) # 输出将是16 decryptor cipher.decryptor() decrypted decryptor.update(ciphertext) decryptor.finalize() print(f解密后: {decrypted}) # 输出 bHello6.5 性能考虑哪种模式最快理论ECB和CTR由于支持并行加密在多核环境下对大数据的加密速度有优势。CBC加密过程是串行的速度可能成为瓶颈。实践对于大多数应用场景在通用CPU上不同模式的速度差异可能并不明显除非你处理的是GB级别以上的数据。安全性永远是第一考量因素。在满足安全需求的前提下CTR模式通常是兼顾性能和安全的良好选择。一个简单的性能测试思路import time data os.urandom(10 * 1024 * 1024) # 10MB 随机数据 key os.urandom(16) iv os.urandom(16) start time.time() # ... 使用不同模式加密 data ... end time.time() print(f耗时: {end - start:.2f}秒)你可以用这个框架去比较不同模式但记住结果会受硬件、Python版本、库版本等因素影响。走完这一趟从原理到代码再从代码到实战的旅程你应该不再需要去死记硬背那些模式的流程图和特性表格了。真正的理解来自于动手实践和观察。下次当你在代码中需要选择加密模式时希望你的第一反应是去回想我们在这里做过的对比实验ECB那重复的密文块、CBC改变IV带来的雪崩效应、以及CTR的并行便利性。这才是属于你自己的、不会被轻易忘记的知识。最后记住那个最重要的安全准则在新项目中优先考虑使用像AES-GCM这样的认证加密模式它能为你省去很多手动验证完整性的麻烦。