利用自定义的 ClassLoader 加密 Java Class 文件

原创 2016年05月31日 11:58:34

本文演示利用自定义的 ClassLoader 加密 Java Class 文件


首先,我们定义一个需要被加密的Java Class: classload.MyClassBase。 为了让客户端使用,需要定义一个 MyClassInterface, 这样客户端就不会直接引用 MyClassBase了,发布到客户端的class文件中是不存在 MyClassBase这个类的。


MyClassBase定义:

package classload;

public class MyClassBase implements MyClassInterface {
	public void say() {
		System.out.append("Hello World!");
	} 
}

MyClassInteface 的定义:

package classload;


public interface MyClassInterface {
	public void say();
}


我们把 classload/MyClassBase.class 这个文件进行加密处理, 变成另外一个文件,加密后的文件名可以放到任意位置, 这里我们把它放到 cipher/CipherMyClassBase.class。(CipherMyClassBase.class 就是加密后的文件)

如何加密后面再说, 先看看,客户端是如何使用的:

		Class<?> clz = loader.loadClass("classload.MyClassBase");
		System.out.println("loaded class:" + clz.getName() + " by " + clz.getClassLoader());
		MyClassInterface obj = (MyClassInterface) clz.newInstance();
		obj.say();

这里客户端通过自定义的 ClassLoader 变量名loader, load一个名字叫 "classload.MyClassBase" 的 Class, 通过 newInstance()方法 new 出它的一个obj, 并强制转换成上面定义的接口类型 MyClassInterface。 注意:在客户端代码中是没有 MyClassBase 这个类。自定义的 loader 会 通过指定的 name  参数 “classload.MyClassBase”, 去找到加密后的文件 cipher/CipherMyClassBase.class, 并把它解密,返回 MyClassBase 的class实例。


现在看看 class文件加密、解密的代码:

package classload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MyCipher {

	public static void main(String[] args) {

		String[] srcFileElement = { System.getProperty("user.dir"), "bin", "classload", "MyClassBase.class" };
		enCipherClass(String.join(File.separator, srcFileElement));
	}

	public static String enCipherClass(String path) {
		File classFile = new File(path);
		if (!classFile.exists()) {
			System.out.println("File does not exist!");
			return null;
		}

		String cipheredClass = classFile.getParent() + File.separator + "Cipher" + classFile.getName();
		System.out.println("enCipherClass() cipheredClass=" + cipheredClass);

		try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(classFile));
				BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(cipheredClass));) {

			int data = 0;
			while ((data = is.read()) != -1) {
				out.write(data ^ 0xFF);
			}

			out.flush();
			is.close();
			out.close();
		} catch (IOException e) {

			e.printStackTrace();
		}
		return cipheredClass;
	}

	public static byte[] deCihperClass(String path) {
		File file = new File(path);
		if (!file.exists()) {
			System.out.println("deCihperClass() File:" + path + " not found!");
			return null;
		}

		System.out.println("deCihperClass() path=" + path);

		try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));) {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int data = 0;
			while ((data = in.read()) != -1) {
				out.write(data ^ 0xFF);
			}
			in.close();
			out.flush();
			out.close();

			return out.toByteArray();

		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

}

这里,仅仅是示例,加解密的算法非常简单, 加密算法是把原.class 文件的每一个字节和 0xFF 异或, 对应的解密方法就是 加密后的字节和 0xFF异或,数学原理为: A^B^B = A。 

上面代码中,加密方法enCipherClass 根据输入的XXX.class文件名, 产生一个加密后的CipherXXX.class 文件, 这里把classload\MyClassBase.class  变为 classload\CipherMyClassBase.class。 

解密方法 deCihperClass 把输入的 class 文件, 变为 byte [] 返回。


运行加密算法后,需要手工把  classload\CipherMyClassBase.class 复制到目录 cipher/, 并删除目录 classload\下的 CipherMyClassBase.class  和 MyClassBase.class。

 

自定义ClassLoader的代码:

		ClassLoader loader = new ClassLoader() {
			@Override
			public Class<?> findClass(String name) {

				System.out.println("findClass() name = " + name);

				String baseName = name.substring(name.lastIndexOf('.') + 1);

				String[] fileNameElements = { System.getProperty("user.dir"), "cipher",
						"Cipher" + baseName + ".class" };
				byte[] data = MyCipher.deCihperClass(String.join(File.separator, fileNameElements));

				Class<?> clz = defineClass(name, data, 0, data.length);
				return clz;
			}
		};


这里采用匿名内部类的方式定义自己的 ClassLoader, 定义自己的ClassLoader,只需要继承 ClassLoader, 并覆写方法 findClass 即可。


完整的 客户端代码:

package classload;
import java.io.File;
public class ClassLoadTest {
	public static void main(String[] args) throws Exception {
		ClassLoader loader = new ClassLoader() {
			@Override
			public Class<?> findClass(String name) {

				System.out.println("findClass() name = " + name);

				String baseName = name.substring(name.lastIndexOf('.') + 1);

				String[] fileNameElements = { System.getProperty("user.dir"), "cipher",
						"Cipher" + baseName + ".class" };
				byte[] data = MyCipher.deCihperClass(String.join(File.separator, fileNameElements));

				Class<?> clz = defineClass(name, data, 0, data.length);
				return clz;
			}
		};
		Class<?> clz = loader.loadClass("classload.MyClassBase");
		System.out.println("loaded class:" + clz.getName() + " by " + clz.getClassLoader());
		MyClassInterface obj = (MyClassInterface) clz.newInstance();
		obj.say();
	}
}


最后,需要注意的一点,客户端通过自定义的ClassLoader  像如下代码加载类:

 loader.loadClass("classload.MyClassBase");

自定义的 ClassLoader 会先把 类加载操作先委派给它的parent, 也就是系统默认的类加载器, 如果系统默认的类加载器,找不到  classload.MyClassBase 这个类,才会调用自己的类加载器,如果classpath下有classload.MyClassBase 这个类,系统默认的类加载器就会找到这个类, 那么自己的类加载器是不会调用的,所以前面说过,需要把classload/MyClassBase.class这个文件删除,自己的类加载器才会起作用。

运行代码看输出:

findClass() name = classload.MyClassBase
deCihperClass() path=C:\Users\myname\myworkspace\Demo\cipher\CipherMyClassBase.class
loaded class:classload.MyClassBase by classload.ClassLoadTest$1@6d06d69c
Hello World!

从上面的输出看,使用的类加载器为我们自定的那个:

classload.ClassLoadTest$1@6d06d69c

如果,把MyClassBase.class 放回到 bin/classload/MyClassBase.class, 输出就变了:


loaded class:classload.MyClassBase by sun.misc.Launcher$AppClassLoader@73d16e93
Hello World!

这个时候,使用的类加载器为:

sun.misc.Launcher$AppClassLoader@73d16e93


我的 eclipse目录结构:


只需关注 classload这个package和cipher目录, 其它包与本文无关。





                    
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

如何对java的class类进行加密

class由于可以进行反编译,这样对某个核心的模块来说,会造成关键技术的泄漏。如何对class类进行加密,是某些公司会考虑的问题。以下公布一个实际的类。实现了一个ClassLoader,可以直接操作加...
  • joeyshi
  • joeyshi
  • 2009年04月30日 08:06
  • 14023

java-对密码进行加密和验证的类

java 密码MD5加密 java加密bytestring算法null package com.sunnylocus.util;      import java.security.Messa...

Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密

二、利用加密算法DES实现java代码加密         传统的C/C++自动带有保护机制,但java不同,只要使用反编译工具,代码很容易被暴露,这里需要了解的就是Java的ClassLoad...
  • educast
  • educast
  • 2015年06月17日 22:40
  • 10684

javaee加密,tomcat使用自己的classloader解密

【起因】 公司需要对一个web项目进行加密之后出售, 大家都知道的,class很好反编译, 所以需要对class文件先进行加密, 然后使用自己的classloader进行解密并加载。 【步骤】 大...
  • uikoo9
  • uikoo9
  • 2013年12月11日 09:26
  • 7685

java的jar包加密及使用

由于项目要求(虽然我觉得代码没什么机密可言...),写好的jar包需要做一定加密处理 这里提供两种办法,一种奇葩,一种通用 1. 直接修改jar文件: 具体步骤:   在代码中插入一段不会运行...

java加密class和解密class加载运行

package csdn.util; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileIn...
  • hfmbook
  • hfmbook
  • 2012年06月11日 14:49
  • 6050

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

自定义classload加载自定义的class文件

大家知道,编译后的class文件如果不经过特殊处理,很容易被人反编译。 本文只是一个简单的例子。对class文件进行简单的加密。通过自定义的classload解密该class文件, ...
  • rznice
  • rznice
  • 2015年03月14日 16:00
  • 949

一个自定义类加载器ClassLoader示例

我们的自定义类加载器
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:利用自定义的 ClassLoader 加密 Java Class 文件
举报原因:
原因补充:

(最多只允许输入30个字)