实现AES解密的类加载器

需求

  • 一些场景,为了防止.class文件被反编译得到源代码,需要对.class文件做加密处理

设计

  1. 编译生成.class文件,如:HelloWorld.class
  2. 使用AES加密工具,加密.class文件,得到.encrypt文件,如HelloWorld.encrypt
  3. 项目启动时,先使用应用类加载器加载类,如果加载失败,使用AES类加载器加载.encrypt文件,得到Class

实现

AESClassLoader

public class AESClassLoader extends ClassLoader {
    final String keyFileName = "classpath:aes.key";
    byte[] key;
    DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
    final String encryptedSuffix = ".encrypt";
    final String classPathPrefix = "classpath:";
    final Logger logger = LoggerFactory.getLogger(AESClassLoader.class);

    public AESClassLoader() throws IOException {
        key = AESUtils.loadkey(keyFileName);
    }

    /**
     * @param name 类的完全限定名
     * @return
     */
    @Override
    public Class<?> loadClass(String name) {
        try {
            //使用父类加载器加载Class
            return super.loadClass(name);
        } catch (Exception e) {
            logger.debug("AESClassLoader的父类加载器加载:{},失败,使用AESClassLoader加载", name);
            //从加密的资源加载Class
            return loadClassFromEncryptedResource(name);
        }
    }

    /**
     * 从加密资源加载Class
     *
     * @param name
     * @return
     */
    private Class<?> loadClassFromEncryptedResource(String name) {
        //获取加密的资源文件
        Resource resource = getEncryptedResource(name);
        try {
            InputStream inputStream = resource.getInputStream();
            byte[] bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            //解密加密字节
            byte[] decryptedBytes = AESUtils.decrypt(bytes, key);
            return defineClass(null, decryptedBytes, 0, decryptedBytes.length);
        } catch (IOException e) {
            logger.debug("查找资源:{},失败,原因:{}", name, e.getMessage(), e);
            throw new RuntimeException(String.format("查找资源:%s,失败", name), e);
        }
    }

    /**
     * 获取加密的资源
     * 如:
     * - com.ldh.classloader.HelloWorld
     * - classpath:com/ldh/classloader/HelloWorld.encrypt
     *
     * @param binaryName
     * @return
     */
    private Resource getEncryptedResource(String binaryName) {
        String[] nameArr = binaryName.split("\\.");
        StringBuilder classPathBuilder = new StringBuilder();
        //获取加密文件名称
        classPathBuilder.append(classPathPrefix);
        if (nameArr.length > 1) {
            for (int i = 0; i < nameArr.length; i++) {
                classPathBuilder.append(nameArr[i]).append("/");
            }
            //删除最后一个"/"
            classPathBuilder.deleteCharAt(classPathBuilder.length() - 1);
            classPathBuilder.append(encryptedSuffix);
            Resource resource = resourceLoader.getResource(classPathBuilder.toString());
            return resource;
        } else {
            throw new RuntimeException(String.format("给定的名称:%s,无效", binaryName));
        }

    }
}
  • 重写loadClass():使得加密的文件直接由AESClassLoader加载,其余的.class由应用类加载器加载

测试

	@Test
    public void loadHelloWorldTest() throws IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        AESClassLoader aesClassLoader = new AESClassLoader();
        Class<?> helloWorldClass = aesClassLoader.loadClass("com.ldh.classloader.HelloWorld");
        Object obj =helloWorldClass.newInstance();
        //对象可以实例化,但是没办法转换成具体的类型,如HelloWorld
        System.out.println("obj Class:"+obj.getClass());
        System.out.println("obj ClassLoader:"+obj.getClass().getClassLoader());
        System.out.println("parent of obj ClassLoader:"+obj.getClass().getClassLoader().getParent());
        System.out.println("HelloWorld Class:"+ HelloWorldUse.class);
        System.out.println("HelloWorld ClassLoader:"+ HelloWorldUse.class.getClassLoader());
        System.out.println("parent of HelloWorld ClassLoader:"+ HelloWorldUse.class.getClassLoader().getParent());

        //因为在编译阶段并不能确定具体的Class,仅能利用反射调用
        Method method = helloWorldClass.getMethod("sayHello");
        method.invoke(obj);
    }

输出

obj Class:class com.ldh.classloader.HelloWorld
obj ClassLoader:com.ldh.ch9.classloader.AESClassLoader@dd3b207
parent of obj ClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
HelloWorld Class:class com.ldh.classloader.HelloWorldUse
HelloWorld ClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
parent of HelloWorld ClassLoader:sun.misc.Launcher$ExtClassLoader@4cb2c100
say: Hello World

posted on 2022-08-21 10:32  DaydayupLiu  阅读(53)  评论(0编辑  收藏  举报