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

9
  • Does this answer your question? Generate JWT token for external app
    – identigral
    Dec 19, 2022 at 8:47
  • @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, 2022 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, 2022 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, 2022 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, 2022 at 21:19

1 Answer 1

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/

2
  • 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, 2022 at 0:04
  • Already mentioned above
    – number41
    Dec 24, 2022 at 2:02

Not the answer you're looking for? Browse other questions tagged .