Showing posts with label Apex. Show all posts
Showing posts with label Apex. Show all posts

Sunday, June 22, 2014

Apex Data Loader in Linux

Salesforce Apex Data Loader is only officially supported on the Windows platform but the source code is available as an open source project for anyone to build it for another platform such as Linux or Mac. The data loader is written in Java using the Spring Framework so it can be easily rebuilt for any platform that supports the JDK and corresponding Eclipse SWT for GUI. If you don't care much for the GUI to do the mapping and simply want to use it from the command-line, then the good news is that the same JAR file from the Windows installation can be used to run the process in another platform without having to rebuild the code.

What's missing however are the Linux version of the utility scripts (in the bin\ directory in windows installation) to do the password encryption and process execution. I recently had to execute the data loader jobs from Linux and ended up writing the missing scripts so it is easy to mimic the Windows command line process (as explained in Chapter 5 in the guide).

The code is on github and includes the default configuration options and a sample extract process. The directories mirror the Windows version and the following steps walk you through the process:
  1. From your Linux (or Mac) terminal, execute the following command to clone the project from github
     $ git clone https://github.com/sthiyaga/dataloader.git 
  2. Copy the dataloader-30.0.0-uber.jar from the Windows installation to dataloader/ directory (from above)
  3. Generate the private key to encrypt the password 
    $ bin/encrypt.sh -g <some-random-seed-text>
  4. Copy the output from Step 3 above to the file conf/private.key (replacing the text in there)
  5. Encrypt the salesforce password (+security token, if required) using the generated private key 
    $ bin/encrypt.sh -e "password+securitytoken" conf/private.key 
  6. Copy the output from Step 4 above to the conf/config.properties file for the sfdc.password token value
  7. Update the conf/config.properties file with sfdc.username and sfdc.endpoint token values
  8. Optionally, adjust any other default parameters in the conf/config.properties file
  9. Run the sample account extract process 
    $ bin/process.sh csvAccountExtractProcess
That's it, you should have the sample extract in the data/ directory.

Enjoy and feel free to use the code!

-Senthil

Friday, June 6, 2014

Java Encryption, Apex Decryption

Ever had a need to decrypt something in Apex that was encrypted in an external app? Well, I just ran into a need for it where an external Java web app generates a token that is encrypted with AES and when it is sent to Salesforce.com, the token had to be decrypted to do a bunch of stuff. There are a couple of things to note if you have a similar need -- Salesforce supports encryption/decryption using AES algorithm (128 bits, 192 bits and 256 bits) but is particular about the encryption mode and padding. The mode has to be cipher block chaining (CBC) and the padding has to be PKCS5. These are different from what Java supports by default, so it is important to pass the correct settings to initialize the cipher in the encrypting application. Secondly, the initialization vector (IV) used during AES encryption can be provided separately or as part of the encrypted text and the appropriate method in the Crypto class in Apex should be used for decrypting. Refer to the docs for the Crypto class for all the gory details - http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_crypto.htm

Ok, now here is a sample class in Java for generating the AES 128-bit key that will be used by both the web application to encrypt and the Apex code to decrypt the token:

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.xml.bind.DatatypeConverter;

public class AESKeyGen {
public static String generatePrivateKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
return DatatypeConverter.printBase64Binary(secretKey.getEncoded()); 
}
public static void main(String[] args) throws Exception {
String privateKey = generatePrivateKey();
System.out.println("Private Key = " + privateKey);
}
}

When run, this will produce a Base64 encoded AES private key that is 128-bits long. Needless to say, this should be kept private from the prying eyes.  

Private Key = useALumM/MAmHU1+hgsnPg==

Next is the Java class that actually encrypts the given text using this private key. In this case I'm including the IV data as part of the encrypted text by prefixing it before encoding to Base64 format.

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class AESEncrypt {
public static String encodeAES(String privateKey, String data) throws Exception {
final int AES_KEYLENGTH = 128;
byte[] iv = new byte[AES_KEYLENGTH / 8];
SecureRandom prng = new SecureRandom();
prng.nextBytes(iv);
Cipher aesCipherForEncryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aesCipherForEncryption.init(Cipher.ENCRYPT_MODEnew SecretKeySpec(DatatypeConverter.parseBase64Binary(privateKey), "AES"), new IvParameterSpec(iv));
byte[] byteDataToEncrypt = data.getBytes();
byte[] byteCipherText = aesCipherForEncryption.doFinal(byteDataToEncrypt);

byte[] ivPlusCipher = new byte[iv.length + byteCipherText.length];
System.arraycopy(iv, 0, ivPlusCipher, 0, iv.length);
System.arraycopy(byteCipherText, 0, ivPlusCipher, iv.length, byteCipherText.length);
return DatatypeConverter.printBase64Binary(ivPlusCipher);
}
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.out.println("Usage: java AESEncrypt <private-key> <text>");
System.exit(-1);
}

System.out.println("Cipher Text = " + encodeAES(args[0], args[1]));
}
}

Here is a sample execution of the encryption class with the above private key: 

> java AESEncrypt "useALumM/MAmHU1+hgsnPg==" "Hello, (Encrypted) World!"

The output should be something similar to what you see below (it changes with each run as the IV is generated randomly each time) and again, the output includes the IV + Encrypted text in Base64 encoded format. 

Cipher Text = 86z+0A1LKPluwKEmeczUqMYMCLo6BlEOeHwx6zZh/bfsrXR8oRg2Z+csM9UHL59J

Now that we have our private key and encrypted text, the following lines of code will decrypt it in Apex (run through the Developer Console in this case) -- note the use of decryptWithManagedIV method since the encrypted text includes the IV in our case: 

Blob key = EncodingUtil.base64Decode('useALumM/MAmHU1+hgsnPg==');
Blob encData = EncodingUtil.base64Decode('86z+0A1LKPluwKEmeczUqMYMCLo6BlEOeHwx6zZh/bfsrXR8oRg2Z+csM9UHL59J');

Blob decryptedData = Crypto.decryptWithManagedIV('AES128', key, encData);

System.Debug( decryptedData.toString() );

Here is the output (from the debug log): 

18:16:50:025 USER_DEBUG [6]|DEBUG|Hello, (Encrypted) World!

That's it, feel free to reuse the code in your projects as needed. Happy Friday!

-Senthil

Sunday, October 6, 2013

Implementing a Stack in Apex

Apex collections comprise of Set, List and Map types and really nothing else. If you come from a Java or .Net background this must seem quite limiting at first given the variety of collections they support. However, in a metadata driven platform such as force.com you shouldn't have to worry about the nuances of a HashMap versus a Hashtable -- you just use a Map data structure and it is automatically optimized for you by the platform. As much as I like the simplicity of the force.com Apex programming I do miss some of the collection types that I have gotten used to in Java such as TreeMap, TreeSet and Stacks which can simplify the programming quite a bit.

Recently there was a need to implement a trigger logic which although could have been done using a List, was more appropriate for the use of a Stack. So I wrote a basic Stack class and sure enough it made the rest of the implementation a lot cleaner. There are really only three basic operations for a Stack -- push() to add an item on to the stack, pop() to remove the most recent (top most) item from the stack and peek() to take a look at the top most item without removing it from the stack. We can also add a size() method to return the number of items currently in the stack and an isEmpty() method to check if the stack is empty. You could optionally implement an iterator to iterate through the Stack but that is left as an exercise for the reader (no, you can't simply expose the List iterator :).

So here is the classic Stack implemented in Apex using the List.

public class Stack {
    private List<Object> items {get; set;}
    
    public Stack() {
        this.items = new List<Object>();
    }
    
    public Integer size() {
        return this.items.size();
    }

    public Boolean isEmpty() {
        return size() == 0;
    }
        
    public void push(Object itemToPush) {
        this.items.add(itemToPush);
    }
    
    public Object pop() {
        if (isEmpty()) {
            throw new StackUnderflowException();
        }
        
        return this.items.remove(size() - 1);
    }
    
    public Object peek() {
        if (isEmpty()) {
            throw new StackUnderflowException();
        }
        
        return this.items.get(size() - 1);
    }    
}

If we try to peek or pop from an empty stack, we would get an StackUnderflowException, which is a custom exception class: 

public class StackUnderflowException extends Exception {
    /* Custom exception */
}

That's pretty much it! Now you can use it for all sorts of LIFO (Last In First Out) type functions such as evaluating expressions or parsing a syntax tree. 

Stack s = new Stack();
s.push(10.0);
s.push(5.0);
s.push('+');

while (!s.isEmpty()) {
    Object o = s.pop();

    if (String.valueOf(o) == '+') {
        Double d1 = Double.valueOf(s.pop());
        Double d2 = Double.valueOf(s.pop());
        Double result = d1 + d2;
        System.Debug(d1 + ' + ' + d2 + ' = ' + result);
    } 
}

It should print:
5.0 + 10.0 = 15.0

Happy Stacking!