基于Python的ECC混合加密公钥导出方法及MAC校验失败问题解决
解决方案
你遇到的ValueError: MAC check failed错误核心原因是加密参数序列化/反序列化不规范,尤其是ECC公钥点、nonce、authTag的存储和读取逻辑错误,导致解密时拿到的参数和加密时不一致,最终AES-GCM校验失败。
1. 修复ECC公钥点的序列化/反序列化逻辑
tinyec.ec.Point类型的ciphertextPubKey包含x、y两个大整数和对应曲线配置,你当前只存储了一个截断的十六进制值,完全丢失了y坐标信息,反序列化后得到的公钥是错误的,自然生成的共享密钥无法通过AES校验。
收发两端提前约定使用固定曲线brainpoolP256r1,无需额外存储曲线信息,序列化方案如下:
from tinyec import ec, registry # 加密侧:将Point对象序列化为64字节(x、y各32字节,大端序存储) def serialize_pubkey(point: ec.Point) -> bytes: x_bytes = point.x.to_bytes(32, byteorder='big') y_bytes = point.y.to_bytes(32, byteorder='big') return x_bytes + y_bytes # 解密侧:将64字节反序列化为Point对象 curve = registry.get_curve('brainpoolP256r1') def deserialize_pubkey(pubkey_bytes: bytes) -> ec.Point: x = int.from_bytes(pubkey_bytes[:32], byteorder='big') y = int.from_bytes(pubkey_bytes[32:], byteorder='big') return ec.Point(curve, x, y)
2. 统一加密文件存储格式
不要使用字符串拼接的方式存储密文和参数,你的现有格式存在乱码、参数分割逻辑不可靠的问题,改用固定长度前缀+固定长度参数的二进制格式,无需任何分隔符即可准确拆分所有内容,格式定义如下:
[密文长度(4字节无符号大端整数)][密文内容][12字节nonce][16字节authTag][64字节序列化ECC公钥]
3. 加密侧写入逻辑示例
import struct # 加密后得到的原始参数:ciphertext(字节类型密文)、nonce(12字节AES-GCM随机数)、authTag(16字节校验标签)、ciphertextPubKey(ECC公钥Point对象) ser_pubkey = serialize_pubkey(ciphertextPubKey) # 打包密文长度为4字节 cipher_len_packed = struct.pack('>I', len(ciphertext)) # 按顺序拼接所有内容写入文件 with open('encrypted_file.bin', 'wb') as f: f.write(cipher_len_packed) f.write(ciphertext) f.write(nonce) f.write(authTag) f.write(ser_pubkey)
4. 解密侧读取逻辑示例
import struct with open('encrypted_file.bin', 'rb') as f: # 读取4字节解出密文长度 cipher_len = struct.unpack('>I', f.read(4))[0] # 按长度读取密文 ciphertext = f.read(cipher_len) # 按固定长度读取其余参数 nonce = f.read(12) authTag = f.read(16) ser_pubkey = f.read(64) # 反序列化公钥 ciphertextPubKey = deserialize_pubkey(ser_pubkey) # 组装参数传入原有解密函数即可 encryptedMsg = (ciphertext, nonce, authTag, ciphertextPubKey) decrypted_data = decrypt_ECC(encryptedMsg, privKey)
内容的提问来源于stack exchange,提问作者Hazem Ha
相关产品推荐
相关产品推荐

