Salesforce integrations can be tricky every so often. For example, one of our customers had to connect a system that required the use of JSON Web Tokens with an RSA-SHA512 signature.

Currently the Crypto class doesn’t support RSA-SHA512 signatures.
(Not to be confused with 512-bit digests and HMAC functions which are both supported)

One workaround is to implement the RSA cryptography in Apex. Let’s see how the Apex language enables us to express a wide variety of solutions, even at the bare metal if needed.

At its core, RSA (invented by Rivest / Shamir / Adleman) is a cryptosystem based on a mathematical trick and involves several moving parts. Apex source code on GitHub.

  1. A private key file holds secret prime numbers
  2. An ASN key reader handles the binary file format
  3. The message to sign consists of a JSON Web Token
  4. Then a SHA512 hash reduces the message to a big integer
  5. Finally the RSA signer executes the modPow signature math

To succeed, we must implement each logical part in Apex then make it performant. A similar look and feel to the platform Crypto class can be achieved starting with pseudocode like this:

Blob sign(Blob inputMessage, String privateKeyFile)
    ASN reader = new ASN(privateKeyFile);
    RSA signer = new RSA(reader.values());
    return signer.sign(SHA512(inputMessage));

Step 1) Private Key File

The signing process always starts with an RSA Private Key. For example, this RSA tool generates a binary representation of a private key wrapped in a header / footer. This is called a PEM file and it is base64 encoded to simplify transport using electronic mail or your clipboard:


To work with individual bytes, convert the data into hexadecimals:

String b64 = 'MIICXAIB...';
Blob binary = EncodingUtil.base64decode(b64);
String hex = EncodingUtil.convertToHex(binary);

Now with each byte seen as a hex pair, structure emerges:


Step 2) Abstract Syntax Notation – ASN.cls

The private key file is not just a random seed. It holds 9 pieces of content in Tag-Length-Value encoding per Abstract Syntax Notation One. Reading each hex pair (or byte) as a string, Apex will extract the critical prime numbers (P, Q, etc) described in the RSA specification.

  • Tag byte
  • Length byte
  • Contents bytes

The first tag tells us the data holds a list of values called a sequence:

30Sequence82025C﹡604 bytes

﹡When content length exceeds 128 bytes (hex 80) the length of the length is also given.
Here, hex 82 means a 2-byte length, then 025C means the sequence is 604 bytes long.

Let’s tabulate all the subsequent values contained inside the sequence:

02Integer011 byte00Version: V
02Integer8181﹡129 bytes00A92302…RSA modulus: N
02Integer033 bytes010001RSA public exponent: E
02Integer8181﹡129 bytes00A2CC60…RSA private exponent: D
02Integer4165 bytes00DFD036…Prime1: P
02Integer4165 bytes00C175DA…Prime2: Q
02Integer4064 bytes7EC1D0A9…Dp Exponent1: D mod (P-1)
02Integer4064 bytes4FF87AE9…Dq Exponent2: D mod (Q-1)
02Integer4064 bytes1BA12159…Coefficient: Qinv mod P

﹡When content length exceeds 128 bytes (hex 80) the length of the length is also given.
Here, hex 81 means a 1-byte length, then 81 means the integer is 129 bytes long. Not bits.

Step 3) JWT Message – Tutorial

JSON Web Tokens exist to prove the integrity of an API request: only the private key holder can sign tokens and issue valid requests. Each token consists of three parts: Header / Payload / Signature. The special values are covered in depth in the tutorial.

Combine the Header and Payload to prepare the message:

String header = '{"alg":"RS512","typ":"JWT"}';
String payload = '{"iat":1581009850,"exp":1581011650}';
String message = base64url(header) + '.' + base64url(payload);

Note the base64 URL variant strips trailing = padding, swaps + for -, and / for _ according to the JWT spec. This avoids issues if intermediate systems use tokens as filenames.

Step 4) Hash Function – SHA512.cls

Hashing the message ensures the input to the signature math is a predictable length. Else the signature math would become more and more expensive with each byte of data in the token.

Blob hashedMessage = Crypto.generateDigest('SHA-512', Blob.valueOf(message));

Step 5) Signature – RSA.cls

The original concept of modular exponentiation underlying RSA was described in 1977 and (assuming small prime numbers) can be executed with pen and paper by hand in 10 minutes:

Signature = CD mod P×Q (for message C, private exponent D, primes P and Q)

In real situations, this modular exponentiation gets computationally expensive and exceeds the Apex CPU limit without using a specific optimization: the Chinese Remainder Theorem.

It isn’t the end of the road. Most crypto libraries use the CRT optimization. In fact, its use is so common that ALL private keys hold 5 extra values, precomputed to help implement CRT. The implementation in Apex can be seen as a direct parallel of the algorithm from Wikipedia:

Chinese remainder theorem (Wikipedia)Apex implementation in RSA.cls
m1 = CDp mod P
m2 = CDq mod Q
h = Qinv (m1 – m2) mod P
m = m2 + HQ mod PQ
M1 = C.powMod(Dp, P);
M2 = C.powMod(Dq, Q);
H = Qinv.multiply(M1.subtract(M2)).mod(P);
M =  M2.add(H.multiply(Q).mod(P.multiply(Q)));

Big Integer math class – BigInt.cls

All the aforementioned math must work with big integers. The calculations must be exact and this is where the real challenge lies. Back in Step 2 you probably spotted the 129-byte value in the private key. That number has 300+ digits while the maximum length of any number in Apex is 19 digits. This isn’t a shortcoming of Apex – most languages have the same constraint. An extra class handles the big integers, representing them as lists of smaller integer primitives.


This solution stands of the shoulders of a number of people who provided reference implementations and ideas. We wish to express appreciation for their published work:

Related Posts

Last modified: 2nd March 2020


Write a Reply or Comment

Your email address will not be published.