Java Encryption

Encrypting a Jar

 The encryptor application comes in a self-encrypted JAR file called encryptx.jar.  You can run this jar at the command line, passing it the appropriate arguments, to encrypt your own JAR files.

Requirements

 To use this application to encrypt a Java JAR, you must have a valid license on your machine for the "Java Encryptor" product from the Nitro License server. The product code for this is "BtXDiTMEIrTvMSsq9Lr0".

 You must also have Java 1.5 or higher installed on your machine, and in your PATH.  Execute java -version at the command line to see what version of Java you have.

 Before encrypting, an LM product to use for the encryption must be defined in the NitroAdmin tool.  When defining the product, you must create both a license key and an encryption key, then download both to your machine for use by the encryptor.  (See the Execution section below)

Execution

Java JAR files may be run in one of two ways:

encryptx.jar <arguments> 
or 
java -jar encryptx.jar <arguments>

 However, the first method is not recommended because it appears to hide console output which you might need to see.

 Running the command with no arguments will display the command syntax.

java -jar encryptx.jar -p product [-k keydir] [-m mainClass] [-plugin]
                        [-resource] jar-file output-jar 

jar-file        The jar file to be encrypted.
output-jar      The new jar to generate.
-p product      The product code for the product the user will need a license
                for to run the application.  This is a required option.
-k keydir       Location of key files for any keys that need to be included.
                If not specified, the current directory will be assumed.
-m mainClass    The classname or filename of the Java class to be executed,
                if any.
-plugin         Jar will only be used by Eclipse plugins in conjunction with
                the License Management plugin.
-resource       The input file is an executable file rather than a jar file.

The product argument is the Product Code from the product that was defined in NitroAdmin.

The keydir argument is the directory where your downloaded product key files can be found.  If this argument is omitted, the key files will be pulled from the current directory.

The mainClass argument contains the jar's main class name.  This class will be stored in the unencrypted portion of the JAR, so it should only contain the minimal amount of code needed to call a main class in the encrypted block.  This argument should only used if the JAR is meant to be executed as an application; if the JAR is only meant to be a library, then omit this argument.

The -plugin argument indicates that the JAR will be used in an Eclipse plugin which depends on the NitroLM plugin being loaded as well.

The -resource argument indicates that the input file is an executable application file rather than a JAR, so it will be derypted differently.

Sample command line for the encryptor, for encrypting a library jar: 

java -jar encryptx.jar -k c:\workspace\TestApp -p ajg87al34HK087gKE83 c:\workspace\jars\testjar.jar C:\workspace\jars\testjarx.jar  

Accessing encrypted classes

There are two main methods to be aware of:

getTarget(), which loads the encrypted class into memory

invokeTargetMethod(), which calls a method inside the encrypted class. 

To call either method, you must first get an instance of a TargetLoader class using LoaderFactory.getTargetLoader().  This will return an instance of TargetLoader.

getTarget

The getTarget() has these two primary signatures:

  • public Object getTarget(Object delegate, String classname);
  • public Object getTarget(Object delegate, String classname, Object[] args, Class[] argTypes);

The getTarget() method will attempt to find a definition of the class with the given classname inside the encrypted block; if it does not find one there, it will default to the parent classloader to load the class. Once found, the class will be instantiated and returned.

  • If the target class has a setDelegate() method, that method will be called and passed the delegate value.  This allows the encrypted class to know what unencrypted class called it, which may be useful.
  • If both args and argTypes are filled in, then getTarget() will look for a constructor which matches those arguments and use that to instantiate the class.  If it cannot find such a constructor, or if those parameters were not passed, then the class will be instantiated with the no-argument constructor.
invokeTargetMethod

The invokeTargetMethod() has the following signature:

public Object invokeTargetMethod(Object targetClass, String methodName, Object[] args, Class[] argTypes);

The invokeTargetMethod() will look for the method methodName in the class targetClass (which is returned by getTarget()) which has the arguments that match args and argTypes.  If it does not find such a method, it will throw an Exception.  If it does find one, it will call the method and return its return value.

Sample class for calling an encrypted class:

package com.simplifiedlogic.testapp;
 
import com.simplifiedlogic.nitro.loaders.LoaderFactory;
import com.simplifiedlogic.nitro.loaders.TargetLoader;
 
public class TestAppMain {
 
    Object targetClass = null;
 
    private static TargetLoader loader = LoaderFactory.getTargetLoader();
 
    public static void main(String[] args) {
        TestAppMain app = new TestAppMain();
        System.out.println("results: " + app.testFunction("hello there", 12));
        System.out.println("results: " + app.testFunction("goodbye", 13));
    }
 
    public String testFunction(String input1, int input2) {
        Object retval = loader.invokeTargetMethod(
            getTarget(), "testFunction",
            new Object[] {input1, Integer.valueOf(input2)},
            new Class[] {String.class, int.class});
        if (retval!=null)
            return (String)retval;
        return null;
    }
 
    private Object getTarget() {
        if (targetClass==null) {
            targetClass = loader.getTarget(this, "com.simplifiedlogic.test.TestJarFront");
        }
        return targetClass;
    }
}

Sample encrypted class:

package com.simplifiedlogic.test;
 
public class TestJarFront {
 
    public TestJarFront() {
        System.out.println("Default constructor");
    }

 
    public String testFunction(String input1, int input2) {
        return "received: " + input1 + ", " + input2;
    }
}

Writing a main class for an encrypted jar

A main class is only needed if the JAR is meant to be executed as an application; if the JAR is only meant to be a library, then it is not used.

This class will be stored in the unencrypted portion of the JAR, so it should only contain the minimal amount of code needed to call a main class in the encrypted block. 

At a minimum, the main class will need to use the same loader functions as above to load the actuall main class of the application (stored inside the encrypted block).  It may also perform other tasks that for one reason or another (such as the needs of third-party software) must be called outside of the encrypted block.

Sample jar main class


import com.simplifiedlogic.nitro.loaders.LoaderFactory;
import com.simplifiedlogic.nitro.loaders.TargetLoader;
 
public class JarMain {
    private static final Class[] svrParams = {String[].class};
   
    public static void main(String[] args) {
        try {
            TargetLoader loader = LoaderFactory.getTargetLoader();
            Object[] svrArgs = { args };
            loader.getTarget(null, "com.simplifiedlogic.programs.AppStarter", svrArgs, svrParams);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Using this method, the application's main class that is inside the encrypted block must have a constructor which takes a single string-array argument (to match the arguments of a standard main() method).  That constructor should perform the necessary functionality to start the application.  If you want to also be able to run this class from the command line during development, then just include a main() method which calls that constructor.


Sample encrypted main class

package com.simplifiedlogic.programs;

public class AppStarter {

    public static void main(String[] args) {
        new AppStarter(args);
    }

    public AppStarter(String[] args) {
        // run the application
    }

}

An alternative to using the String[] constructor would be to use a default constructor, and to have JarMain use the invokeTargetMethod() call to invoke specific method(s) in AppStarter to start the application.