如何从 JWK 密钥创建密钥以使用 Python 验证 COSE signature-1?

How to create key from JWK key for verifying COSE signature-1 with Python?

我正在编写 python 脚本来验证新西兰疫苗接种护照。我在拥有政府证书和解码的 COSE 消息并需要尝试验证其签名的那一刻挣扎:

key_as_dict = {
    KpKty: cose.keys.keytype.KtyEC2,
    # OKPKpCurve: cose.keys.curves.EllipticCurve, # this one also does not work
    OKPKpCurve: cose.keys.keytype.KtyEC2,
    OKPKey: kid,
    KpKeyOps: [VerifyOp],
    OKPKpD: jwk_key['x'],
    OKPKpX:jwk_key['y'],
}
govt_key = CoseKey.from_dict(key_as_dict)
cose_decoded.key = govt_key
print("key ready")
print("header", cose_decoded.verify_signature())

错误信息:

cose.exceptions.CoseInvalidKey: COSE curve cannot be None

这是政府提供的用于测试目的的 JWK 密钥:

{
  "kty": "EC",
  "crv": "P-256",
  "x": "zRR-XGsCp12Vvbgui4DD6O6cqmhfPuXMhi1OxPl8760",
  "y": "Iv5SU6FuW-TRYh5_GOrJlcV_gpF_GpFQhCOD8LSk3T0"
}

有谁知道如何将政府证书正确映射到密钥字典?

更多信息:

失败的原因是您对给定的密钥使用了错误的key type

对于作为 JWK ("kty": "EC") 提供的密钥,您需要使用 EC2,但在您的代码中,您试图将其映射到 OKP 参数。

因此您首先需要在代码的导入部分更改两行:

#from cose.keys.keyparam import KpKty, OKPKpCurve, OKPKpD, OKPKpX
from cose.keys.keyparam import KpKty, EC2KpCurve, EC2KpX, EC2KpY
#from cose.keys.okp import OKPKey
from cose.keys.ec2 import EC2Key

然后从JWK中提取关键参数x和y。参数以Base64Url编码存储,需要解码。 Python Base64 解码器坚持填充,即使 Base64Url 不需要它。因此,在解码之前在 Base64Url 编码字符串上添加填充“=”的神秘代码:

x = jwk_key['x']
if len(x) % 4:
    x = x + "=" * (4 - len(x)%4)
ec_x = base64.urlsafe_b64decode(x)

y = jwk_key['y']
if len(y) % 4:
    y = y + "=" * (4 - len(y)%4)
ec_y = base64.urlsafe_b64decode(y)

最后,修改映射代码如下:

key_as_dict = {
    KpKty: cose.keys.keytype.KtyEC2,
    EC2KpCurve: P256,
    EC2Key: kid,
    KpKeyOps: [VerifyOp],
    EC2KpX: ec_x,
    EC2KpY: ec_y,
}

如您所见,我将参数从 OKP... 更改为对应的 EC2...

程序的输出(我跳过了第 1..5 部分,因为您已经知道结果)是:

*** Part 6: Verifying certificate
key ready
header True


下面是一个最小的可运行示例,仅显示基于代码中硬编码 JWK 密钥的密钥映射:

import base64
import cose
from cose.keys.curves import P256, CoseCurve
from cose.keys.keyparam import KpKty, EC2KpCurve, EC2KpX, EC2KpY
from cose.keys.keyparam import KpKeyOps
from cose.keys.keyops import SignOp, VerifyOp
from cose.keys import CoseKey
from cose.keys.ec2 import EC2Key
import json

# this is in reality loaded from the web
jwk_string = '''{"kty": "EC", "crv": "P-256",
               "x": "zRR-XGsCp12Vvbgui4DD6O6cqmhfPuXMhi1OxPl8760",
               "y": "Iv5SU6FuW-TRYh5_GOrJlcV_gpF_GpFQhCOD8LSk3T0"}'''
jwk_key = json.loads(jwk_string)
kid = "123"  # just an example

# mapping the incoming JWK to a COSE key
x = jwk_key['x']
if len(x) % 4:
    x = x + "=" * (4 - len(x)%4)
ec_x = base64.urlsafe_b64decode(x)

y = jwk_key['y']
if len(y) % 4:
    y = y + "=" * (4 - len(y)%4)
ec_y = base64.urlsafe_b64decode(y)

key_as_dict = {
    KpKty: cose.keys.keytype.KtyEC2,
    EC2KpCurve: P256,
    EC2Key: kid,
    KpKeyOps: [VerifyOp],
    EC2KpX: ec_x,
    EC2KpY: ec_y,
}

govt_key = CoseKey.from_dict(key_as_dict)
print("key ready")

附带说明:确保 ECDSA 包是最新的。我的电脑上已经有一个旧版本,在验证过程中出现了一个奇怪的错误:

TypeError: from_public_point() got an unexpected keyword argument 'validate_point'

升级后消失:

pip install ecdsa --upgrade