尧图精选

ZeroNet 内置纯 Python AES 加密库 pyaes 实战指南:五种工作模式、BlockFeeder 流式加密与密钥派生全解析

🕒 发布时间:2026/10/1 21:11:04 📁 来源:尧图网络
网络后端【免费下载链接】ZeroNetZeroNet - Decentralized websites using Bitcoin crypto and BitTorrent network项目地址https://gitcode.com/gh_mirrors/ze/ZeroNet点击查看免费下载本文以 ZeroNet 仓库中随附的 pyaes 库位于 src/lib/pyaes/为对象系统讲解这套无第三方依赖的纯 Python AES 实现包括 128/192/256 位密钥的块密码核心、CBC/CFB/CTR/ECB/OFB 五种常用工作模式、面向流式数据的 BlockFeeder 与 Stream Feeder API以及从口令派生密钥的三种典型方案。读完本文你将能够在自己的 Python 项目中直接复刻 pyaes 的加解密流程并理解它在 ZeroNet 的 sslcrypto 回退后端中承担的角色。一、pyaes 是什么零依赖的纯 Python AES 实现pyaes 是 AES 块密码算法及其常见工作模式CBC、CFB、CTR、ECB、OFB的纯 Python 实现。与依赖 C 扩展的 PyCrypto 等库不同pyaes 不依赖任何外部模块因此非常适合在受限环境无编译器、无 OpenSSL 绑定中作为加密回退方案。在本仓库中pyaes 位于 src/lib/pyaes/由四个源文件组成文件职责aes.pyAES 块密码核心S 盒、密钥扩展、轮变换及五种模式类、Counter 计数器类blockfeeder.pyEncrypter / Decrypter / encrypt_stream / decrypt_stream 流式封装util.pyPKCS#7 填充、字节缓冲兼容层Python 2/3 差异处理init.py公共 API 导出版本号 VERSION [1, 3, 0]其导出接口集中在init.py块密码AES、五个模式类AESModeOfOperationCTR / CBC / CFB / ECB / OFB、计数器Counter、流式封装Encrypter / Decrypter / encrypt_stream / decrypt_stream以及两个填充常量PADDING_NONE / PADDING_DEFAULT。pyaes 的核心特性支持全部 AES 密钥长度128 位 / 192 位 / 256 位支持全部常见工作模式CBC、CFB、CTR、ECB、OFB纯 Python 实现无外部依赖提供 BlockFeeder API可轻松对任意长度的流进行加解密同时支持 Python 2.x 与 3.xPython 3 下必须传入bytes而非str。二、密钥与初始化向量API 的起点2.1 密钥长度规则所有密钥只能是三种长度之一128 位16 字节、192 位24 字节或 256 位32 字节。这个约束在 aes.py 的AES.__init__中强制校验非法的密钥长度会抛出ValueError(Invalid key size)。2.2 生成随机密钥直接使用系统安全随机源生成密钥import os # 128 bit, 192 bit and 256 bit keys key_128 os.urandom(16) key_192 os.urandom(24) key_256 os.urandom(32)2.3 从口令派生密钥如果需要从易记的口令生成密钥推荐使用基于口令的密钥派生函数PBKDFPassword-Based Key Derivation Function例如 scrypt。本仓库的 sslcrypto 回退后端在生成随机 IV 时也使用了 os.urandom 这一来源。具体派生方案详见本文第七节。三、五种工作模式详解工作模式各有取舍。总体而言CBC 与 CTR 是被推荐的模式ECB 不被推荐仅作为完整性测试而保留。以下各例均假设使用同一组演示密钥仅用于演示切勿用于生产import pyaes # A 256 bit (32 byte) key key This_key_for_demo_purposes_only! # For some modes of operation we need a random initialization vector # of 16 bytes iv InitializationVe3.1 CTR 计数器模式推荐CTR 模式将块密码转换为流密码对递增的计数器值加密后与明文异或因此明文可以是任意长度无需填充。这也是 ZeroNet 的 sslcrypto 回退后端默认采用的标准之一默认算法aes-256-cbc同时支持 ctr。aes pyaes.AESModeOfOperationCTR(key) plaintext Text may be any length you wish, no padding is required ciphertext aes.encrypt(plaintext) # \xb6\x99\x10\xa4\x96\x88\xd1\x89\x1co\xe6\x1d\xef;\x11\x03\xe3\xee # \xa9V?wY\xbfe\xcdO\xe3\xdf\x9dV\x19\xe5\x8dk\x9fh\xb87\xdb\xa3\xd6 # \x86\xf4\xbd\xb0\x97\xf1\t\x02\xe9 \xed print(repr(ciphertext)) # 注意CTR 模式内部维护状态解密必须新建实例 aes pyaes.AESModeOfOperationCTR(key) decrypted aes.decrypt(ciphertext) # True print(decrypted plaintext)使用自定义初始值CTR 默认初始计数为 1aes.py 中Counter.__init__的initial_value 1。可以传入pyaes.Counter指定初始值counter pyaes.Counter(initial_value 100) aes pyaes.AESModeOfOperationCTR(key, counter counter) ciphertext aes.encrypt(plaintext) # WZ\x844\x02\xbfoY\x1f\x12\xa6\xce\x03\x82Ei)\xf6\x97mX\x86\xe3\x9d # _1\xdd\xbd\x87\xb5\xccEM_4\x01$\xa6\x81\x0b\xd5\x04\xd7Al\x07\xe5 # \xb2\x0e\\\x0f\x00\x13,\x07 print(repr(ciphertext))从源码看Counter类将初始值展开为 16 字节的计数器数组并提供increment()方法aes.py从低位向高位逐字节递增溢出回卷为 0。想要自定义计数行为时通常只需覆写increment方法。3.2 CBC 密码块链接模式推荐CBC 属于块模式明文长度必须是 16 字节的整数倍配合 BlockFeeder 时会自动做 PKCS#7 填充。它需要一个 16 字节的随机初始化向量iv。aes pyaes.AESModeOfOperationCBC(key, iv iv) plaintext TextMustBe16Byte ciphertext aes.encrypt(plaintext) # \xd6:\x18\xe6\xb1\xb3\xc3\xdc\x87\xdf\xa7|\x08{k\xb6 print(repr(ciphertext)) # CBC 模式同样维护状态解密必须新建实例 aes pyaes.AESModeOfOperationCBC(key, iv iv) decrypted aes.decrypt(ciphertext) # True print(decrypted plaintext)3.3 CFB 密文反馈模式CFB 是分段密码segment cipher送入模式的每一块数据都必须是段大小segment size的整数倍。例如选择 8 字节段aes pyaes.AESModeOfOperationCFB(key, iv iv, segment_size 8) plaintext TextMustBeAMultipleOfSegmentSize ciphertext aes.encrypt(plaintext) # v\xa9\xc1w\x8aL\x93\xcb\xdf\xa0/\xf8Y\x0b\x8d\x88i\xcb\x85rmp # \x85\xfe\xafM\x0c)\xd5\xeb\xaf print(repr(ciphertext)) # 解密同样需要新建实例 aes pyaes.AESModeOfOperationCFB(key, iv iv, segment_size 8) decrypted aes.decrypt(ciphertext) # True print(decrypted plaintext)注意blockfeeder.py 的 CFB 段模式封装允许在结尾处用剩余的密码块处理非段长度对齐的末块。而 ZeroNet 的 sslcrypto 回退后端为了 OpenSSL 兼容将 CFB 的segment_size设为 16 字节见 sslcrypto/fallback/aes.py而非默认值。3.4 OFB 输出反馈模式OFB 同样是流密码明文可为任意长度无需填充。它通过反复加密 IV 生成密钥流再与明文异或。aes pyaes.AESModeOfOperationOFB(key, iv iv) plaintext Text may be any length you wish, no padding is required ciphertext aes.encrypt(plaintext) # v\xa9\xc1wO\x92^\x9e\rR\x1e\xf7\xb1\xa2\x9dl1\xc7\xe7\x9d\x87(\xc26s # \xdd8\xc8\xb6\xd9!\xf5\x0cM\xaa\x9b\xc4\xedLD\xe4\xb9\xd8\xdf\x9e\xac # \xa1\xb8\xea\x0f\x8ev\xb5 print(repr(ciphertext)) # OFB 模式维护状态解密需要新建实例 aes pyaes.AESModeOfOperationOFB(key, iv iv) decrypted aes.decrypt(ciphertext) # True print(decrypted plaintext)3.5 ECB 电子密码本模式不推荐ECB 不推荐使用任意两个相同的明文块会产生完全相同的密文块会暴露数据的统计模式经典例证就是 Wikipedia 上 ECB 加密图片后仍能看出 Tux 企鹅轮廓。该模式主要是为完整性而保留。由于 ECB 不维护任何状态加解密可复用同一个对象aes pyaes.AESModeOfOperationECB(key) plaintext TextMustBe16Byte ciphertext aes.encrypt(plaintext) # L6\x95\x85\xe4\xd9\xf1\x8a\xfb\xe5\x94X\x80|\x19\xc3 print(repr(ciphertext)) # ECB 无状态无需为解密新建对象 decrypted aes.decrypt(ciphertext) # True print(decrypted plaintext)在 aes.py 的类文档中ECB 的安全性警告写得很明确任何两个相同块产生相同密文暴露数据模式。在init.py 中同样给出了五个模式的完整清单。四、BlockFeeder任意长度数据的便捷加解密大多数工作模式要求数据按块或段对齐处理任意长度的大字符串会很麻烦。BlockFeeder 类通过跨多次调用缓存字节、在可用时立即返回结果并在结束时自动填充或剥离填充解决了这个问题import pyaes # Any mode of operation can be used; for this example CBC key This_key_for_demo_purposes_only! iv InitializationVe ciphertext # 可以逐行加密无论行长如何 encrypter pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv)) for line in open(/etc/passwd): ciphertext encrypter.feed(line) # 最后必须调用一次 feed() 冲刷剩余字节并补足填充 ciphertext encrypter.feed() # 可以分块解密这里拆成两半 decrypter pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv)) decrypted decrypter.feed(ciphertext[:len(ciphertext) / 2]) decrypted decrypter.feed(ciphertext[len(ciphertext) / 2:]) # 同样需要最后一次 feed() 冲刷并剥离填充 decrypted decrypter.feed() print(open(/etc/passwd).read() decrypted)从 blockfeeder.py 的实现可以看到内部机制feed(data)会先把新数据并入内部缓冲然后仅在缓冲超过 16 字节时才向底层模式投喂可消费的块并始终保留 16 字节用于确定末尾填充feed()即feed(None)触发最终处理对块模式追加 PKCS#7 填充或剥离随后将内部缓冲置为None之后再调用会抛出ValueError(already finished feeder)每个模式类通过注入_can_consume/_final_encrypt/_final_decrypt三个方法blockfeeder.py来适配自身特性块模式一次消费 16 字节段模式按段大小消费流模式则有多少消费多少。填充行为由padding参数控制默认PADDING_DEFAULT使用 PKCS#7见 util.py 的append_PKCS7_padding/strip_PKCS7_padding也可显式指定PADDING_NONE。注意 util.py 中的to_bufferable兼容层Python 3 下会自动将str转为bytes这正是 README 强调Python 3 请传bytes的原因。五、Stream Feeder整流文件加解密如果目标是加密/解密流式数据或大文件Stream Feeder 更进一步将读写过程封装为一个函数调用import pyaes # Any mode of operation can be used; for this example CTR key This_key_for_demo_purposes_only! # 创建用于加密的工作模式 mode pyaes.AESModeOfOperationCTR(key) # 输入与输出文件 file_in open(/etc/passwd) file_out open(/tmp/encrypted.bin, wb) # 以流方式加密文件默认按 8kb 分块读取 pyaes.encrypt_stream(mode, file_in, file_out) # 关闭文件 file_in.close() file_out.close()解密方式完全一致只需将pyaes.encrypt_stream换成pyaes.decrypt_stream并把加密文件作为file_in、解密目标作为file_out。实现层面blockfeeder.py 定义了BLOCK_SIZE (1 13)即 8192 字节 8kb的默认分块大小_feed_stream循环读取、投喂并写出最后调用一次feed()冲刷。encrypt_stream与decrypt_stream均支持通过block_size与padding参数自定义分块大小和填充策略。六、底层 AES 块密码一般场景应优先使用上述工作模式。但如果你要试验自定义模式或需要直接处理单个加密块可以用AES类。块密码一次只能加密/解密恰好一个 16 字节块且输入必须是以整数表示字节的列表/数组import pyaes # 16 byte block of plain text plaintext Hello World!!!!! plaintext_bytes [ ord(c) for c in plaintext ] # 32 byte key (256 bit) key This_key_for_demo_purposes_only! # AES 实例 aes pyaes.AES(key) # 加密 ciphertext aes.encrypt(plaintext_bytes) # [55, 250, 182, 25, 185, 208, 186, 95, 206, 115, 50, 115, 108, 58, 174, 115] print(repr(ciphertext)) # 解密 decrypted aes.decrypt(ciphertext) # True print(decrypted plaintext_bytes)该实现基于 RijndaelFIPS-197算法aes.py核心特征包括轮数随密钥长度变化number_of_rounds {16: 10, 24: 12, 32: 14}aes.py即 128 位密钥 10 轮、192 位 12 轮、256 位 14 轮内置 S 盒与逆 S 盒S/Si、加密用查找表T1-T4、解密用查找表T5-T8以及解密密钥扩展表U1-U4将 SubBytes、ShiftRows、MixColumns 等轮变换合并为查表操作以提升纯 Python 性能密钥扩展按 FIPS-197 第 5.2 节实现其中256 位密钥的扩展逻辑略有不同aes.pyencrypt/decrypt均校验块长度非 16 字节会抛出ValueError(wrong block length)aes.py。七、密钥到底是什么口令到密钥的三种转换方案很多加密新手会混淆密钥与口令。可以把密钥理解为*密码*但 AES 要求该密码必须是特定长度——16、24 或 32 字节。当你创建 AES 对象时密钥长度会被自动检测这也是ValueError(Invalid key size)的由来因此必须传入长度正确的密钥。而用户往往希望使用任意长度、便于记忆的口令。此时就需要用PBKDF基于口令的密钥派生函数将口令转换为指定长度的密钥。下面是三种典型做法7.1 使用 crypt PBKDF源自 python-pbkdf2import pbkdf2 password HelloWorld # crypt PBKDF 返回 48 字节串 key pbkdf2.crypt(password) # 分别截取 16 字节、24 字节、32 字节的密钥 key_16 key[:16] key_24 key[:24] key_32 key[:32]7.2 使用 scrypt有意设计为慢速抗暴力破解scrypt 的慢是刻意的通过增加计算与内存成本让暴力猜测口令变得困难。import pyscrypt password HelloWorld # salt盐必不可少用于抵御彩虹表攻击 salt SeaSalt # N、r、p 是控制派生难度的参数数值越大越耗时、越占内存 N 1024 r 1 p 1 # 分别派生 16、24、32 字节密钥scrypt 的第 6 个参数表示密钥长度 key_16 pyscrypt.hash(password, salt, N, r, p, 16) key_24 pyscrypt.hash(password, salt, N, r, p, 24) key_32 pyscrypt.hash(password, salt, N, r, p, 32)7.3 使用哈希函数如 SHA256——注意彩虹表风险用 SHA256 直接哈希口令同样可行但除非配合盐salt使用否则可能遭受彩虹表攻击import hashlib password HelloWorld # SHA256 哈希算法返回 32 字节串 hashed hashlib.sha256(password).digest() # 分别截取 16 字节、24 字节、32 字节的密钥 key_16 hashed[:16] key_24 hashed[:24] key_32 hashed八、性能说明README 明确记录了基于其自带回归测试原库位于tests/test-aes.py主要用途是回归而非基准的性能对比数据摘录如下供选型参考CPython 下CBC、ECB、OFB 模式约比 PyCrypto 慢 30 倍CFB 约慢 80 倍CTR 约慢 300 倍PyPy 下CBC、ECB、OFB 约慢 4 倍CFB 约慢 12 倍CTR 约慢 19 倍。README 还指出PyCrypto 文档中提到计数器回调是 CTR 模式性能瓶颈的根源因此 PyCrypto 使用了专门优化的计数器pyaes 作者表示会继续研究该问题。这套数据意味着在性能敏感的生产场景优先选用带原生加速的库如 PyCrypto/OpenSSLpyaes 的价值在于零依赖、跨平台、可移植的回退能力——这正是它被 ZeroNet 选作 sslcrypto 回退后端的原因。九、在 ZeroNet 中的实际落地sslcrypto 回退后端pyaes 在 ZeroNet 中的真实角色是sslcrypto 的纯 Python 回退后端fallback backend。当 OpenSSL 后端不可用时src/lib/sslcrypto/fallback/aes.py 直接import pyaes并基于它实现 AES 加解密算法名解析支持aes-128/192/256-cbc|ctr|cfb|ofb共 12 种组合sslcrypto/fallback/aes.pyCBC直接使用pyaes.AESModeOfOperationCBC(key, iviv)CTR注释中明确警告pyaes always uses 1 as initial counter因此将随机生成的 16 字节 IV 先转换为整数再构造pyaes.Counter(iv_int)传入以保证与 OpenSSL 语义一致CFB将segment_size从 pyaes 默认值改为 16 字节以兼容 OpenSSL加密时通过os.urandom(16)生成随机 IV并将(ciphertext, iv)一并返回给调用方sslcrypto/fallback/aes.py。再向上游看plugins/CryptMessage/CryptMessagePlugin.py 中的消息加密即通过sslcrypto.aes.encrypt / decrypt完成CryptMessagePlugin.py形成了CryptMessage → sslcrypto → pyaes的完整调用链。换言之在缺少 OpenSSL 加速的环境里ZeroNet 的私密消息加密正是依赖本文介绍的这套 pyaes 实现兜底运转。十、FAQ为什么会有 pyaespyaes 的诞生源于作者自己的 pyscrypt 库当时需要一个支持256 位密钥 CTR 模式的纯 Python AES 实现。搜索后发现已有实现要么缺失 CTR要么仅支持 128 位密钥。在完整学习 AES 并完成实现后作者顺手把通用解决方案整理成了一个库——于是便有了 pyaes。这也解释了为什么 CTR 模式在该库中拥有开箱即用的完整支持。总结pyaes 是一个麻雀虽小五脏俱全的 AES 纯 Python 实现底层 aes.py 提供完整的密钥扩展与轮变换五个模式类覆盖了全部常用工作模式blockfeeder.py 则把任意长度的数据、流与文件加解密变成几行代码的事。在 ZeroNet 中它是 sslcrypto 回退后端的加密基石保证了无 OpenSSL 环境下的可用性。实践要点回顾CTR/OFB 免填充、CBC/ECB 需 16 字节对齐、CTR/CBC/CFB/OFB 均需新建实例解密、ECB 因模式泄露而不可用于生产、Python 3 务必传入bytes。赞分享网络后端【免费下载链接】ZeroNetZeroNet - Decentralized websites using Bitcoin crypto and BitTorrent network项目地址https://gitcode.com/gh_mirrors/ze/ZeroNet点击查看免费下载相关推荐Cosmos 仓库中的纯 C AES 实现AES-128/192/256 加解密与流加密实战指南Cosmos 仓库中的纯 C AES 实现AES 128/192/256 加解密与流加密实战指南 本篇指南以 Cosmos 仓库 code/cryptogra教程示例工程CodeIgniter 3 加密库Encryption Library完全指南AES-128-CBC、HKDF 密钥派生与 HMAC 认证实战CodeIgniter 3 加密库Encryption Library完全指南AES 128 CBC、HKDF 密钥派生与 HMAC 认证实战 CodeI后端Web框架使用 C StreamCipher 对数据流进行 AES 加密与解密Cosmos 仓库流式加密实战指南使用 C StreamCipher 对数据流进行 AES 加密与解密Cosmos 仓库流式加密实战指南 本篇技术指南以 Cosmos 开源仓库中 AES C教程示例工程上一篇Dance 基础教程animate 四种动画曲线easeInOut、Spring、贝塞尔一文掌握下一篇浏览器适配难题终结者gh_mirrors/fr/frontend-stuff兼容性工具使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →