[Answered]: JWT component for ESP-IDF (JSON Web Tokens)

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

[Answered]: JWT component for ESP-IDF (JSON Web Tokens)

Postby kolban » Sun Aug 19, 2018 8:09 pm

When using certain cloud IoT services such as Google Cloud Platform (GCP) we have to authenticate with the cloud by providing a JSON Web Token (JWT) ... see:

https://jwt.io/

While there are many libraries available, the number of such libraries for C/C++ is relatively low. I had a quick look through them and none are immediately useable on the ESP-IDF platform. They either use their own JSON parsers (not cJSON) or require a full "openssl" stack (where we use mbedtls).

As such, these projects would appear to need porting or a new JWT written for our own needs. I'm contemplating having a go at this but before embarking on that quest, I want to see if either it is already done or if someone else has a project in flight down this path?

So ... anyone using JWT with ESP-IDF? If not, anyone interested in this area besides myself?
Last edited by kolban on Sun Aug 26, 2018 12:25 am, edited 1 time in total.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

p-rimes
Posts: 89
Joined: Thu Jun 08, 2017 6:20 pm

Re: JWT component for ESP-IDF (JSON Web Tokens)

Postby p-rimes » Mon Aug 20, 2018 6:51 pm

Only a few mbedTLS commands needed. You might find this (GPL) implementation useful, although it is GCP specific, it does overview the signing process (any functions using the mbedtls_ API) for both RS256 and ES256.

https://github.com/mongoose-os-libs/gcp ... _gcp.c#L69

s.allasia
Posts: 27
Joined: Tue Jan 09, 2018 3:12 pm

Re: JWT component for ESP-IDF (JSON Web Tokens)

Postby s.allasia » Thu Aug 23, 2018 12:36 pm

Hi kolban,
I'm interisting to use GCLOUD (IOT core): I'm trying to sign my mqtt password with JSON Web Token (JWT) but actually I can't generate the digital signature with RSASSA-PKCS1-v1_5 using SHA-256.
I followed istructions on https://tools.ietf.org/html/rfc7518#section-3.3, but the final signature I can't do it: I don't understand the last step of the signature.
My topic:
viewtopic.php?f=2&t=6879
Any idea?
Thanks

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: JWT component for ESP-IDF (JSON Web Tokens)

Postby kolban » Thu Aug 23, 2018 3:00 pm

Howdy,
This is exactly my puzzle too. While JWT is generic, my practical need is for GCP (Google Cloud Platform). I'm in no immediate rush and haven't started digging too deeply. My first thought was this thread to see if anyone has walked this pass previously. The Mongoose OS implementation looks good. There are also some other open source JWT implementations on Github but these leverage richer openssl than we have in ESP-IDF. If you are working on a direct implementation of the JWT RFC directly, I for one would be delighted to collaborate with you and others as long as the end result is open source free distribution on Github.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

foxbat
Posts: 1
Joined: Sat Aug 25, 2018 5:46 pm

Re: JWT component for ESP-IDF (JSON Web Tokens)

Postby foxbat » Sat Aug 25, 2018 6:09 pm

I have managed to get GCP connectivity working with mbedTLS using the RS256 scheme and just ESP-IDF/freertos. My code is scattered across a few C++ classes so not so simple to copy/paste here but the steps aren't too hard. I'll detail them here.

Generate a 2048 bit RSA keypair. Store the public half in the IOT console. GCP does not support less than 2048 bits for RSA.

Get your private key into your program however you want. I reference it as a file in component.mk. Load it up using the mbedtls_pk_parse_key() function:

Code: Select all

mbedtls_pk_init(&_context);                                     // this just zeros out the internal pointers

int rc = mbedtls_pk_parse_key(
    &_context,
    (const uint8_t *) privateKey.c_str(),
    privateKey.length() + 1,
    nullptr,
    0);
Create the first two parts of the JWT as per the standard. The first part should be a base64 representation of this:

Code: Select all

{
  "alg": "RS256",
  "typ": "JWT"
}
The second part should be a base64 encoding of this:

Code: Select all

{
  "iat": 1535218434,
  "exp": 1535222034,
  "aud": "YOUR-GCP-PROJECT-ID"
}
I don't know whether GCP validates "iat" so I always knock it back 10 minutes to account for clock differences. I set "exp" to +3600 seconds.

VERY IMPORTANT: The base64 alphabet must be the URL-safe alphabet with no "=" padding, ie. this:

Code: Select all

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
Concatenate your two base64 fragments separated by a "." and feed that string first into a SHA256 hash and then a signature. The signature will be 256 bytes long:

Code: Select all

  uint8_t hash[32];
  int rc = mbedtls_md(
  		mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
  		(const uint8_t *)str.c_str(), str.length(), hash);

  size_t sig_len = mbedtls_pk_get_len(context);
  uint8_t *sig=(uint8_t *)calloc(sig_len,1);

  // sign the hash

  rc = mbedtls_pk_sign(
  		context,
  		MBEDTLS_MD_SHA256,
      hash,
      sizeof(hash),
      sig,
      &sig_len,
      NULL,NULL);
Base64 encode your signature and append it with another "." separator to the first two parts.

I can then use that JWT to make successful calls to Google over an SSL connection. If you still get problems after doing all then double and triple check your project/registry/device identifiers in all the places you need to specify them in the call. Use the online debugger at jwt.io to check your JWT against your public key. Don't even bother trying Google until jwt.io tells you that your signature is valid.

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: JWT component for ESP-IDF (JSON Web Tokens)

Postby kolban » Sun Aug 26, 2018 12:24 am

Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

snahmad75
Posts: 445
Joined: Wed Jan 24, 2018 6:32 pm

Re: [Answered]: JWT component for ESP-IDF (JSON Web Tokens)

Postby snahmad75 » Thu Dec 13, 2018 9:19 pm

Hi,

Do JWT should get generate from given user name and password.

We will allow user and password to get set/change

Is cookie auth on mongoose web server different from JWT?


I guess I can return pre-generated JWT for my user admin. and return in http hearder and receive via http header.


Thanks,
Naeem

jamiehug
Posts: 2
Joined: Mon Apr 01, 2019 3:56 pm

Re: [Answered]: JWT component for ESP-IDF (JSON Web Tokens)

Postby jamiehug » Mon Apr 01, 2019 4:00 pm

Hi Kolban,

I managed to get your example working perfectly with RSA keys so thanks for that. I am now looking at generating a JWT using EC keys for Google Cloud Platform. Have you had any luck implementing this? :)
Last edited by jamiehug on Tue Apr 02, 2019 8:18 am, edited 1 time in total.

jamiehug
Posts: 2
Joined: Mon Apr 01, 2019 3:56 pm

Re: [Answered]: JWT component for ESP-IDF (JSON Web Tokens)

Postby jamiehug » Mon Apr 01, 2019 4:42 pm

Hi Kolban,

Thanks for your work on the JWT example, I got it to work perfectly with RSA keys. I am now looking to generate a JWT token for Google IoT MQTT comms using EC keypairs (I have generated an EC key pair and saved the private/public keys into a .pem file that I read in on the esp32). See Google's specs on how they specify the format of the JWT below. Using mbedtls_pk_sign and changing the header of the JWT to ES256 does not seem to work, I am not sure if mbedtls_pk_sign is using ECDSA? I have also created a JWT for the keypair using the Google JWT example to test the rest of my MQTT program works by the way. Would really appreciate the help. :)

https://cloud.google.com/iot/docs/how-t ... tials/jwts

DerrickLau
Posts: 7
Joined: Mon Apr 13, 2020 9:09 pm

Re: [Answered]: JWT component for ESP-IDF (JSON Web Tokens)

Postby DerrickLau » Sat Oct 10, 2020 4:35 am

I am using an UWP app to talk to my ESP32 via SPP and bluetooth classic. I am able to open a socket to my ESP-32 and send a JSON message over bluetooth to it, and confirmed that I received it via the serial port monitoring:
V (361538) TARDIS: ::receiveMessage() - {"IdToken":"eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lICI6ImhlbGxvICIsInNjb3BlIjoiaHR0cDovL2R1bW15LmNvbS8ifQ.FPkHESpldjwEsdE_ii8936gFq4pfptl3b6ao13BTLZk","Command":"testing 4","TimeSent":{"Ticks":0,"Days":0,"Hours":0,"Milliseconds":0,"Minutes":0,"Seconds":0,"TotalDays":0,"TotalHours":0,"TotalMilliseconds":0,"TotalMinutes":0,"TotalSeconds":0},"TimeReceived":{"Ticks":0,"Days":0,"Hours":0,"Milliseconds":0,"Minutes":0,"Seconds":0,"TotalDays":0,"TotalHours":0,"TotalMilliseconds":0,"TotalMinutes":0,"TotalSeconds":0}}�
As you can see it got the JSON message, but I was ambitious and added a JWT token at the top. You'll notice that the token JSON is signed and encoded in UTF8 bytes too. I generated it using .NET standard 2.0 library in my UWP app:

Code: Select all

using System.Security;
using System.Text;
using System.IdentityModel;
using Microsoft.IdentityModel;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

           // Create Security key  using private key above:
           // not that latest version of JWT using Microsoft namespace instead of System
            var securityKey = new Microsoft
               .IdentityModel.Tokens.SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));

            // Also note that securityKey length should be >256b
            // so you have to make sure that your private key has a proper length
            //
            var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials
                              (securityKey, SecurityAlgorithms.HmacSha256Signature);

            //  Finally create a Token
            var header = new JwtHeader(credentials);
            string alg = header.Alg;

            //Some PayLoad that contain information about the  customer
            var payload = new JwtPayload
           {
               { "some ", "hello "},
               { "scope", "http://dummy.com/"},
           };

            //
            var secToken = new JwtSecurityToken(header, payload);
            var handler = new JwtSecurityTokenHandler();

            // Token to String so you can use it in your client
            string tokenString = handler.WriteToken(secToken);
How in my C++ ESP-IDF code should I decode my token so that I can read the header and payload elements?

And how do I encrypt my message in the UWP C# app and decrypt it in my C++ ESP-IDF app?

Who is online

Users browsing this forum: Bing [Bot] and 119 guests