将 AWS KMS 非对称密钥签名/验证签名编码为 base64 并验证

问题描述

我正在使用 AWS KMS 非对称 ECC (ES256) 密钥通过 python3 中的 API 调用签署和验证令牌。响应返回

{
    "KeyId": "arn:aws:kms:us-east-1:000000000000:key/1234","Signature": "b'0D\\x02 \\x18\\xd9\\x13\\x96\\x9d\\xb00p\\xc9H\\'-\\xc6@{\\xd1V\\xf5\\xeb\\x83\"1\\x0e:\\x98\\xb4\\xea6P\\x0f\\xdf\\x82\\x02 \\x1fPt\\xc0\\x81pB\\xaa\\xe7_\\xf2~\\x91BlU\\x05\\xba1\\xe0\\xfe\\xf6\\xe4\\xebL\\nl\\xfc\\xf8\\xce\\xc6_'","Signingalgorithm": "ECDSA_SHA_256" 

}

如果我使用 AWS CLI,响应“签名”是 base64 编码的:

{
    "KeyId": "arn:aws:kms:us-east-1:000000000000:key/1234","Signature": "MEQCIBqjAiAnMKkugooWU6/AqCyfhQUocoiVeIIf8lL2p7YbAiBEJOl2cp9HzQNufiMBezIjZuSGW6ID13l7JSzTgLlv+g==","Signingalgorithm": "ECDSA_SHA_256"
}

每当我尝试使用 python3 中的 API 响应对 base64.urlsafe_b64encode(string)“签名”进行 base64 编码时,我都无法验证签名。这是我收到的错误示例:

调用验证时发生错误 (InvalidKeyUsageException) 操作:asn1:结构错误标签不匹配(16 vs {class:1 标签:2 长度:39 isCompound:真}){可选:假显式:假 应用程序:假私有:假认值:标签: stringType:0 timeType:0 set:false omitEmpty:false} ecdsaSignature @2

如何对 API 返回的“签名”字节字符串进行 base64 编码,并且仍然能够验证?

更新:

如果我更新为 base64.b4encode 而不是 base64.urlsafe_encode,我会得到相同的结果:

def _sign_token(self,key_id,message):
    signature = kms_client.sign(
        KeyId=key_id,Message=message,MessageType="RAW",Signingalgorithm="ECDSA_SHA_256",)
    logger.log(msg=signature,level=cl.log_level)
    return base64.b64encode(signature["Signature"]).decode()

日志消息返回:

{"asctime": "2021-04-03 19:40:27,593","levelname": "INFO","name": "util.sign","lineno": 214,"message": null,"env": "local","KeyId": "arn:aws:kms:us-east-1:000000000000:key/309d8dc5-409e-4f57-96a8-c71223c84dc2",“签名”: "b'0E\x02!\x00\xcc\x82\xf4$@?\x9e\n\xd7$\x94\x9f.\x1d5\x19{W\x1e\xcff\x8b&\xf0\xef\x88\xcf ^"\xf6\xa4\x1b\x02 \x0c\x18\xab?\x93\xd5\x88Cx\xf6\x0c\x1b\xcf\xd9\xd9\xd2\xd4"\xcf\x94\xede_>\x8c\x01~J\xea\x0ezB'","Signingalgorithm": "ECDSA_SHA_256","ResponseMetadata": {"HTTPStatusCode": 200,"HTTPHeaders": {"content-type": “应用程序/x-amz-json-1.1”,“内容长度”:“233”,“连接”: "close","access-control-allow-origin": "*",“访问控制允许方法”: “头部,获取,放置,发布,删除,选项,补丁”, “访问控制允许标头”: “授权、内容类型、内容长度、内容 md5、缓存控制、x-amz-content-sha256、x-amz-date、x-amz-security-token、x-amz-user-agent、x -amz-target,x-amz-acl,x-amz-version-id,x-localstack-target,x-amz-tagging","access-control-expose-headers": "x-amz-version-id ","date": "星期六,03 2021 年 4 月 19:40:27 GMT","server": "hypercorn-h11"},"RetryAttempts": 0}}

验证错误

"stacktrace": "发生错误 (InvalidKeyUsageException) 时 调用验证操作:asn1:结构错误标签不匹配 (16 vs {class:1 tag:13 length:69 isCompound:false}) {optional:false 显式:假应用:假私有:假认值: 标签:stringType:0 timeType:0 set:false omitEmpty:false} ecdsaSignature @2"

def _verify_signature(self,message,signature):
    response = kms_client.verify(
        KeyId=key_id,Signature=signature,)
    logger.log(msg=response,level=cl.log_level)
    return response

client.verify 要求签名为字节 https://boto3.amazonaws.com/v1/documentation/api/1.11.4/reference/services/kms.html#KMS.Client.verify

如果我不使用 .encode(),我会得到同样的结果:

"stacktrace": "发生错误 (InvalidKeyUsageException) 时 调用验证操作:asn1:结构错误标签不匹配 (16 vs {class:1 tag:13 length:69 isCompound:false}) {optional:false 显式:假应用:假私有:假认值: 标签:stringType:0 timeType:0 set:false omitEmpty:false} ecdsaSignature @2"

def _sign_token(self,level=cl.log_level)
    return base64.b64encode(signature["Signature"])

注意:

如果我在没有 base64 编码的情况下将 client.sign 响应“签名”传递给 client.verify,则验证通过。

解决方法

AWS cli 编码的字符串不是 urlsafe-base64 编码的!它使用标准 base64 编码,即 base64.b64encode

标准 base64 编码使用三个元字符 /+=/+ 在路径或查询字符串中使用都不安全,URL 安全编码将这两个分别替换为 _-。由于 /+ 存在于 AWS CLI 编码字符串中,因此可以推断它使用标准 base64 编码而不是 URL 安全编码。

base64.b64encode 的返回值是 bytes 对象。通常使用 Base-64 编码将二进制编码为文本。要将值改为文本,您需要 decode 它(例如,将 UTF-8 转换为 unicode 字符串):

base64_encoded_as_str = base64.b64encode(binary_value).decode()
,

AWS 支持流程有效。结果我在尝试验证而不是 decode()

时对 base64 编码的签名进行了 base64.b64decode() 操作