uruk-project / Jwt

JSON Web Token implementation for .Net & .Net Core

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SymmetricJwk string key decoding fail

MortalFlesh opened this issue · comments

Hello,
I'm trying to create a SymmetricJwk with user password - so only him can verify the JWT again.

But I got an Exception.

System.FormatException: The input is not a valid Base-64 URL string as it contains a non-base 64 character.
   at JsonWebToken.ThrowHelper.ThrowOperationNotDoneException(OperationStatus status)
   at JsonWebToken.Base64Url.Decode(ReadOnlySpan`1 base64Url, Span`1 data)
   at JsonWebToken.Base64Url.Decode(ReadOnlySpan`1 base64Url)
   at JsonWebToken.Base64Url.Decode(String data)
   at JsonWebToken.SymmetricJwk..ctor(String k)

I found this https://stackoverflow.com/questions/44866847/convert-frombase64string-does-not-work-in-code-but-works-in-online-tool/44867049 so it explained me, why I got the error exactly.

But I think it might be a bug for creating a JWK, since when I create a token here https://jwt.io/#debugger-io with that password, it is created without any problem.

So I'm not sure, why there is _k = Base64Url.Decode(k); in the first place, could it be done by System.Text.Encoding.UTF8.GetBytes instead? I guess it would be more user friendly.

But maybe I see it all wrong (I'm kinda new to JWT world).

It is not recommended to use a user password as a symmetric key for at least 2 reasons:
Firstly the key must have a minimal size, based on the algorithm used. For example for HS256 it is 128 bits, and for HS512 it is 256 bits.
Secondly the key entropy may not be sufficient when representing a user password, because not all the bits are used.

This is also true with non-ASCII characters, like in dvojčárka.
"dvojčárka😁".Length == 11 but
Encoding.UTF8.GetBytes("dvojčárka😁") == byte[15] { 100, 118, 111, 106, 196, 141, 195, 161, 114, 107, 97, 240, 159, 152, 129 }
As you can see, characters are between 100 and 196 (except the the 240 for the emoji), and the binary length may be unexpected (even if it is not an issue to a key bigger than expected)

This is why there is currently no implementation for creating a symmetric key from a password string.

The SymmetricJwk constructor with a string accept only a valid Base64-URL encoded string, because all binary data in JWT is in Base64-URL (which is slightly different to Base64 and completly different to Base64 + URL encoding). You can manipulate Base64-URL with the class Base64Url, but always Base64-URL to binary and binary to Base64-URL.

If you want to use a user password as key, you should use
new SymmetricJwk(Encoding.UTF8.GetBytes("my_password"))

Another option would be to derive the user password with a key derivation algorithm like PBKDF2 then use the resulting key.

So I'm not sure, why there is _k = Base64Url.Decode(k); in the first place, could it be done by System.Text.Encoding.UTF8.GetBytes instead? I guess it would be more user friendly.

One option would be to add a static method like SymmetricKey.FromString or SymmetricKey.FromPassword, but I do not like the give opportunity to use a low entropy key.

so only him can verify the JWT again.

It seems to be a rare good use case for symmetric key, but be careful in case of bad password, and to not store unsecurely the password.

Hello,
thank you for explaining to me - it is much more clear now.

My use-case is quite more specific - I have SPA, where users are authenticated by LDAP, then I create a JWT for them to communicate between client/server just in the SPA. I'm using a UUID generated on application start-up, so the app can only validate a tokens, it created in this current running.

And I'm using a password for JWK only for super-admin, where I have control about a password (because I set it), and I need a "static" JWT, with a specific key for it.

So as you explained, I wouldn't add any new SymmetricKey create function as well - I would say, its rather and edge case and you should know what to do.

But still - I was surprised by the exception - it might be explained in the readme section, because it could easily happen to someone and it would be a runtime exception in other cases.

Thank you!

Samples have been updated to use SymmetricJwk.FromBase64Url(string) instead of new SymmetricJwk(string)
Documentation added to Jwk constructors parameters to specify the expected data type (like Base64-URL for the ctor with a string)
Available in release 1.5.