Jwt token generation with RS256 and decode with utf-8 in k6

I am trying to generate jwt token with a key but it in not same as I wanted. I referred Simple JWT example for k6 load testing tool · GitHub this and used but not as generated.
In python I have used like this

jwt.encode(
    {"userNumber": 1, "username": "abc@qqq..com"}, private_key, algorithm="RS256"
).decode("utf-8")

How can we generate the token in K6?

Hi @ge_orgejoseph, welcome to the community forum :tada:

Can you please tell me what you tried with the given example, what was the result and what did you expect (what you get in python). You can generate a private key just for that so that the examples are full without compromising your real keys.

Hi @mstoykov thanks for your comments. I was able to convert payload and header to base64 and combined using “.”
And it is now matching with first two parts base64(header) + "." + base64(payload)

let secret = "anything"
let token = base64(header) + "." + base64(payload); // upto here is fine
let hash = crypto.hmac('sha256', secret, token, "base64rawurl");
console.log(token + "." + hash)  // hash value is wrong when compared with python

Signature was unable to verify. Can anyone help

I did some more digging and RS256 is using RSA which is asymetrical unlike the HS256 algorithm which is symmetrical and uses HMAC.

Unfortunately, k6 does not have API for signing with RSA and at least crypto-js and tweet-nacl do not support RSA as well. js-crypto-rsa supposedly should work, but unfortunately I can’t make it load, because of nodejs module resolution problems. And I am pretty sure that even if I managed it will probably still will have problems :(.

I then found this which I kind of made to work, but unfortunately I ran into the problem that this is both very slow and me not understanding what needs to happen(not really into cryptography at all) - for example I am now pretty sure that this code does not actually implement RSASSA-PKCS1-V1_5 signing which is what is needed for the jwt key.

I did go through some crypto extensions(and an old PR) until I :man_facepalming: and remembered that there actually is a xk6-jwt extension, unfortunately (of course) it seems to also not work :(.

Again unfortunately, I, spend way too much time on this to continue down this rabbit whole, but you can probably fix xk6-jwt - the predominant problem is (AFAIK) that it doesn’t take ArrayBuffer but instead []byte and apart from that it probably needs better interface :man_shrugging: .

Hopefully one day Extend/rewrite `k6/crypto` · Issue #2248 · grafana/k6 · GitHub will be prioritized highly enough and we will start adding APIs to k6 at that point, but until then I would recommend:

  • either use HS256 (don’t know what the implications of that are) at least for load testing
  • write your own extension (fix the linked above) and use that.

I doubt this helps you, but hopefully in the future someone will skip some of the things I’ve tried :crossed_fingers: and find a different solution :man_shrugging:

Thanking @mstoykov your research on this. I too have reached the same thoughts and idea with in day that makes me to dig a little more in K6. And your response makes the confirmation on those. I will got for creating extension that sounds good for my case.

The above post will helps and in future it help others.

1 Like

Glad it helps you @ge_orgejoseph :)!

You can also make your extension public and we will add it to the list of extensions :slight_smile:

Hi @ge_orgejoseph,

I’m tackling the same issue now - have you managed to create a working extension?

Any updates on this topic?
(We plan to start using k6, but we use RS256 for the token signature)

Hi @eugeneg,

k6 does now have support for parts of the WebCrypto API, but not the RSA ciphers specifically.

From somewhat qucik googling I managed to get this example working

import KJUR from "https://unpkg.com/jsrsasign@10.8.6/lib/jsrsasign.js";

function addIAT(request) {
	var iat = Math.floor(Date.now() / 1000) + 257;
	data.iat = iat;
	return data;
}

var header = { alg: "RS256", typ: "JWT" };
var data = {
	fname: "name",
	lname: "name",
	email: "email@domain.com",
	password: "abc123$",
};

data = addIAT(data);

var privateKey =
	"-----BEGIN RSA PRIVATE KEY----- \
MIIBOQIBAAJAcrqH0L91/j8sglOeroGyuKr1ABvTkZj0ATLBcvsA91/C7fipAsOn\
RqRPZr4Ja+MCx0Qvdc6JKXa5tSb51bNwxwIDAQABAkBPzI5LE+DuRuKeg6sLlgrJ\
h5+Bw9kUnF6btsH3R78UUANOk0gGlu9yUkYKUkT0SC9c6HDEKpSqILAUsXdx6SOB\
AiEA1FbR++FJ56CEw1BiP7l1drM9Mr1UVvUp8W71IsoZb1MCIQCKUafDLg+vPj1s\
HiEdrPZ3pvzvteXLSuniH15AKHEuPQIhAIsgB519UysMpXBDbtxJ64jGj8Z6/pOr\
NrwV80/EEz45AiBlgTLZ2w2LjuNIWnv26R0eBZ+M0jHGlD06wcZK0uLsCQIgT1kC\
uNcDTERjwEbFKJpXC8zTLSPcaEOlbiriIKMnpNw=\
-----END RSA PRIVATE KEY-----";

var sHeader = JSON.stringify(header);
var sPayload = JSON.stringify(data);

export default () => {
	var sJWT = KJUR.jws.JWS.sign(header.alg, sHeader, sPayload, privateKey);
	console.log(JSON.stringify(sJWT));
};

Which returns something looking okay. And is based on this stackoverflow answer.

Your mileage might vary depending on what exactly you need to work with though and do.

Hope this helps you and anyone else who comes across this.

1 Like