Why Sign JSON Web Tokens From A Password When We Can Use RSA or ECDSA?

One of the most widely used token standards is JSON Web Tokens (JWT).

Photo by Pop & Zebra on Unsplash

Why Sign JSON Web Tokens From A Password When We Can Use RSA or ECDSA?

One of the most widely used token standards is JSON Web Tokens (JWT).

With this we have:

  • A header. This defines the token type (such as JWT) and the signing method that we will use.
  • A payload. This defines the main payload data, and is defined in a simple JSON format. This might include the user’s ID, their email address, and so on. The fields are flexible and can be created for any purpose.
  • A signature. This is either a public key signature (with RSA or ECDSA) or an HMAC signature (and which uses a given hashing method and a secret password).

The registered claim names are:

  • “iss” (Issuer). This identifies the issuer of the token.
  • “sub” (Subject). This defines the subject of the token.
  • “aud” (Audience). This defines the general audience for the token.
  • “exp” (Expiration Time). This defines the time that the token will expire.
  • “nbf” (Not Before). This defines the time that the token will start.
  • “iat” (Issued At). This defines the time that the token was created.
  • “jti” (JWT ID). This defines the ID of the token.

Overall. the format of the token is header.payload.signature. So:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmcmVkQGhvbWUiLCJzdWIiOiJNeSBlbWFpbCIsIm5iZiI6MTY1OTEwMTA3OSwiaWF0IjoxNjU5MTAxMDc5LCJleHAiOjE2NTkxODc0NzksImp0aSI6IklEMTI0NTY3OSJ9.MXS5U2jGMyYbbs7Ek03IB_z2_1lWZCMnqIRo-TIJb6o

has three fields:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
eyJpc3MiOiJmcmVkQGhvbWUiLCJzdWIiOiJNeSBlbWFpbCIsIm5iZiI6MTY1OTEwMTA3OSwiaWF0IjoxNjU5MTAxMDc5LCJleHAiOjE2NTkxODc0NzksImp0aSI6IklEMTI0NTY3OSJ9
MXS5U2jGMyYbbs7Ek03IB_z2_1lWZCMnqIRo-TIJb6o

and which identify the header, the payload and the signature. If we do a Base64 decoding of “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9”, we get:

{"alg":"HS256","typ":"JWT"}

and for “eyJpc3MiOiJmcmVkQGhvbWUiLCJzdWIiOiJNeSBlbWFpbCIsIm5iZiI6MTY1OTEwMTA3OSwiaWF0IjoxNjU5MTAxMDc5LCJleHAiOjE2NTkxODc0NzksImp0aSI6IklEMTI0NTY3OSJ9”, we get:

{"iss":"fred@home","sub":"My email", "nbf":1659101079,"iat":1659101079,"exp":1659187479,"jti":"ID1245679"}

We can see that this token is not encrypted.

Overall, these tokens provide an excellent way to gain access to networked services, and where a user can gain a token and then pass it to the required services.

The signature methods

The methods that provide thesignature include HS256 (HMAC SHA-256), ES256 (ECDSA using P-256 and SHA-256) and RS256 ( RSASSA-PKCS1-v1_5 with the SHA-256). HS256 uses a password to provide the key for the signature, while ES256 and RS256 require a private key to sign the token and a public key to provide it. In this case, we will use public key encryption to sign the token, and where the private key signs the token, and the public key is used to verify it.

Some JavaScript coding for signing a JSON Web Token with RSA is [here]:

function gojwt(size,method, iss, sub, id) {



var kp = KEYUTIL.generateKeypair("RSA",size );


var priv = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS8PRV");

document.getElementById("keys").innerHTML = "Private key:\n" + priv;




var pub = KEYUTIL.getPEM(kp.pubKeyObj, "PKCS8PUB");
document.getElementById("keys").innerHTML += "Public key:\n" + pub;


var oHeader = { alg: method, typ: 'JWT' };



var oPayload = {};
var tNow = KJUR.jws.IntDate.get('now');
var tEnd = KJUR.jws.IntDate.get('now + 1day');
oPayload.iss = iss;
oPayload.sub = sub;
oPayload.nbf = tNow;
oPayload.iat = tNow;
oPayload.exp = tEnd;
oPayload.jti = id



var sHeader = JSON.stringify(oHeader);
var sPayload = JSON.stringify(oPayload);

document.getElementById("JWT").innerHTML = "Header:\n" + sHeader;
document.getElementById("JWT").innerHTML += "\n\nPayload:\n" + sPayload;

var sJWT = KJUR.jws.JWS.sign(method, sHeader, sPayload, priv);

document.getElementById("JWT").innerHTML += "\n\nSignature:\n" + sJWT;

var isValid = KJUR.jws.JWS.verifyJWT(sJWT, pub, { alg: [method], iss: [iss], sub: [sub] });

document.getElementById("JWT").innerHTML += "\n\nValid JWT: " + isValid;


}



We can see that the private key (priv) is used in the signing method, and the public key (pub) is used in the verification. A sample run is [here]:

Header:
{"alg":"RS256","typ":"JWT"}
Payload:
{"iss":"fred@home","sub":"My email","nbf":1659128165,"iat":1659128165,"exp":1659214565,"jti":"ID124567"}
Signature:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmcmVkQGhvbWUiLCJzdWIiOiJNeSBlbWFpbCIsIm5iZiI6MTY1OTEyODE2NSwiaWF0IjoxNjU5MTI4MTY1LCJleHAiOjE2NTkyMTQ1NjUsImp0aSI6IklEMTI0NTY3In0.gLlbPlyr_BHsFXOFMMwcgfWKdda8KfbMk_DXfQrDBRrj4azdWxKifKPHNSNi9ZpXyLh6uw8UeDw1DzLlj57MnA
Valid JWT: true

Notice, that the header and the payload are not encrypted. Here is the demo:

https://asecuritysite.com/javascript/jwt2

Conclusions

A typically way that we sign JSON Web Tokens is to derive a key from a password — the HMAC signing. This causes a problem in having to share the password. An improved way is thus to sign the token from a trusted issuer with their private key, and then anyone can use the associated public key to check. This can be defined within PKI (Public Key Infrastructure).

Here’s the code:

https://asecuritysite.com/javascript/jwt2