-
-
Save nikitakarpenkov/7a39c5cd6170dbcac74cf797d03e60f7 to your computer and use it in GitHub Desktop.
| public class RSA { | |
| private Key key; | |
| // Hex digits | |
| private static final String DIGITS = '0123456789abcdef'; | |
| private static final Decimal HEX_BASE = 16; | |
| public abstract class Key { | |
| private String modulus; | |
| public Key(String modulus) { | |
| this.modulus = modulus; | |
| } | |
| } | |
| public virtual class PublicKey extends Key { | |
| private String exponent; | |
| public PublicKey(String modulus, String exponent) { | |
| super(modulus); | |
| this.exponent = exponent; | |
| } | |
| } | |
| public class PrivateKey extends PublicKey { | |
| private String privateExponent; | |
| public PrivateKey(String modulus, String privateExponent) { | |
| super(modulus, null); | |
| this.privateExponent = privateExponent; | |
| } | |
| public PrivateKey(String modulus, String exponent, String privateExponent) { | |
| super(modulus, exponent); | |
| this.privateExponent = privateExponent; | |
| } | |
| } | |
| public RSA(Key key) { | |
| this.key = key; | |
| } | |
| public String encrypt(String input) { | |
| PublicKey publicKey = (PublicKey)this.key; | |
| return modPow(input, publicKey.modulus, publicKey.exponent); | |
| } | |
| public String decrypt(String input) { | |
| PrivateKey privateKey = (PrivateKey)this.key; | |
| return modPow(input, privateKey.modulus, privateKey.privateExponent); | |
| } | |
| public String modPow(String input, String modulus, String exponent) { | |
| 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; | |
| } | |
| } |
Ca you provide any examples of decryption?
I am trying to use this code, but the results are pretty bad until now
Hi @nikitakarpenkov,
I tried the below code for decryption -
String modulus = EncodingUtil.base64Encode(Blob.valueOf(' <modulus> '));
String privateExp = EncodingUtil.base64Encode(Blob.valueOf(' <exp> '));
RSA.PrivateKey pvtKey = new RSA.PrivateKey(modulus,privateExp );
RSA rsaDecrypt = new RSA(pvtKey);
String decryptedText = rsaDecrypt.decrypt( encryptedText );
But I get an error at line 140 - List index out of bounds: -1
Can you pls guide on this issue?
@nicolas1bruno any luck with encryption/decryption?
I have moved to AES
I am using the decrypt method with a radom initializationVector
this guarantees a random answer
the initializationVector needs to be passed foward and back
but the secret key is the same ever
Did you find any solution?
openssl rsa -in private_key.pem -noout -text
returns many exponents; I also am getting the same error
ERROR running force:apex:execute: Class.RSA.pkcs1Pad2: line 140, column 1 Class.RSA.modPow: line 57, column 1 Class.RSA.decrypt: line 48, column 1 AnonymousBlock: line 14, column 1 AnonymousBlock: line 14, column 1
Where did you store your keys?