Using the below code, I am able to build a decoded JSON Web Token (decodedToken
). When I paste it into the debugger on https://jwt.io, the header and payload are correctly shown on the right. But when I paste the encoded token (encodedToken
) into the debugger, I get this message:
Looks like your JWT payload is not a valid JSON object. JWT payloads must be top level JSON objects as per https://www.rfc-editor.org/rfc/rfc7519#section-7.2
It also says, "Invalid Signature," in big red letters at the bottom.
In addition, when using the encoded token, the header area shows my decoded header, and the payload area shows my entire decoded token. So somehow jwt.io is successfully decoding the JWT. But it shouldn't be trying to parse the JSON of the entire decoded token as a single unit. It should be trying to parse the JSON of the header and payload portions separately.
I would like to know why jwt.io can't quite my payload of my encoded token. The values of both the decodedToken
and encodedToken
are shown as comments in the below source code. Thank you.
private static string GetEncodedToken(string privateKey)
{
JObject header = JObject.FromObject(new { alg = "ES384", typ = "JWT" });
JObject payload = JObject.FromObject(new { name = "value" });//simple test JSON data
string decodedToken = Base64URLEncode(header) + "." + Base64URLEncode(payload);
//eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidmFsdWUifQ
byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
using (ECDsa ecdsa = ECDsa.Create())
{
ecdsa.ImportECPrivateKey(privateKeyBytes, out _);
string encodedToken = JWT.Encode(decodedToken, ecdsa, JwsAlgorithm.ES384);
//eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.ZXlKaGJHY2lPaUpGVXpNNE5DSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnVZVzFsSWpvaWRtRnNkV1VpZlE.kYj0M2GNC5w9oRrIVcHJ9dSQLbOEjNjxd61zSXD5iz0nBRRWMWyxj2l1DAhKPa_hpaFUscuubCzYe_W1SKpo3s06on-hYnICgQH4fVoAwZxiM_N4W761jecOExfivztn
return encodedToken;
}
}
private static string Base64URLEncode(JObject jObject)
{
string jsonString = jObject.ToString(Formatting.None);
byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonString);
string escapedBase64 = Convert.ToBase64String(jsonBytes).TrimEnd('=').Replace('+', '-').Replace('/', '_');
return escapedBase64;
}
Thank you for the answers so far. I think I'm close, but jwt.io still says, "Invalid Signature," even though I've pasted in the public key. Using the below information, I'm hoping someone can tell me why my signature is said to be invalid.
I've generated a temporary private key just for the purpose of debugging this situation on stackoverflow.com:
MIGkAgEBBDBywQ7LVcyOGzxJ0Tpjpww2zUZbbtb3WVm4A3uv7ho31jJzQRYTpSqR7+ORAdoxmamgBwYFK4EEACKhZANiAASG50vW1r/O1XmUbTBb6yx1YSABh1USA6MJ8HJnYJ58tjGGVPL88a6Z1gOUlAsHtNhL44PhnTNTNNFdaH2Z41yn7oZmBhuon0vuUNFic2HDpfa/uFwRUAmhSBQz8hu+980=
I initialized an instance of ecdsa
with my private key and generated a public key with Convert.ToBase64String(ecdsa.ExportSubjectPublicKeyInfo())
:
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhudL1ta/ztV5lG0wW+ssdWEgAYdVEgOjCfByZ2CefLYxhlTy/PGumdYDlJQLB7TYS+OD4Z0zUzTRXWh9meNcp+6GZgYbqJ9L7lDRYnNhw6X2v7hcEVAJoUgUM/IbvvfN
Here is the updated code:
private static string GetSignedEncodedToken(string privateKey)
{
JObject payload = JObject.FromObject(new { name = "value" });//simple test JSON data
string payloadString = payload.ToString(Formatting.None);
byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
using (ECDsa ecdsa = ECDsa.Create())
{
ecdsa.ImportECPrivateKey(privateKeyBytes, out _);
string signedEncodedToken = JWT.Encode(payloadString, ecdsa, JwsAlgorithm.ES384);
return signedEncodedToken;
}
}
And here is the signed encoded token: eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.ew0KICAibmFtZSI6ICJ2YWx1ZSINCn0.KpJAgc3-yaoGmHGAXHOeH3BPgpxdBRm461yWia60dgjuQHG5iLnwLQtQgdZtsHnI-bEK_wdmvu85ZrF7n-TdWiFb4FQxGeLBeeRfnMLJhKfInu_7MYEWPS2Ohm4yBAqg
As mentioned by jps, I needed to wrap my public key with "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----". Then jwt.io shows, "Signature Verified."
What you're calling decodedToken
here is basically already an encoded token without signature. The function JWT.Encode
takes the first parameter and repeats what you already did manually, i.e. it creates a base64url encoded header and payload for you and then signs the token. You don't need to create your own header and you don't need to care about base64url encoding.
Therefore you just need to pass your payload
(not base64 encoded) there. The result is a signed token.
And jwt.io shows "invalid signature" because you probably didn't pass the public key to the "public key" field in the right column. Without knowing the public key, the signature can't be verified.
When you paste a public key there, make sure it has the header and footer lines -----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
, then verification should work fine.