views:

310

answers:

1

hi all. i want to implement a custom classloader in order to digitally signing my jar files. because of performance issues i don't want to encrypt all of my classes. so i want implement a custom classloader which when it has been called it delegates class to it's parent and if the parent failed to load class it handles by itself. this is my code:

package org.dpdouran.attach;
import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.spec.AlgorithmParameterSpec;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class CustomClassLoader extends ClassLoader {
    private static final int BUFFER_SIZE = 8192;
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("loading...  "+className);
        String clsFile = className.replace('.', '/') + ".class";
        InputStream in = getResourceAsStream(clsFile);
        if(in==null)
            return null;
        byte[] buffer = new byte[BUFFER_SIZE];
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int n = -1;
        try {
            while ((n = in.read(buffer, 0, BUFFER_SIZE)) != -1) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        //do decrypt
        byte[] classBytes = out.toByteArray();
        byte[] iv = new byte[] { (byte) 0x8E, 0x12, 0x39, (byte) 0x9C,
                0x07, 0x72, 0x6F, 0x5A };
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        Cipher dcipher=null;
        try {
            dcipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte keyBytes[] = "abcdEFGH".getBytes();
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "DES");
        try {
            dcipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] dbytes = null;
        try {
            dbytes = dcipher.doFinal(classBytes);
        } catch (IllegalBlockSizeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return defineClass(className, dbytes, 0, dbytes.length);
    }
    public CustomClassLoader( ClassLoader parent){
        super(sun.misc.Launcher.getLauncher().getClassLoader());
    }
}

also i have changed default system classloader by changing "java.system.class.loader" property. when i running my application it works fine with unencrypted class but when it reaches to encrypted class it throws ClassFormatError and my findClasses method is never called! how can i sure that my custom classloader will be called on encrypted classses? thanks

A: 

Try setting classloader programatically and look if it works

Thread.currentThread().setContextClassLoader(myClassLoader);

When I was implementing my classloader I implemented loadClass method instead of findClass. Try to do something like

try {
  return super.loadClass(name);
catch (Exception e) {
     ... your code here
}

On the other hand documentation says not to override loadClass but findClass.

//Edit

Another idea.

It looks like you keep your encrypted classes with not encrypted .class files. So it looks like parent classloader finds proper .class file and never calls your findClass. But the format of class file is wrong, because it's encrypted. The sequence is my.loadClass->parent.loadClass->parent.loadClass ... ->my.findClass

So you must be sure that parent classloader won't find the class. Just rename encrypted classes - give them another extension or keep them in separate folder.

That should do the trick

peperg
i have tried this but by doing so i can not catch the ClassFormatError exception ! it throws a way!
arash
look at the //Edit part above
peperg
Changing the extension of encrypted classes will probably suffice.
rsp
i have tried change of extension and overriding loadClass method but it does not work it throwsCaused by: java.lang.ClassNotFoundException: oracle.i18n.text.converter.CharacterConverterOGS at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 27 moreand it never calls my code!! :(is any help?
arash
can any one help?
arash
try something like : Class myClass = new MyClassloader().loadClass("playground.MyClass"); System.out.println(myClass.newInstance());
peperg