实现AES解密的类加载器
需求
- 一些场景,为了防止
.class
文件被反编译得到源代码,需要对.class
文件做加密处理
设计
- 编译生成
.class
文件,如:HelloWorld.class - 使用AES加密工具,加密
.class
文件,得到.encrypt
文件,如HelloWorld.encrypt
- 项目启动时,先使用应用类加载器加载类,如果加载失败,使用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) 编辑 收藏 举报