0

i need to convert the java code to apex code. i tried my best using Crypto class but the result are totally different from the one i get using java code. Below are the two java classes which i need to convert. Please help me to convert this java classes.

class one:

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;


public class AESUtils {
    private static int ENCRYPTION_BITS = 128;


    public static String encryptMessage(String message, String secretKey) {
        try {
            Cipher aesCipher = Cipher.getInstance("AES");
            aesCipher.init(Cipher.ENCRYPT_MODE, createSecretKeyFromString(secretKey));

            byte[] encryptedBytes = aesCipher.doFinal(message.getBytes());

            return Base64.encodeBase64String(encryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }


    private static SecretKey createSecretKeyFromString(String secretKey) {
        byte[] d = Base64.decodeBase64(secretKey);
        SecretKey key = new SecretKeySpec(d, 0, d.length, "AES");
        return key;
    }
}

Class 2:

import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;

public class TokenGenerator {
    private static final String CONDENSED_DATE_FORMAT = "yyyyMMddHHmmssSSSZ";
    public static void main(String [] args){

        if(args.length ==4){
            System.out.println(generateToken(args[0],args[1],Integer.parseInt(args[2]),Integer.parseInt(args[3])));
        }else{
            System.out.println("Please pass following parameters");
            System.out.println("tokenId \nsecretKey \nuses \nttl");
        }


    }

    public static String generateToken(String tokenId, String secretKey,int uses, int ttl) {
        StringBuilder s = new StringBuilder();
        s.append("d=");
        s.append(DateFormatUtils.format(new Date(), CONDENSED_DATE_FORMAT));
        s.append(";");
        s.append("c="+uses);
        s.append(";");
        s.append("t="+uses);

        String payload = s.toString();
        System.out.println("payload", payload);
        return tokenId + ":" + AESUtils.encryptMessage(payload, secretKey);
    }
}

Apex code which i wrote below

String secretKey = 'xxxxxxxxxxxxxxxxxxxxxx==';
            String tokenId = 'xxxxxxxxxxxx';
            Datetime d = System.now();
            String payload = 'd='+d.format('yyyyMMddHHmmssSSSZ')+';c='+10+';t='+10;
            system.debug('payload' + payload);
            String message = tokenId + ':' + encryptMessage(payload, secretKey);

 public static string encryptMessage(String message, String secretKey){
        Blob key = EncodingUtil.base64Decode(secretKey);
        Blob data = Blob.valueOf(message);
        Blob encrypted = Crypto.encryptWithManagedIV('AES128', key, data);
        return EncodingUtil.base64Encode(encrypted);
    }

1 Answer 1

1

One reason the results will be different is that in both examples you are using the current DateTime as an input so those will be different. Instead for your testing hard code the same fixed DateTime in both Java and Apex. Ensure all other inputs are identical too.

Another factor is exactly what Java "AES" means. According to this Java default Crypto/AES behavior (read the various ansers for more information) it is likely:

AES is AES/ECB/PKCS5Padding

but from the Apex Using the Apex Crypto Class.

AES128, AES192, AES256 for encryption. PCKS#5 padding and Cipher Block Chaining.

where one difference is ECB (Electronic Codebook) vs CBC (Cipher Block Chaining). See e.g. Block cipher mode of operation.

If you are in control of the Java code, tie the algorithm used there down to match the Apex algorithm (which is less configurable).

PS

If you are in the situation where Apex doesn't support the algorithm you need, then you will have to introduce a proxy service (hosted on e.g. Heroku) that does the translation. The Apex calls the proxy using CBC, then the proxy translates to ECB to call your target and the reverse translation is done on the way back. Its a chunk of work best avoided if possible but might be necessary in your case.

8
  • thanks but the result are being sent to the third party api to fetch data. since this is used for authorization i don't think date time will be the issue. is there any work around for the apex code? Nov 8, 2016 at 9:10
  • @RitzArlekar The point about the DateTime is that after encryption even a small change to the input will dramatically change the output so for your testing you should ensure the inputs are identical. The Salesforce page I linked to says "The standard AES algorithm is used with a Cipher Mode of Cipher Block Chaining (CBC) and PKCS#5 padding. Ensure that any applications that you interact with use the same parameters." and I haven't found a way to vary that.
    – Keith C
    Nov 8, 2016 at 9:50
  • i checked with hard coding the date with the same string for java application and in sfdc. both the string are different. java always return string of size 64 where as in sfdc it returns a string of 88 characters. i think EncodingUtil.base64Encode(encrypted) is giving me problem while converting. because the length value in encrypted is of 64. is there any other way to convert the 'encrypted' value to string? Nov 10, 2016 at 5:38
  • @RitzArlekar If the Blob contains a string you can turn it into a string using encrypted.toString() so I guess worth a try. But I'd expect that call to fail in this case.
    – Keith C
    Nov 10, 2016 at 9:12
  • i even tried doing that. but i am facing this error 'System.StringException: BLOB is not a valid UTF-8 string' Nov 10, 2016 at 9:18

You must log in to answer this question.

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