0

I am trying to generate a JWT token that has to be given to another system. I have followed the below steps for doing so:

  1. Created a Connect App. I have got consumer key using this App.
  2. Created a self signed certificate. Downloaded the certificate(crt) file. Converted the crt file into key file using openSSL. I used command:
openssl req -newkey rsa:2048 -nodes -keyout SSO_Self_Signed.key -out SSO_Self_Signed.csr

SSO_Self_Signed is a label name of my certificate. The generated key file has private key in between -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- 3. I have created an APEX class that will generate JWT token using the creds we obtained in STEP 1 and STEP 2. The code snippet of my code is also mentioned below:

jwtToken is generated on line 46. When I am trying to validate the JWT in https://jwt.io/, I am getting invalid signature and I am not able to figure out the exact cause of the issue.

Invalid Signature error when JWT is validated

The inputs passed to the class includes - iss:mailaddress; sub:other system url; aud: consumer key from step1; privateKey: key from step2.

public class SSOJWTGenerator {
    public static String generateJWT(String iss, String sub, String aud, String privateKey) {
        String alg = 'RS256';
        String typ = 'JWT';

        // Create the JWT header
        Map<String, Object> header = new Map<String, Object>{
            'alg' => alg,
            'typ' => typ
        };
        String encodedHeader = base64UrlEncode(JSON.serialize(header));

        // Create the JWT claim set
        Map<String, Object> claimSet = new Map<String, Object>{
            'iss' => iss,
            'sub' => sub,
            'aud' => aud,
            'exp' => String.valueOf(DateTime.now().getTime()/1000 + 300),
            'iat' => String.valueOf(DateTime.now().getTime()/1000)
        };
        String encodedClaimSet = base64UrlEncode(JSON.serialize(claimSet));

        // Create the signature
        String input = encodedHeader + '.' + encodedClaimSet;
        
        privateKey = privateKey.replace('-----BEGIN PRIVATE KEY-----', '');
        privateKey = privateKey.replace('-----END PRIVATE KEY-----', '');
        privateKey = privateKey.deleteWhitespace();
        
        Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
        Blob signatureBlob = Crypto.sign('RSA-SHA256', Blob.valueOf(input), privateKeyBlob);
        String signature = base64UrlEncode(signatureBlob);

        // Combine the header, claim set, and signature to create the JWT token
        String jwtToken = encodedHeader + '.' + encodedClaimSet + '.' + signature;
        System.debug('jwtToken'+ jwtToken);
        return jwtToken;
    }

    private static String base64UrlEncode(String input) {
        // Replace + with -, / with _, and remove any trailing = signs
        String base64 = EncodingUtil.base64Encode(Blob.valueOf(input));
        base64 = base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
        return base64;
    }

    private static String base64UrlEncode(Blob input) {
        // Replace + with -, / with _, and remove any trailing = signs
        String base64 = EncodingUtil.base64Encode(input);
        base64 = base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
        return base64;
    }
}
jps
  • 20,041
  • 15
  • 75
  • 79
  • On the right column of your jwt.io screenshot, I can see that the field for the public key is empty. Without knowing the public key, jwt.io can't verify the signature. Paste your public key there, then it should work. – jps Feb 13 '23 at 10:06
  • @jps how can I add the public key here? Will it go in header, payload or signature? Also where can I get a public the key? – user15754268 Feb 13 '23 at 16:54
  • I think you might have misunderstood this. I mean you should add the public key on the jwt.io website in the right column. On your screenshot, you can see the two fields on the right sides, the upper one for the public key. That's where you have to paste it, so that jwt.io can verify the token. And you can generate the public key from your private key: [Use RSA private key to generate public key?](https://stackoverflow.com/q/5244129) – jps Feb 13 '23 at 17:08
  • @jps I am looking on way we do it using APEX instead of openSSL. – user15754268 Feb 13 '23 at 17:23
  • @jps WIll I only need the public key for JWT.io and not in my APEX case? I was able to get the public key and could see signature verified. I used this openSSL command to get the public key: ```openssl rsa -in SSO_Self_Signed.key -pubout -outform PEM -out SSO_Self_Signed.pub``` – user15754268 Feb 13 '23 at 17:42
  • You will generally need the public key, wherever you want/need to verify the token. The token was signed with a private key and can only be verified with the public key. – jps Feb 13 '23 at 18:05
  • Thanks @jps Also on the private key part. Do I need to pass the whole private key for signature? I mean along with ```-----BEGIN PRIVATE KEY-----``` and ```-----END PRIVATE KEY-----```. Currently I am removing this part from the key. Will I have to send it in sign method? – user15754268 Feb 13 '23 at 18:41
  • I don't know in which exact format the framework that you use here needs the private key. But usually, you should get an exception or error code if the import of the key fails. If you get a signed token as a result the key was most probably right. Can't say much more, because I'm not familiar with Salesforce and Apex. – jps Feb 14 '23 at 22:31

0 Answers0