1

The following should be immensely helpful for people looking to construct JWTs in Apex in a much simpler way than provided in many online examples:

I've been going through the joys of learning to construct a JWT token in Apex, and I found only a couple examples which seemed helpful, though always seemingly overlong and overcomplicated, but in using their methodologies I am still just getting the error "FATAL_ERROR System.SecurityException: Invalid Crypto Key".

I don't have anything to go off of other than that. Anyone spot it below? This is for Snowflake btw.

NOTE: In the example below, I am getting "privateKey" from a rsa_key.p8 file. I also have a rsa_key.pub file, but that is not used in the Crypto.sign() method. I have tried using the text string directly in code, and uploading as a static resource. Since it begins with "-----BEGIN ENCRYPTED PRIVATE KEY-----", it appears that must be removed, otherwise the required EncodingUtil.base64Decode fails with it.

    String privateKeyPKCS8 = 'MIIJQwIBADhutg4078gf2fg2f23goesonforeverandamen...';
Long iat = DateTime.now().getTime();    
Long exp = DateTime.now().addMinutes(60).getTime();      

String base64header = EncodingUtil.base64Encode(blob.valueof('{"alg": "RS256","typ":"JWT"}'));
    //system.debug('base64header = '+base64header); //returns static value of: eyJhbGciOiAiUlMyNTYiLCJ0eXAiOiJKV1QifQ

String base64payload = EncodingUtil.base64Encode(blob.valueof('{"iss":"NAME-FQA12325.SFDC_INTEGRATION_USER.SHA256:JUNUEuibBIBU787Gug78g7g9g7RqgF/3LhEU=","sub":"NAME-FQA12325.SFDC_INTEGRATION_USER","iat":'+iat+',"exp":'+exp+'}'));           
    //system.debug('base64payload = '+base64payload);     

String base64signature = EncodingUtil.base64Encode(Crypto.sign('RSA-SHA256', Blob.valueof(base64header +'.'+base64payload), EncodingUtil.base64Decode(privateKeyPKCS8)) );      
    //system.debug('signature = '+base64signature);

String FinalJWT = base64header+'.'+base64payload+'.'+base64signature;

----------EDIT------------: As mentioned below, these two questions below are similar, but do not address how to troubleshoot "FATAL_ERROR System.SecurityException: Invalid Crypto Key", and do not outline how a Named Credential can be used with a rsa_key.p8 and rsa_key.pub file. The trailhead link is good theoretical knowledge but does not provide direction on how to accomplish the above in any code or otherwise

Generate JWT token for external app

Named Credentials - What is the difference between JWT & JWT Token Exchange

number41
  • 948
  • 2
  • 11
  • 29
  • @identigral thanks but no, I saw that post, but I still receive "FATAL_ERROR System.SecurityException: Invalid Crypto Key" - question is, why? – number41 Dec 19 '22 at 16:11
  • Likely because of the private key format. If you change your code to use out of the box method as referenced in the above Q&A and you're still getting an error, feel free to update your question with these details. – identigral Dec 19 '22 at 16:28
  • @identigral when you say “out of the box method“, are you referring to a named credential? I did look into that, but the named credential requires a certificate I do not have, it does not look like it supports the authentication I am doing now – number41 Dec 19 '22 at 21:09
  • You have the public key somewhere. RSA is an asymmetric crypto scheme, it requires private and public key as a keypair. Once you find your keypair in the required format, you'll be able to upload it and use the out of the box method. Why implementing your own signature instead of using a platform library is a bad idea – identigral Dec 19 '22 at 21:19
  • @identigral So, I'm still not sure if you are referring to Named Credentials or not. Are you? When you say "upload it", upload it to where? I have the public and private keys, but I can't follow you without any specificity. Appreciate you replying though. – number41 Dec 20 '22 at 00:15
  • The referenced answer shows two methods: one with Named Credentials and one without. Both methods require you to upload the keypair to Salesforce's key vault. Please try it, then edit your answer with the results of your attempt. – identigral Dec 20 '22 at 02:09
  • @identigral I do not see a method being suggested without a Named Credential, and I don't see anything describing how the keypair can be uploaded for the Named Credential. For the code samples I do see in your links, I have tried these methods, including uploading the rsa_key.p8 file as a static resource, which only yields "Unrecognized base64 character:" when it is passed through EncodingUtil.base64Decode. If you having any specific code suggestions to try, I would be happy to. – number41 Dec 20 '22 at 04:08

1 Answers1

0

Here's the helpful answer for anyone else struggling - I was told the private key in my rsa_key.p8 file was in PKCS8 format, but apparently it was not, but fortunately it was very easy to convert it using the line below in Terminal. There was no need to use a named credential or upload anything to a key vault (which I imagine would not help anyway if the key was still in the wrong format).

openssl pkcs8 -topk8 -nocrypt -in /Users/bob/Desktop/rsa_key.p8 -outform PEM

This was only discovered through extensive searching and the items below, it would be nice if SF provided this little suggestion for verifying the PKCS8 format required by Crypto.sign().

An important point that is not made clear is that there are different types of JWT tokens, and having public and private RSA tokens requires you to generate the token using Crypto.sign(), but other types may require different methods like Crypto.generatemac(), so that adds to the confusion.

Otherwise, the code in the description above is correct if you are using RSA tokens.

How to create a signature using crypto class

https://blog.jeffdouglas.com/2010/07/06/using-rsa-sha1-with-salesforce-crypto-class/

number41
  • 948
  • 2
  • 11
  • 29
  • From the doc: The value of privateKey should be in RSA's PKCS #8 (1.2) Private-Key Information Syntax Standard form . The shape/type/signature/etc of JWT depends on the recipient. There are many flavors and they're described in JWT family of RFCs (7519,7515). Last but not least, exposing a private key outside of a Named Credential or key vault is a significant security risk. – identigral Dec 24 '22 at 00:04
  • Already mentioned above – number41 Dec 24 '22 at 02:02