利用自定义的 ClassLoader 加密 Java Class 文件
2016-05-31 11:58
579 查看
本文演示利用自定义的 ClassLoader 加密 Java Class 文件
首先,我们定义一个需要被加密的Java Class: classload.MyClassBase。 为了让客户端使用,需要定义一个 MyClassInterface, 这样客户端就不会直接引用 MyClassBase了,发布到客户端的class文件中是不存在 MyClassBase这个类的。
MyClassBase定义:
MyClassInteface 的定义:
我们把 classload/MyClassBase.class 这个文件进行加密处理, 变成另外一个文件,加密后的文件名可以放到任意位置, 这里我们把它放到 cipher/CipherMyClassBase.class。(CipherMyClassBase.class 就是加密后的文件)
如何加密后面再说, 先看看,客户端是如何使用的:
这里客户端通过自定义的 ClassLoader 变量名loader, load一个名字叫 "classload.MyClassBase" 的 Class, 通过 newInstance()方法 new 出它的一个obj, 并强制转换成上面定义的接口类型 MyClassInterface。 注意:在客户端代码中是没有 MyClassBase 这个类。自定义的 loader 会 通过指定的 name 参数 “classload.MyClassBase”, 去找到加密后的文件 cipher/CipherMyClassBase.class,
并把它解密,返回 MyClassBase 的class实例。
现在看看 class文件加密、解密的代码:
这里,仅仅是示例,加解密的算法非常简单, 加密算法是把原.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, 定义自己的ClassLoader,只需要继承 ClassLoader, 并覆写方法 findClass 即可。
完整的 客户端代码:
最后,需要注意的一点,客户端通过自定义的ClassLoader 像如下代码加载类:
自定义的 ClassLoader 会先把 类加载操作先委派给它的parent, 也就是系统默认的类加载器, 如果系统默认的类加载器,找不到 classload.MyClassBase 这个类,才会调用自己的类加载器,如果classpath下有classload.MyClassBase 这个类,系统默认的类加载器就会找到这个类, 那么自己的类加载器是不会调用的,所以前面说过,需要把classload/MyClassBase.class这个文件删除,自己的类加载器才会起作用。
运行代码看输出:
从上面的输出看,使用的类加载器为我们自定的那个:
如果,把MyClassBase.class 放回到 bin/classload/MyClassBase.class, 输出就变了:
这个时候,使用的类加载器为:
我的 eclipse目录结构:
只需关注 classload这个package和cipher目录, 其它包与本文无关。
首先,我们定义一个需要被加密的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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序