☰
cryptography 异常体系详解:UnsupportedAlgorithm、AlreadyFinalized 等内置异常的使用与源码剖析
2026/9/27 21:22:58 网站建设 项目流程
  • 密码学

【免费下载链接】cryptography

cryptography is a package designed to expose cryptographic primitives and recipes to Python developers.

项目地址:https://gitcode.com/gh_mirrors/cr/cryptography
点击查看免费下载

cryptography 是面向 Python 开发者的密码学工具包,在其 docs/exceptions.rst 中系统定义了 6 个核心异常类型。本文将逐一讲解这些异常的触发场景、正确的捕获与处理方式,并结合 src/cryptography/exceptions.py 的源码实现与仓库测试用例(如 test_cmac.py、test_argon2.py)深入剖析其底层原理,帮助你在实际项目中写出健壮的密码学错误处理代码。

异常模块总览:cryptography.exceptions 的定位

所有密码学相关异常统一收敛在cryptography.exceptions模块中,由文档docs/exceptions.rst正式定义。在源码层面,这些类定义于 src/cryptography/exceptions.py,全部直接或间接继承自 Python 内置的Exception:

异常类继承关系一句话语义
UnsupportedAlgorithmException请求的算法(或算法组合)不受支持
AlreadyFinalizedException上下文(context)在 finalize 之后仍被继续使用
InvalidSignatureException签名/消息认证码校验失败
NotYetFinalizedException在 finalize 之前就访问了 AEAD 的 tag 属性
AlreadyUpdatedException已调用过 update 之后又向上下文追加附加数据
InvalidKeyExceptionKDF 验证方法计算出的密钥与期望密钥不匹配

除了文档列出的这 6 类,exceptions.py 还定义了InvalidTag(AEAD 解密时 tag 校验失败)与InternalError(携带 OpenSSL 错误码的底层错误)两个补充类型,它们在文档未列出的场景中同样会从密码学操作中抛出。

值得关注的是,UnsupportedAlgorithm与InternalError是仅有的两个携带额外属性的异常:

  • UnsupportedAlgorithm构造函数接受message与可选的reason(_Reasons | None),并在实例上保存self._reason;
  • InternalError保存err_code属性,其中包含来自 Rust 侧 OpenSSL 绑定的错误列表list[rust_openssl.OpenSSLError]。

理解UnsupportedAlgorithm:算法与运行环境不匹配的信号

UnsupportedAlgorithm是所有异常中出现频率最高、覆盖范围最广的一类,它表示"当前请求的算法或算法组合不受支持"。需要强调的是,它不代表你的代码写错了(这类问题通常抛TypeError/ValueError),而是代表运行时环境(尤其是底层 OpenSSL 后端)无法满足算法需求。

从 Rust 侧看 reason 枚举

在 src/cryptography/exceptions.py 中,_Reasons直接复用自 Rust 侧实现:

_Reasons = rust_exceptions._Reasons

该枚举定义在 src/rust/src/exceptions.rs,共包含 12 个成员,可据此精确诊断"哪种能力缺失":

reason 值含义
BACKEND_MISSING_INTERFACE后端缺少所需接口
UNSUPPORTED_HASH哈希算法不受支持
UNSUPPORTED_CIPHER分组密码算法不受支持
UNSUPPORTED_PADDING填充方案不受支持
UNSUPPORTED_MGFMGF(掩码生成函数)不受支持
UNSUPPORTED_PUBLIC_KEY_ALGORITHM公钥算法不受支持
UNSUPPORTED_ELLIPTIC_CURVE椭圆曲线不受支持
UNSUPPORTED_SERIALIZATION序列化格式不受支持
UNSUPPORTED_X509X.509 功能不受支持
UNSUPPORTED_EXCHANGE_ALGORITHM密钥交换算法不受支持
UNSUPPORTED_DIFFIE_HELLMANDiffie-Hellman 参数不受支持
UNSUPPORTED_MACMAC 算法不受支持

从 src/rust/src/exceptions.rs 可以看到,Rust 绑定通过pyo3::import_exception!直接引用 Python 侧的异常类,确保底层 Rust 实现抛出的错误能原样映射回cryptography.exceptions,保持异常类型的统一。

典型触发路径:模式与算法类型不匹配

以分组密码模式校验为例,src/cryptography/hazmat/primitives/_modes.py 中的_check_nonce_length明确展示了触发逻辑:

def _check_nonce_length(nonce, name, algorithm): if not isinstance(algorithm, BlockCipherAlgorithm): raise UnsupportedAlgorithm( f"{name} requires a block cipher algorithm", _Reasons.UNSUPPORTED_CIPHER, ) if len(nonce) * 8 != algorithm.block_size: raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.")

同理,modes.py 以及椭圆曲线、Ed448、ML-DSA、ML-KEM、X25519/X448 等模块(参见 ec.py、mldsa.py、mlkem.py)都会在算法不受后端支持时抛出UnsupportedAlgorithm。

实战捕获建议

由于该异常可能在任何初始化算法对象、或调用涉及后端能力的方法时抛出,官方推荐在代码中显式捕获并降级处理:

from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.primitives import hashes try: digest = hashes.Hash(hashes.SHA3_512()) except UnsupportedAlgorithm: print("当前 OpenSSL 后端不支持 SHA3-512,请升级或更换后端。")

测试中同样如此——test_cmac.py 通过raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER)精确断言了"使用非分组密码算法构造 CMAC"应抛出的异常与 reason。

掌握两个"状态机"异常:AlreadyFinalized 与 AlreadyUpdated

密码学上下文对象(如哈希、CMAC、HMAC、AEAD 加解密器)普遍遵循"可更新 → 终结 → 只读"的状态机。AlreadyFinalized与AlreadyUpdated正是状态机违反时的哨兵。

AlreadyFinalized:finalize 之后的任何使用都是非法操作

文档定义:当上下文在终结(finalized)之后仍被使用时抛出。例如对已经finalize()的对象再次调用update()、finalize()、verify()或copy()。

测试 test_cmac.py 完整展示了这一场景:

def test_raises_after_finalize(self): key = b"2b7e151628aed2a6abf7158809cf4f3c" cmac = CMAC(AES(key)) cmac.finalize() with pytest.raises(AlreadyFinalized): cmac.update(b"foo") with pytest.raises(AlreadyFinalized): cmac.copy() with pytest.raises(AlreadyFinalized): cmac.finalize() with pytest.raises(AlreadyFinalized): cmac.verify(b"")

同样的断言也遍布 test_aes.py、test_chacha20.py、test_block.py 等测试文件,说明这是所有上下文型原语的统一约定。

在 Rust 侧,src/rust/src/exceptions.rs 提供了便捷的构造辅助:

pub(crate) fn already_finalized_error() -> CryptographyError { CryptographyError::from(AlreadyFinalized::new_err("Context was already finalized.")) }

实战要点:捕获AlreadyFinalized通常意味着程序设计缺陷(对已终结对象重复操作)。合理做法是在业务层避免复用上下文对象,如确有需要,重新创建一个新对象。

AlreadyUpdated:update 之后的二次数据追加

文档定义:**在已调用过 update 之后又向上下文添加附加数据(additional data)**时抛出。它保护的是那些"只允许一次 update 之后不可再追加"的上下文类型。其触发场景相对集中在特定的原语实现中,属于较罕见的错误,通常意味着调用方对接口语义理解有误——应当将全部数据一次性传入,或改用支持流式多次 update 的接口。

签名与密钥验证的失败信号:InvalidSignature 与 InvalidKey

这两个异常分别守护"验证类"操作的两条链路:MAC/签名校验链路与 KDF 密钥校验链路。

InvalidSignature:HMAC 或非对称签名验证失败

文档定义:签名验证失败时抛出,可发生于 HMAC 或非对称密钥签名校验。它不携带任何附加信息(源码中为空的pass类,见 exceptions.py),仅作为布尔判定的异常化表达。

典型用法是CMAC.verify():

from cryptography.hazmat.primitives.cmac import CMAC from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.exceptions import InvalidSignature cmac = CMAC(AES(b"2b7e151628aed2a6abf7158809cf4f3c")) cmac.update(b"6bc1bee22e409f96e93d7e117393172a") try: cmac.verify(b"foobar") # 标签不匹配 except InvalidSignature: print("MAC 校验失败,数据可能被篡改。")

test_cmac.py 中的test_invalid_verify正是用pytest.raises(InvalidSignature)断言这一行为。HMAC(HMAC.verify())与 RSA/ECDSA/Ed25519 等非对称签名(verify())在验证失败时同样抛出该异常。

实战要点:校验失败是"预期内可恢复"的错误(可能源于篡改或传输损坏),通常捕获后走重试、告警或拒绝流程;而TypeError/ValueError才代表调用方自身的参数错误。

InvalidKey:KDF 验证方法计算的密钥不匹配

文档定义:密钥派生函数(KDF)的 verify 方法计算出的密钥与期望密钥不匹配时抛出。其触发场景典型见于 Argon2 等密码哈希 KDF 的verify()调用。

test_argon2.py 展示了相关断言,且 test_argon2.py 中还存在match="did you mean to use"的提示性消息,说明InvalidKey在部分实现中会携带帮助用户修正 API 用法的友好提示(例如误把verify当hash使用时的引导信息)。

实战要点:密码验证场景中,InvalidKey表示"口令错误",应据此返回"用户名或密码错误"之类的统一响应,避免向攻击者泄露账户存在性。

容易被忽略的 NotYetFinalized:AEAD tag 的时序保护

文档定义:在上下文尚未终结(finalized)时就访问 AEAD 的 tag 属性时抛出。这是 AEAD(如 AES-GCM、ChaCha20-Poly1305)特有的时序约束——tag 只有在finalize()之后才存在,提前读取是非法操作。

这一设计的正确用法是先终结、再取 tag:

from cryptography.hazmat.primitives.ciphers.aead import AESGCM aesgcm = AESGCM(key) ct = aesgcm.encrypt(nonce, data, None) # 直接返回密文+tag,一步完成 # 若使用分步 API(如 Cipher 上下文),则必须先 finalize 再读取 tag

捕获NotYetFinalized通常意味着流程编排错误(忘记调用finalize()),属于编程缺陷而非外部攻击信号。

完整实践:一段涵盖多类异常的错误处理模板

综合上述语义,给出一个可落地的统一错误处理范式:

from cryptography.exceptions import ( AlreadyFinalized, AlreadyUpdated, InvalidKey, InvalidSignature, NotYetFinalized, UnsupportedAlgorithm, ) def safe_verify(data, tag, cmac): try: cmac.update(data) cmac.verify(tag) return True except InvalidSignature: return False # 校验失败:可恢复 except AlreadyFinalized: cmac = new_cmac() # 编程缺陷:重建上下文后重试 return safe_verify(data, tag, cmac) except AlreadyUpdated: raise ValueError("数据必须一次性传入") from None except UnsupportedAlgorithm as e: print(f"后端不支持该算法: {e._reason}") return None

总结与最佳实践清单

  • 按语义分类处理:UnsupportedAlgorithm反映环境能力缺失(可升级 OpenSSL 或换后端);InvalidSignature/InvalidKey是验证失败(可恢复的业务结果);AlreadyFinalized/AlreadyUpdated/NotYetFinalized是状态机违规(编程缺陷,应修复调用逻辑)。
  • 善用 reason 属性:捕获UnsupportedAlgorithm后读取_reason(_Reasons枚举的 12 个成员,见 exceptions.rs),可向用户给出精确的缺失能力提示。
  • 统一从cryptography.exceptions导入:所有异常类型均在该模块中定义(exceptions.py),Rust 侧实现也通过pyo3::import_exception!回指该模块,保证类型一致。
  • 用测试固化行为:仓库测试(如 test_cmac.py、test_argon2.py、test_aes.py)对每个异常都有精确断言,可作为自己代码中pytest.raises断言的参照模板。
  • 密码学

【免费下载链接】cryptography

cryptography is a package designed to expose cryptographic primitives and recipes to Python developers.

项目地址:https://gitcode.com/gh_mirrors/cr/cryptography
点击查看免费下载
上一篇:react-dates性能优化:useCallback与事件处理
下一篇:GitHub项目创新指标:Badges4-README.md-Profile技术创新徽章

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询