错误信息
Invalid EC key. Point is not on the curve specified.
在 cryptography 50.0 上复现。EllipticCurvePublicKey.from_encoded_point() 对同样的失败给出更简短的报告:“Invalid EC key.”。其他技术栈的措辞各不相同——OpenSSL 1.1 的错误栈写的是“point is not on curve”,Node 则以“Invalid JWK EC key”拒绝该 JWK——但它们背后的数学原理完全相同。
Python cryptography — ValueError from EllipticCurvePublicNumbers.public_key()
含义
你提供的 (x, y) 坐标对不满足你所指定曲线的方程 y² = x³ + ax + b (mod p)。各个库在导入时都会校验这一点,因为接受不在曲线上的点会让无效曲线攻击成为可能,进而泄露私钥——所以这种拒绝是一项安全检查,而不是吹毛求疵。这项检查只需代入曲线方程计算一次,开销很小,所有成熟的库都会执行:不在曲线上的点总会在某处被拒绝,与其在下游出问题,不如在这里明确地报错。
原因
坐标与错误的曲线搭配
常见secp256k1 上的点(比特币/以太坊)被声明为 SECP256R1/P-256——或者反过来。两条曲线都使用 32 字节的坐标,因此其他检查都发现不了这种混用,只有曲线方程能发现。
坐标字节损坏或被截断
常见前导零被去掉(坐标只剩 31 字节)、十六进制解码时出现差一错误、混淆了 base64 和 base64url,或者只是翻转了一个位——对 x 或 y 的任何改动几乎肯定会让点离开曲线。
字节序错误
偶见在所有标准编码中,坐标都是大端序。按小端序反转后的字节(与硬件或自行编写的代码对接时很常见)描述的是一个完全不同的点——而且不在曲线上。
修复方法
- 1.
用正确的曲线,从编码后的点构建密钥
from_encoded_point 一步完成长度和结构的校验;如果数据来源不明确,就换另一条候选曲线试试。
python from cryptography.hazmat.primitives.asymmetric import ec point = b"\x04" + x_bytes + y_bytes # uncompressed, 65 bytes for P-256 key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256R1(), point) # if that raises, test: ec.SECP256K1() — the other 256-bit suspect - 2.
检查原始数据
构建之前,确认长度为 32 字节且数值合理;在左侧用零填充,恢复被去掉的前导零。
python print(len(x_bytes), len(y_bytes)) # must be 32, 32 for P-256 x_bytes = x_bytes.rjust(32, b"\x00") # repair stripped leading zeros y_bytes = y_bytes.rjust(32, b"\x00")