ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Generate HS256 JWT Using SQLRPGLE V7R3

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Generate HS256 JWT Using SQLRPGLE V7R3

    I've been hitting a few roadblocks in my attempt to generate an HS256 jwt using SQLRPGLE.

    I wanted to make sure i'm not missing anything, and before I post any code, I wanted to make sure I am thinking about this correctly, from an RPG perspective. It is important to preface, by default, my jobs run in CCSID(37). If this needs to change, let me know.

    Here's my flow:

    build & store json header string in ebcdic format
    convert json header string to ascii
    build & store payload string in ebcdic format
    convert json payload string to ascii
    get & store secret key in ebcdic format
    convert secret key string to ascii

    build & store base64 encoded ascii header string and remove trailing = symbols (url encoded)
    build & store base64 encoded ascii payload string and remove trailing = symbols (url encoded)
    build & store a string as follows: base64_url_encoded_header_ascii + '.' + base64_url_encoded_payload_ascii

    execute Qc3CalculateHMAC() using the ascii secret key as the salt and the base64_url_encoded string to be hashed

    convert the response from binary to ascii <-- how do you do this??
    build & store signature string in ascii using the HMAC ascii value
    build & store base64 encoded ascii signature string using the HMAC ascii value

    build & store jwt using: base64_encoded_header_ascii + '.' + base64_encoded_payload_ascii + '.' + base64_encoded_signature_ascii

    If this looks correct, the part i'm struggling with is how to get the binary response from the HMAC() api into an ascii RPG variable so that i can base64 encode it?












  • #2
    I have just been faced with the exact same task. There are a few issues with the sequence you have outlined, mostly around the fact that you need the stuff in UTF-8 and it is easier just to build it that way.

    Here's the best news. There is a ready made set of routines from Mihael Schmidt which not only build the JWTs but also includes validation routines. I also found routines to do the timestamp -> epoch translation which I needed since the APIs I was using require an expiration.

    You can get Mihael's stuff - it is a plug-in for the ILEastic project but can be built stand-alone. Here's the URL:
    Embedded application server for ILE on IBM i. Contribute to sitemule/ILEastic development by creating an account on GitHub.



    Especially look at:
    Embedded application server for ILE on IBM i. Contribute to sitemule/ILEastic development by creating an account on GitHub.


    The routines work really well and will save you many hours.

    Comment


    • #3
      Thanks for your response Jon. When I was referring to "ascii" i was meaning ccsid 1208 (which i think is utf-8?).

      I will give this a look!

      Comment


      • JonBoy
        JonBoy commented
        Editing a comment
        You should - it saved me a LOT of time.

    • #4
      Apologies if I'm, in essence, re-opening an old post and I should have created a new post but my issue relates to this post. Feel free to head-slap me and advise if the correct protocol is to open a new post and refer to this one.

      The issue: Adobe Analytics now require a JWT to access their systems to retrieve an access token. the JWT has to be signed using a private key which you can generate on their website in the format (the begin and end components are very important):

      -----BEGIN PRIVATE KEY-----
      LOTSOFCHARACTERSWILLBEINYOURKEYANDITS76CHARSPERLIN E
      FORTHEFIRST21LINES
      DITTO
      UNTIL
      LASTLINEOF24CHARS
      -----END PRIVATE KEY-----

      I've successfully managed to use jsonwebtoken in NodeJS and that's all lovely and it can successfully talk into Adobe and receive the access token. Unfortunately, the place I'm working at don't have NodeJS on the I or any other internal system that I could utilise. So I've extract the C and RPG elements out of ILEastic and got them compiled all lovely but no matter how I try to format the private key (/n for newlines, partial /n on the begin and end parts etc.) I receive CPF9DDB 'The key string or Diffie-Hellman parameter string is not valid' from QC3HMAC. Has anyone had any experience/success when using a private key to sign?

      Thanks for reading.
      Rob

      Comment


      • #5
        When you used Node to create the JWT how did you format the private key? With newlines? A sample of the code you used would be helpful - just the bit that creates the JWT.

        You say you tried /n and "partial /n" (whatever that means) but neither of those would mean anything to an RPG routine so how did you actually form the string that you used? Show us your code for that please.

        Comment


        • #6
          Hi Jon

          Thanks for replying.

          In Node I formatted it as a literal:

          Code:
          const payload =
            {"exp": Math.floor(Date.now() / 1000) + (60),
             "iss": "fooooooo@AdobeOrg",
             "sub": "barrrrrrrrrrrrrrr@techacct.adobe.com",
             "https://ims-na1.adobelogin.com/s/ent_analytics_bulk_ingest_sdk": true,
             "aud": "https://ims-na1.adobelogin.com/c/thiswouldbetheclientid"
            };
          
          const privateKey =
          `-----BEGIN PRIVATE KEY-----
          REMOVED/actual/key/but/here/were/the/lines
          and/I/used/the/literal/quotes/and/not
          the/standard/single/quotes/which/seemed
          to/keep/the/line/feels/in
          -----END PRIVATE KEY-----`;
          
          const token = jwt.sign(JSON.stringify(payload), privateKey, { algorithm: 'RS256' });
          I found that I needed the embedded line feeds after the -----BEGIN PRIVATE KEY----- even when pasting the key on their website for a JWT generation.

          In terms of the RPG and what I mean by /n and partial /n (sorry for not explaining better) in that first I tried:

          Code:
          privateKey = '-----BEGIN PRIVATE KEY-----' +
           'TheRestOfTheKeyLine1' +
           'etc' +
           '-----END PRIVATE KEY-----';
          then I tried to get new lines in there (but I get what you mean about it meaning nothing but I am at pulling hair out time)


          Code:
          privateKey = '-----BEGIN PRIVATE KEY-----/n' +
           'TheRestOfTheKeyLine1' +
           'etc/n' +
           '-----END PRIVATE KEY-----';
          and then when that didn't work I tried /n all over the place! Obviously that didn't work.

          Am I even using the private key in the correct form?
          ​​​​​​​
          Regards
          Rob

          Comment


          • #7
            Just for chuckles, change the original private key definition to move the line breaks. e.g.
            Code:
            const privateKey =
            `-----BEGIN PRIVATE KEY-----
            REMOVED/actual/key/
            but/here/were/the/lines and/I/used/the/literal/quotes/and/not
            Does it still work like that? I'm finding it hard to believe that they are needed _within_ the key test. BUT - it would surprise me a lot less if they were needed after
            Code:
            ----BEGIN PRIVATE KEY-----
            Regardless - if they need to be incorporated into the string then in RPG you need to add the hex equivalent, For example CR is x'0D' in EBCDIIC so your RPG would be
            Code:
            privateKey = '-----BEGIN PRIVATE KEY-----' + 'X'0D' +
            TheRestOfTheKeyLine1' +
            Just find the required hex codes and add them.

            Comment


            • #8
              A newline inside of a template literal in Node typically results in a linefeed (LF) character (Not a CR.) So I would expect the correct code to be

              Code:
              dcl-c LF x'25';
                :
                :
              privateKey = '-----BEGIN PRIVATE KEY-----' + LF
                         + 'REMOVED/actual/key/but/here/were/the/lines' + LF
                         + 'and/I/used/the/literal/quotes/and/not' + LF
                         + 'the/standard/single/quotes/which/seemed' + LF
                         + 'to/keep/the/line/feels/in' + LF
                         + '-----END PRIVATE KEY-----';

              Comment


              • JonBoy
                JonBoy commented
                Editing a comment
                Yup - CR was just the first character I could remember the hex for.

                Had not looked at node enough to have encountered the idea of template literals so I just read the backticks as the result of a copy/paste "smart quotes" thing. Must remember that oddity in looking at other node examples.

            • #9
              Morning both

              Tried your suggestions and variations of them. Also tried using Qc3CalculateSignature and received pretty much the same error. I'm not great with the whole public/private keys thang and the usage of so am just going to have to find a way to get the process working for now and then see if I can talk the client into having an internal service running python or NodeJS exposed to me so that I can use that via http to sign.

              Thanks muchly for both responding.
              Rob

              Comment


              • #10
                One more thought ... you said earlier that "So I've extract the C and RPG elements out of ILEastic ..." - are you sure that you haven't messed up any of that code? Have you, for example, tried running some other arbitrary characters through your extracted code and compared the result with one of the web JWT calculators, or indeed your node routine?

                Comment


                • #11
                  Hi JB

                  Apologies for not responding sooner. For the moment I'm just getting the thing working and once I have some time after I'll look into whether it's a faux pas on my side.

                  Regards
                  Rob

                  Comment

                  Working...
                  X