带有ES256的无效JWT签名 [英] Invalid JWT signature with ES256

查看:117
本文介绍了带有ES256的无效JWT签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试手动创建ES256 JWT令牌.我有一个用python编写的小脚本,该脚本签署了使用 ecdsa-python 的sha256哈希.但是,该签名在 jwt.io 上无效.

复制步骤:

  1. 创建base64标头+有效负载:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOixMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6Mz

  1. 从base64标头+有效负载创建SHA256哈希:

FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F

  1. 生成ec_private密钥:

openssl ec -in ec_private.pem -noout -text

  1. 使用小型python程序ecdsa对SHA256哈希进行签名

来自json导入转储的

 从ellipticcurve.ecdsa导入Ecdsa从ellipticcurve.privateKey导入PrivateKey导入base64def toBase64Url(输入):返回input.replace("+",-").replace("/","_").rstrip("=")#从PEM字符串生成privateKeyprivateKey = PrivateKey.fromPem("----- BEGIN EC参数-----BgUrgQQACg ==-----结束EC参数---------- BEGIN EC私钥-----MHcCAQEEIJfChy9fKFItzqcb8DKBm + 2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==-----结束EC私钥-----(")#从json创建消息消息="FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F"签名= Ecdsa.sign(消息,私钥)#在base64中生成签名.该结果可以作为Digital-Signature参数发送到请求标头中的Stark Ba​​nk.打印("Base64:" + signature.toBase64())打印("Base64Url:" + toBase64Url(signature.toBase64()))#要仔细检查邮件是否与签名匹配,请执行以下操作:publicKey = privateKey.publicKey()打印(哈希验证成功:" + str(Ecdsa.verify(消息,签名,公钥))) 

输出:

Base64:MEQCIFyP4IoZGhzGfDCPX6fVxjtB + nrXDVhOTQwdc5vu8z4eAiBNalfGHqdaO3nCmTqimpAHF + IHzxk8em + OMMHrJkPOhA ==

Base64Url:MEQCIFyP4IoZGhzGfDCPX6fVxjtB-nrXDVhOTQwdc5vu8z4eAiBNalfGHqdaO3nCmTqimpAHF-IHzxk8em-OMMHrJkPOhA

哈希验证成功:是

  1. jwt.io 中检查签名是否给出无效签名

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.MEQCIFyP4IoZGhzGfDCPX6fVxjtB-nrXDVhOTQwdc5vu8z4eAiBNalfGHqdaO3nCmTqimpAHF-IHzxk8em-OMMHrJkPOhA

键:

公共:

-----开始公钥-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==----- END公钥-----

私人:

----- BEGIN EC私钥-----MHcCAQEEIJfChy9fKFItzqcb8DKBm + 2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==----- END EC私钥-----

我知道有很多jwt签名python库,但是这样做是为了了解如何创建jwt令牌.

正如@Topaco指出的那样,该库使用曲线secp256k1而不是secp256r1.secp256r1 |prime256v1 |NIST P-256是由不同标准组织为同一条曲线选择的所有不同名称(椭圆曲线密码术(ECC)用于传输层安全性(TLS)的密码套件).我将库更改为 python-ecdsa ,并将代码更改为:

从ecdsa导入

 导入SigningKey,NIST256p导入base64def toBase64Url(输入):返回input.replace("+",-").replace("/","_").rstrip("=")sk = SigningKey.from_pem("----- BEGIN EC私钥-----MHcCAQEEIJfChy9fKFItzqcb8DKBm + 2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==-----结束EC私钥-----(")vk = VerifyingKey.from_pem(").----- BEGIN公钥-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==-----结束公钥-----(")签名= sk.sign(b"FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F")打印(base64.b64encode(签名))print("Base64:" + base64.b64encode(signature).decode("utf-8"))print("Base64Url:" + toBase64Url(base64.b64encode(signature).decode("utf-8"))))断言vk.verify(签名,b"FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F")打印(哈希验证成功:"+ str(vk.verify(signature,b" FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F))) 

输出:

Base64:rMBgC0ismGdd5rd7n1L + LDsQ2UO5 + cjBwPNYh + xBZvO6fKoJIfmfyNpxw + kxmyKWlK + 55dF5eMH1u327DMJvvA ==

Base64Url:rMBgC0ismGdd5rd7n1L-LDsQ2UO5-cjBwPNYh-xBZvO6fKoJIfmfyNpxw-kxmyKWlK-55dF5eMH1u327DMJvvA

哈希验证成功:是

但是签名仍然无效.

解决方案

您正在隐式使用哈希的库,默认情况下应用SHA1.IE.为了与ES256兼容,必须明确指定SHA256,并且必须使用未修饰的JWT,例如:

来自ecdsa的

 导入SigningKey,VerifyingKey导入base64从hashlib导入sha256def toBase64Url(输入):返回input.replace("+",-").replace("/","_").rstrip("=")jwt = b" eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOxIxMjM0NTY3ODkwiiwibmFtZSI6IkpvaG4gRG9liwiYWRtaW4IOnRydWUsImlhMnsk = SigningKey.from_pem("----- BEGIN EC私钥-----MHcCAQEEIJfChy9fKFItzqcb8DKBm + 2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==-----结束EC私钥-----(")vk = VerifyingKey.from_pem(").----- BEGIN公钥-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ ==-----结束公钥-----(")签名= sk.sign(jwt,hashfunc = sha256)print("Base64:" + base64.b64encode(signature).decode("utf-8"))print("Base64Url:" + toBase64Url(base64.b64encode(signature).decode("utf-8"))))断言vk.verify(signature,jwt,hashfunc = sha256)print(哈希验证成功:" + str(vk.verify(signature,jwt,hashfunc = sha256))) 

可能的输出是:

  Base64:Mr4/DF87ek66E2GcAc + 2H3ulHplCnxygz65h9dkdvm8QsZBbm2N5EjIgyiWsynza9zCGjjnzBUiXYvij9LLikA ==Base64Url:Mr4_DF87ek66E2GcAc-2H3ulHplCnxygz65h9dkdvm8QsZBbm2N5EjIgyiWsynza9zCGjjnzBUiXYvij9LLikA哈希验证成功:True 

生成的已签名令牌

<预类= 朗 - 无prettyprint-越权"> <代码> eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.Mr4_DF87ek66E2GcAc-2H3ulHplCnxygz65h9dkdvm8QsZBbm2N5EjIgyiWsynza9zCGjjnzBUiXYvij9LLikA

然后可以使用此处使用的公钥在 https://jwt.io/上成功验证

./p>

I'm trying to manually create an ES256 JWT token. I've a small script written in python which signs a sha256 hash which uses ecdsa-python. But the signature is invalid on jwt.io.

Steps to reproduce:

  1. Create base64 header + payload:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0

  1. Create SHA256 hash from the base64 header + payload:

FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F

  1. Generate ec_private key:

openssl ec -in ec_private.pem -noout -text

  1. Use the small python program to ecdsa sign the SHA256 hash

from json import dumps
from ellipticcurve.ecdsa import Ecdsa
from ellipticcurve.privateKey import PrivateKey

import base64

def toBase64Url(input):
    return input.replace("+", "-").replace("/", "_").rstrip("=")

# Generate privateKey from PEM string
privateKey = PrivateKey.fromPem("""
    -----BEGIN EC PARAMETERS-----
    BgUrgQQACg==
    -----END EC PARAMETERS-----
    -----BEGIN EC PRIVATE KEY-----
    MHcCAQEEIJfChy9fKFItzqcb8DKBm+2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49
    AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBa
    Iv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ==
    -----END EC PRIVATE KEY-----
""")

# Create message from json
message = "FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F"

signature = Ecdsa.sign(message, privateKey)

# Generate Signature in base64. This result can be sent to Stark Bank in the request header as the Digital-Signature parameter.
print("Base64: " + signature.toBase64())
print("Base64Url: " +toBase64Url(signature.toBase64()))



# To double check if the message matches the signature, do this:
publicKey = privateKey.publicKey()

print("Hash verification succesfull: " + str(Ecdsa.verify(message, signature, publicKey)))

The output:

Base64: MEQCIFyP4IoZGhzGfDCPX6fVxjtB+nrXDVhOTQwdc5vu8z4eAiBNalfGHqdaO3nCmTqimpAHF+IHzxk8em+OMMHrJkPOhA==

Base64Url: MEQCIFyP4IoZGhzGfDCPX6fVxjtB-nrXDVhOTQwdc5vu8z4eAiBNalfGHqdaO3nCmTqimpAHF-IHzxk8em-OMMHrJkPOhA

Hash verification succesfull: True

  1. Check the signature in jwt.io gives Invalid signature

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.MEQCIFyP4IoZGhzGfDCPX6fVxjtB-nrXDVhOTQwdc5vu8z4eAiBNalfGHqdaO3nCmTqimpAHF-IHzxk8em-OMMHrJkPOhA

Keys:

Public:

-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0 uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ== -----END PUBLIC KEY-----

Private:

-----BEGIN EC PRIVATE KEY----- MHcCAQEEIJfChy9fKFItzqcb8DKBm+2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49 AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBa Iv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ== -----END EC PRIVATE KEY-----

I know that there are many jwt signing python libraries but the use of this is to understand how a jwt token is created.

EDIT:

As @Topaco pointed out this library uses curve secp256k1 instead of secp256r1. secp256r1 | prime256v1 | NIST P-256 are all different names chosen by different standards organizations for the same curve (Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)). I changed the library to python-ecdsa and the code to:

from ecdsa import SigningKey, NIST256p
import base64

def toBase64Url(input):
    return input.replace("+", "-").replace("/", "_").rstrip("=")


sk = SigningKey.from_pem("""
    -----BEGIN EC PRIVATE KEY-----
    MHcCAQEEIJfChy9fKFItzqcb8DKBm+2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49
    AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBa
    Iv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ==
    -----END EC PRIVATE KEY-----
""")
vk = VerifyingKey.from_pem("""
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0
uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ==
-----END PUBLIC KEY-----
""")
signature = sk.sign(b"FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F")

print(base64.b64encode(signature))
print("Base64: " + base64.b64encode(signature).decode("utf-8"))
print("Base64Url: " + toBase64Url(base64.b64encode(signature).decode("utf-8")))

assert vk.verify(signature, b"FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F")
print("Hash verification succesfull: " + str(vk.verify(signature, b"FFC89E33091FFDD3C61798A0A74BF7C2D1A6FD231A6CB519F33952F7696BBE9F")))

The output:

Base64: rMBgC0ismGdd5rd7n1L+LDsQ2UO5+cjBwPNYh+xBZvO6fKoJIfmfyNpxw+kxmyKWlK+55dF5eMH1u327DMJvvA==

Base64Url: rMBgC0ismGdd5rd7n1L-LDsQ2UO5-cjBwPNYh-xBZvO6fKoJIfmfyNpxw-kxmyKWlK-55dF5eMH1u327DMJvvA

Hash verification succesfull: True

But the signature is still invalid.

解决方案

The library you are using hashes implicitly, applying SHA1 by default. I.e. for compatibility with ES256 SHA256 must be explicitly specified and the unhashed JWT must be used, e.g.:

from ecdsa import SigningKey, VerifyingKey
import base64
from hashlib import sha256

def toBase64Url(input):
    return input.replace("+", "-").replace("/", "_").rstrip("=")

jwt = b"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"

sk = SigningKey.from_pem("""
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJfChy9fKFItzqcb8DKBm+2oH0YTZ7N61SQpyABgVZANoAoGCCqGSM49
AwEHoUQDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0uoYcvX4Z7ROUIgYRvgfpsjBa
Iv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ==
-----END EC PRIVATE KEY-----
""")
vk = VerifyingKey.from_pem("""
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1TG2uvIMdfWkteiDWeHNYbQNSW/0
uoYcvX4Z7ROUIgYRvgfpsjBaIv70SuYpmBLwl0AuEBoXIVTCclCme6SdEQ==
-----END PUBLIC KEY-----
""")

signature = sk.sign(jwt, hashfunc=sha256)

print("Base64: " + base64.b64encode(signature).decode("utf-8"))
print("Base64Url: " + toBase64Url(base64.b64encode(signature).decode("utf-8")))

assert vk.verify(signature, jwt, hashfunc=sha256)
print("Hash verification succesfull: " + str(vk.verify(signature, jwt, hashfunc=sha256)))

A possible output is:

Base64: Mr4/DF87ek66E2GcAc+2H3ulHplCnxygz65h9dkdvm8QsZBbm2N5EjIgyiWsynza9zCGjjnzBUiXYvij9LLikA==
Base64Url: Mr4_DF87ek66E2GcAc-2H3ulHplCnxygz65h9dkdvm8QsZBbm2N5EjIgyiWsynza9zCGjjnzBUiXYvij9LLikA
Hash verification succesfull: True

The resulting signed token

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.Mr4_DF87ek66E2GcAc-2H3ulHplCnxygz65h9dkdvm8QsZBbm2N5EjIgyiWsynza9zCGjjnzBUiXYvij9LLikA

can then be successfully verified on https://jwt.io/ with the public key used here.

这篇关于带有ES256的无效JWT签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆