0
Answered

JWT sign with private key

Kris 3 months ago updated by Vladimir Ovchinnikov 3 weeks ago 10

Hi

I need to create a JWT token signed with private key and encoded with SHA256withRSA algorithm.

This would be used for connection with third party API.

Is there any function in iRidium library I could use to do this? Method IR.CalculateHash(algorithm,string) does not seem to have 'private key' input parameter and I could not find anything else in the documentation.

Any ideas how I could accomplish this?

Thanks!

Under review

Hello.

1) using the IR.Base64Encode method, to encode the Header.

2) using the IR.Base64Encode method, to encode the Payload.

3) Form a common string Base64 (Header)+. + Base64 (Payload). Please note that the dot is required.

4) using the private and public keys using the SHA256 with RSA algorithm, get a Verify Signature from the string that was obtained in step 3. there Is an IR.CreateEncryption method, but you can use pure JS. Let us know if you need an example of using the IR.CreateEncryption method.
5) use the IR.Base64Encode Method to encode the string from step 4.
6) the Result from step 3 + . + result from step 4 = JWT. Please note that the dot is required.

Hi,

I generally stuck on point 4. Can you please give me an example of using IR.CreateEncryption method as it may also come handy later.

I have only private key, no public key.


According to documentation I also need to encode to Base64url - is there a method in iRidium to do that? Or will I have to write conversion in JS?

P.S.

My aim is to get authorized to Google OAuth2.0 Service Account, i am following this documentation: https://developers.google.com/identity/protocols/oauth2/service-account#httprest

We will add documentation for the IR.CreateEncryption method soon. Below is the "raw" information.

There are 2 types of creating an RSA encryption object:

1) With the key size.

var RSA = IR.CreateEncryption(IR.ENCRYPTION_RSA, key_size);

Parameters:

IR.ENCRYPTION_RSA - encryption type

key_size key size in bytes 32(256 bits), 64 (512 bits), 128(1024 bits), 256(2048 bits), 512(4096 bits) (number)

2) Using a public or private key.

var RSA = IR.CreateEncryption(IR.ENCRYPTION_RSA, key, is_publick);

Parameters:

IR.ENCRYPTION_RSA - encryption type

key - the key (string)

is_publick - flag whether this key is public or not (true, false)

Methods:

1) Encrypt.

RSA.Encode(data (string/array), resultType, inputType);

Parameters:

You can pass a string or an array of numeric data.

resultType - constant (return value type)

IR.RESULT_TYPE_ARRAY - get an array of numeric data (returns an array)

IR.RESULT_TYPE_BASE64_STRING - get a string in Base64 to avoid losing data, then you can use IR.Base64Encode() (returns a string)

inputType - constant (type of the first parameter)

Only valid if the first parameter is a string.

IR.INPUT_TYPE_BYTE_STRING - signal that just a string has arrived (you don't need to specify it, this parameter is used by default)

IR.INPUT_TYPE_BASE64_STRING - signal that a base64 string has arrived

2) Decrypt.

RSA.Decode(data (string/array), resultType, inputType);

Parameters:

You can pass a string or an array of numeric data.

resultType - constant (return value type)

IR.RESULT_TYPE_ARRAY - get an array of numeric data (returns an array)

IR.RESULT_TYPE_STRING - get just a string

IR.RESULT_TYPE_BASE64_STRING - get a string in Base64 to avoid losing data, then you can use IR.Base64Encode() (returns a string)

inputType - constant (type of the first parameter)

Only valid if the first parameter is a string.

IR.INPUT_TYPE_BYTE_STRING - signal that just a string has arrived (you don't need to specify it, this parameter is used by default)

IR.INPUT_TYPE_BASE64_STRING - signal that a base64 string has arrived

3) Get the public key.

RSA.GetPublickKey() (return string or undefined);

Hi,

Thank you for the documentation.

I have created my JWT as described, but I am still having problems with the authorisation. The OAuth server keeps returning the 'Invalid JWT signature' error.

I think this might be due to my Base64 conversion. They require to convert to Base64url. I cannot see any function in iRidium to convert to URL-safe base64.

I have used standard IR.Base64Encode() and made the output UR: friendly with following code:

B64URLE : function(string){
            var inData = JSON.Stringify(string);
            var encodedData = IR.Base64Encode(inData);
            return encodedData.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');

But that does not seem to work.

The output is different than what online converters do.

For example, I am trying to convert following header:

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

The result from IR.Base64Encode() and making it URL friendly is : eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QifQ, while online converters convert it correctly to eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.

However, after decoding, the output string is the same.

Is there any other way to encode to Base64url in iRidium? I cannot see any possibilities without using external libraries.

I am also wondering if this might because iRidium EncryptionObject.Encode(data (string/array), resultType, inputType) takes IR.INPUT_TYPE_BASE64_STRING instead of Base64URL?

This is how I create my JWT:

var googleOAuthVars = {
        PrivateKey : "-----BEGIN PRIVATE KEY-----\n****KEY****\n-----END PRIVATE KEY-----\n",
        Scopes : ['https://www.googleapis.com/auth/drive'],
        JWT : jwt = {
                    Header : {"alg":"RS256","typ":"JWT"},
                    ClaimSet : claimSet = {
                      "iss": "email@gserviceaccount.com",
                      "scope": "https://www.googleapis.com/auth/drive",
                      "aud": "https://oauth2.googleapis.com/token",
                      "exp": parseInt((new Date().getTime())/1000)+20*60,
                      "iat": parseInt(new Date().getTime()/1000)
                    },
                    JWT : jwt = "",
                   }
}


var header = utilities.B64URLE(googleOAuthVars.JWT.Header);
var claimSet = utilities.B64URLE(googleOAuthVars.JWT.ClaimSet);
var signatureInput = header+"."+claimSet;
var signature = utilities.Encrypt(googleOAuthVars.PrivateKey,signatureInput);
var signature64 = utilities.B64URLE(signature);
googleOAuthVars.JWT.JWT = signatureInput+"."+signature64;     //JWT

var utilities = {
        B64URLE : function(string){
            var inData = JSON.Stringify(string);
            var encodedData = IR.Base64Encode(inData);
            return encodedData.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
        },
        B64URLD : function(string){
            string = (string + '===').slice(0, string.length + (string.length % 4));
            string =  string.replace(/-/g, '+').replace(/_/g, '/');
            var decodedData = IR.Base64Decode(string);       
            return decodedData;
        },
        Encrypt : function(in_key,in_data){
            var key = in_key;
            var data = in_data;
            var RSA = IR.CreateEncryption(IR.ENCRYPTION_RSA, key, false);
            var result = RSA.Encode(data, IR.RESULT_TYPE_ARRAY, IR.INPUT_TYPE_BASE64_STRING);
            return result;
         }
}

EDIT:

After thorough investigation, turns out that iRidium JSON.Stringify() creates whitespaces after each parameter. Thus the differences between web and iRidium encoders.

Now my Base64 strings seem to be correct, but the server still rejects the signature.

Is there an error with how I used IR.CreateEncryption()?

Hello.

Base64URL is a modification of the main Base64 standard.

In i3 pro, there is no separate method for Base64URL. You can write a function in JavaScript to do this. You may need information from here.

Hi.

I am pretty sure I have everything encoded into Base64URL but the response from the OAuth2 server is 'Invalid JWT signature' error.

I suspect the problem lies with IR.CreateEncryption().

Since the JWT should be signed as follows:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

I am not sure how I am to achieve this with IR.CreateEncryption() since the encryption object can be created either with key size or private/public key.

From the example above I need to tell the algorithm the encryption type and the key?

Is this achievable with IR.CreateEncryption()?

Can you please give an example?

Thanks!

Hello.

1) the Google documentation says that you need to Base64-encoded (not Base64url).

2) Sign with a private key associated with the service account identified by the client email.

3) Only one type of encryption can be used in CreateEncryption - RSA.

4) Decode the JWT claim set and verify the key that signed the assertion is associated with the service account.

Hello.

Do you need our help at the moment?

Hi,

No thank you.

I chose different approach to this problem.