7

So I am struggling a bit to convert some code written in java to APEX. The struggle here is that I have an integer array as one of my encryption key, it looks something similar to this below:

[242, 2, 56, 23, 15, 5, 12, 105, 19, 47, 116, 89, 25, 152, 200, 215]

and in java I convert it to 128 bytes

[-14, 2, 56, 23, 15, 5, 12, 105, 19, 47, 116, 89, 25, -104, -56, -41]

and then to Base64 I get 8gI4Fw8FDGkTL3RZGZjI1w== that is then used as the IvParameterSpec() in the jave Crypto package.

However, I can't figure out to save my life how to do that in apex? The integer array will be an initialization vector for "AES128" encryption.

I tried this:

List<Integer> values256 = new List<Integer>{242, 2, 56, 23, 15, 5, 12, 105, 19, 47, 116, 89, 25, 152, 200, 215};
List<Integer> values128 = new List<Integer>();
    for(Integer bytes : values256){

        if(bytes > 127){
            values128.add((256-bytes)*-1);
        }else{
            values128.add(bytes);
        }   
    }



String encodedString = String.fromCharArray(values128);
String myBase64String = EncodingUtil.base64Encode(Blob.valueof(encodedString));
System.Debug(values128);
System.Debug(myBase64String);

But my base64 string looks like this: 77+yAjgXDwUMaRMvdFkZ776Y77+I77+X which is very different from what I get in Java. Any idea on what I am doing wrong?

EDIT: I wanted to add that I am really new to the world of encryption so I may be missing some obvious fundamentals! So feel free to correct me...

6
  • The last two == in the Java string are padding, so the result strings probably aren't the same length. Could you post your Java code? Commented Nov 11, 2016 at 16:26
  • @IllusiveBrian just added it
    – Tekill
    Commented Nov 11, 2016 at 16:35
  • @IllusiveBrian any suggestions?
    – Tekill
    Commented Nov 11, 2016 at 17:07
  • As sfdcfox said, you'd probably need to roll your own conversion to get the exact same result, but if you're just trying to generate an IV for internal use, does it really matter whether the result is exactly the same as the python (?) code you're transcribing? If it needs to match an external codebase, can you modify the codebase's result to match? Commented Nov 11, 2016 at 17:21
  • The IV is not for internal use is to access a third-party service that I don't manage, they provide me the key and the shared key
    – Tekill
    Commented Nov 11, 2016 at 17:29

2 Answers 2

5

String.fromCharArray is going to create a UTF-8 encoded string, which will alter the bytes in the stream. For example, 242 will be encoded as two bytes instead of one. Any value outside 0-127 will be encoded as multiple bytes when using this function, including all negative integers. Since we can't really work with binary data directly in Apex Code, just store the base-64 encoded value directly in a string or some persistent storage (e.g. a custom setting).


public class Base64 {
    static String[] codes = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');

    // Encodes integers 0-255 into base 64 string
    public static String encode(Integer[] source) {
        // Preallocate memory for speed
        String[] result = new String[(source.size()+2)/3];
        // Every three bytes input becomes four bytes output
        for(Integer index = 0, size = source.size()/3; index < size; index++) {
            // Combine three bytes in to one single integer
            Integer temp = (source[index]<<16|source[index+1]<<8)|source[index+2];
            // Extract four values from 0-63, and use the code from the base 64 index
            result[index]=codes[temp>>18]+(codes[(temp>>12)&63])+(codes[(temp>>6)&63])+codes[temp&63];
        }
        if(Math.mod(source.size(),3)==1) {
            // One byte left over, need two bytes padding
            Integer temp = (source[source.size()-1]<<16);
            result[result.size()-1] = codes[temp>>18]+(codes[(temp>>12)&63])+codes[64]+codes[64];
        } else if(Math.mod(source.size(),3)==2) {
            // Two bytes left over, need one byte padding
            Integer temp = (source[source.size()-2]<<16)|(source[source.size()-1]<<8);
            result[result.size()-1] = codes[temp>>18]+(codes[(temp>>12)&63])+(codes[(temp>>6)&63])+codes[64];
        }
        // Join into a single string
        return String.join(result, '');
    }
}
5
  • So are you saying that there is no way for me to take the integer array and process in apex to give me the key in terms of a base64 string?
    – Tekill
    Commented Nov 11, 2016 at 16:34
  • or I guess I would have to implement my own dictionary?
    – Tekill
    Commented Nov 11, 2016 at 16:37
  • @Tekill You could take the integer array and do so, but not using the library. You'd have to essentially write a intArray2Base64 function using bit operators like left-shift, right-shift, bit-and, etc.
    – sfdcfox
    Commented Nov 11, 2016 at 16:37
  • any tutorial or literature I can read up on to do something like that? I am not sure I have a clear path on how to do so at the moment
    – Tekill
    Commented Nov 11, 2016 at 17:07
  • @Tekill I found some old code I had laying around, so I've added it to the answer for you. Please let me know if it's explanatory enough.
    – sfdcfox
    Commented Nov 11, 2016 at 22:23
2

I took the code from @sfdcfox to use in my own project, and was getting out an almost correct answer. I tweaked the code, and just wanted to post it here for anyone else that might want it.

public class Base64 {
    static String[] codes = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');

    // Encodes integers 0-255 into base 64 string
    public static String encode(Integer[] source) {
        Integer length = source.size();
        // Preallocate memory for speed
        String[] result = new String[(length+2)/3];
        // Every three bytes input becomes four bytes output
        Integer size = length - Math.mod(length, 3);
        Integer resultIndex = 0;
        for(Integer index = 0; index < size;) {
            // Combine three bytes in to one single integer
            Integer temp = (source[index++]<<16|source[index++]<<8)|source[index++];
            // Extract four values from 0-63, and use the code from the base 64 index
            result[resultIndex++]=codes[temp>>18]+(codes[(temp>>12)&63])+(codes[(temp>>6)&63])+codes[temp&63];
        }
        if(Math.mod(length,3)==1) {
            // One byte left over, need two bytes padding
            Integer temp = (source[length-1]<<16);
            result[result.size()-1] = codes[temp>>18]+(codes[(temp>>12)&63])+codes[64]+codes[64];
        } else if(Math.mod(source.size(),3)==2) {
            // Two bytes left over, need one byte padding
            Integer temp = (source[length-2]<<16)|(source[length-1]<<8);
            result[result.size()-1] = codes[temp>>18]+(codes[(temp>>12)&63])+(codes[(temp>>6)&63])+codes[64];
        }
        String base64Encoded = String.join(result, '');
        // Join into a single string
        return EncodingUtil.base64Decode(base64Encoded);
    }
}

You must log in to answer this question.

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