Skip to content

Instantly share code, notes, and snippets.

@karmats
Last active August 9, 2023 08:18
Show Gist options
  • Save karmats/4270441be5a34fff7062 to your computer and use it in GitHub Desktop.
Save karmats/4270441be5a34fff7062 to your computer and use it in GitHub Desktop.
RSA encryption with public key in salesforce apex
public with sharing class RsaEncryption {
private String modulus;
private String exponent;
// Hex digits
private static final String DIGITS = '0123456789abcdef';
private static final Decimal HEX_BASE = 16;
public RsaEncryption(String modulus, String exponent) {
this.modulus = modulus;
this.exponent = exponent;
}
public String encrypt(String input) {
Blob mod = EncodingUtil.base64Decode(modulus);
Blob exp = EncodingUtil.base64Decode(exponent);
// Pad password.nonce
Blob pn = Blob.valueOf(String.fromCharArray(pkcs1Pad2(input, mod.size() - 1)));
Decimal modDec = hexToDecimal(EncodingUtil.convertToHex(mod));
Decimal expDec = hexToDecimal(EncodingUtil.convertToHex(exp));
Decimal pnDec = hexToDecimal(EncodingUtil.convertToHex(pn));
// Calcluate padded^exp % mod and convert to hex
Decimal result = modPow(pnDec, expDec, modDec);
String hexResult = decimalToHex(result);
// If length is uneven, add an extra 0
if ((hexResult.length() & 1) == 1) {
hexResult = '0' + hexResult;
}
// Generate the data to be encrypted.
Blob encodedData = EncodingUtil.convertFromHex(hexResult);
return EncodingUtil.base64Encode(encodedData);
}
@testVisible
private static Decimal hexToDecimal(String hex) {
Decimal result = 0;
integer length = hex.length();
integer i = 0;
while(i < length) {
integer hexByte = DIGITS.indexOf(hex.substring(i, i + 1).toLowerCase());
i++;
result += hexByte * HEX_BASE.pow(length - i);
}
return result;
}
@testVisible
private static String decimalToHex(Decimal d) {
String hex = '';
while (d > 0) {
Decimal digit = modulus(d, HEX_BASE); // rightmost digit
hex = DIGITS.substring(digit.intValue(), digit.intValue() + 1) + hex; // string concatenation
d = d.divide(16, 0, RoundingMode.FLOOR);
}
return hex;
}
// base^exp % mod
@testVisible
private static Decimal modPow(Decimal base, Decimal exp, Decimal mod) {
if (base < 1 || exp < 0 || mod < 1) {
return -1;
}
Decimal result = 1;
while (exp > 0) {
if ((exp.longValue() & 1) == 1) {
result = modulus((result * base), mod);
}
base = modulus((base * base), mod);
exp = exp.divide(2, 0, RoundingMode.FLOOR);
}
return result;
}
// dividend % divisor
@testVisible
private static Decimal modulus(Decimal dividend, Decimal divisor) {
Decimal d = dividend.divide(divisor, 0, RoundingMode.FLOOR);
return dividend - (d * divisor);
}
// Pad using PKCS#1 v.2. See https://en.wikipedia.org/wiki/PKCS_1
// s = String to pad
// n = bytes to fill must be bigger than s.length()
@testVisible
private static List<integer> pkcs1Pad2(String s, integer n) {
// Byte array
List<integer> ba = new List<integer>();
// Fill array with zeros to get the right size
for(integer i = 0; i < n; i++) {
ba.add(0);
}
integer i = s.length() - 1;
while(i >= 0 && n > 0) {
ba.set(--n, s.charAt(i--));
}
ba.set(--n, 0);
while(n > 2) { // random non-zero pad
// Since the array is converted to a string, choose integers that corresponds
// to a proper char code see http://www.asciitable.com
integer rnd = Math.round(Math.random() * (127 - 32) + 32);
ba.set(--n, rnd);
}
ba.set(--n, 2);
ba.set(--n, 0);
return ba;
}
}
@lucastobias
Copy link

How the parameters modulus and exponent works? what are the possible values for them?

@nikitakarpenkov
Copy link

@lucastobias, modulus and exponent are public key components encoded in base64. You need to have a key first.

@nathanhinchey
Copy link

Where did you store your keys?

@murtazaalimir
Copy link

Can someone confirm this is symmetric encryption/decryption or asymmetric?

@SwatiTaunk
Copy link

I am able to get the modulus and exponent from public key and use this class to encrypt the data. Can anyone guide on Decryption using private key?

@pduprejazz
Copy link

Also would like to know how you are pulling the Public key from Key Management. Also Are you able to do RSA Decryption using the Public key shared with a third party. Thanks

@mritzi
Copy link

mritzi commented Aug 28, 2020

I've the public key, using which I retrieved the modulus & exponent (using openssl CLI), but the generated key is not being accepted by the vendor. Getting error : token invalid

Anyone faced the issue of incorrect key generation. As there is no decrypt method in this, I cant verify the reverse, by decrypting the content.

@az-oolloow
Copy link

Anybody got this working such that you could decrypt in javascript/java/.net or something else?

@jmcaudron
Copy link

Not so simple to understand how to use this code. You need to have 2 parameters : modulus and exponent.
Do you have a example explaining the format for these parameters ?

@alex-shekhter
Copy link

I took the gist(s) like that one I implemented RSA OAEP SHA-256 based on it. It has few shell scripts to generate keys and prepare them to be used with Apex. It has a test class with example of how to be used. Hope it will be helpful for the community. One thing to remember is that it takes a lot of CPU for 4K RSA OAEP SHA256 decryption.

https://github.com/alex-shekhter/apex-crypto-rsa-oaep-sha256

@rickywgr
Copy link

rickywgr commented Aug 9, 2023

If I have an RSA public key, how do I get the modulus and exponent? Has anyone successfully implemented RSA public key encryption with this code?

@alex-shekhter
Copy link

openssl rsa -pubin -inform PEM -text -noout < YOUR_PUBLIC_KEY_IN_PEM_FORMAT

@rickywgr
Copy link

rickywgr commented Aug 9, 2023

can i get them in apex code ?

@alex-shekhter
Copy link

alex-shekhter commented Aug 9, 2023 via email

@rickywgr
Copy link

rickywgr commented Aug 9, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment