您的位置:首页 > 运维架构 > Tomcat

javaee加密部署项目通过tomcat使用自定义的classload解密

2017-06-16 13:39 405 查看
参考文章:http://blog.itpub.net/29754888/viewspace-1220306/

如上述文章所说,继承tomcat的WebappClassLoader自定义自己的classload,并不适用于spring。

原因是spring加载类是用另外的方式来加载。

意思应该是是需要封装的jar代码中不能有spring的注解(依赖注入、控制器声明等等)

原理:

通过加密java代码的class文件,实现对源代码的保护,并通过自定义的classload文件来解密class文件,把类加载到项目中

自定义自己的classload,并重写findClass方法。类加载器通过调用findClass方法,寻找并加载类文件。

通过重写findClass方法,可以把预先加密好的class文件,解密后再加载到项目中

package generator;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.catalina.loader.WebappClassLoader;

/**
* 自定义的classloader 可以解密文件并加载
*
* @author uikoo9
*/
public class UClassLoader extends WebappClassLoader {

/**
* 算法
*/
private String algorithmStr="AES/ECB/PKCS5Padding";

/**
* key
*/
private String keyStr="abcdefg123456789";

/**
* 默认构造器
*
* @throws Exception
*/
public UClassLoader() {
super();
}

/**
* 默认构造器
*
* @param parent
* @throws Exception
*/
public UClassLoader(ClassLoader parent) {
super(parent);
}

/*
* (non-Javadoc)
*
* @see
* org.apache.catalina.loader.WebappClassLoader#findClass(java.lang.String)
*/
public Class findClass(String name) throws ClassNotFoundException {
if (name.contains("com.A")
|| name.contains("com.B")
|| name.contains("com.C")) {
return findClassEncrypt(name);
} else {
return super.findClass(name);
}
}

/**
* 查找class
*
* @param name
* @return
* @throws ClassNotFoundException
*/
private Class findClassEncrypt(String name) throws ClassNotFoundException {
byte[] classBytes = null;
try {
classBytes = loadClassBytesEncrypt(name);
} catch (Exception e) {
e.printStackTrace();
}

Class cl = defineClass(name, classBytes, 0, classBytes.length);
if (cl == null) {
System.out.println("找不到该类:" + name);
throw new ClassNotFoundException(name);
}
return cl;
}

/**
* 加载加密后的class字节流
*
* @param name
* @return
* @throws Exception
*/
private byte[] loadClassBytesEncrypt(String name) throws Exception {
// 获取当前文件路径
// File directory = new File("");
// String parentPath = directory.getAbsolutePath()+File.separator;
String parentPath = "F:\\";
List<String> basepath = new ArrayList<String>();
basepath.add(parentPath + "A\\");// 项目物理地址
basepath.add(parentPath + "B\\");// 项目物理地址
basepath.add(parentPath + "C\\");// 项目物理地址
String cname = null;
File file = null;
for (String path : basepath) {
cname = path + name.replace('.', '\\') + ".cls";
file = new File(cname);
if (file.exists()) {
break;
}
}

if (file != null) {
if (!file.exists()) {
throw new Exception("File Not found:" + cname);
}
}
FileInputStream in = new FileInputStream(cname);
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] buf = new byte[1024 * 100];// 100KB
int len = 0;
while ((len = in.read(buf)) != -1) {
buffer.write(buf, 0, len);
}
in.close();
return decrypt(parseHexStr2Byte(new String(buffer.toByteArray())));

} finally {
in.close();
}
}

/**
* 解密
*
* @param content
* @return
*/
private byte[] decrypt(byte[] content) {
try {
byte[] keyBytes = this.keyStr.getBytes("utf-8");
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(this.algorithmStr);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}

/**
* 十六进制字符串转字节
*
* @param hexStr
* @return
*/
private byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}

}在tomcat的content.xml文件中,配置自定义的classload为tomcat默认的classload
在<Content>中添加如下代码

<Loader loaderClass="generator.UClassLoader" delegate="true"></Loader>指定环境上下文类加载为自定义的classload
注意:自定义的classload放在tomcat的lib下,并且文件夹层级结构需要跟包名一致generator/UClassLoader.class

大概步骤如下

1、对需要加密的class进行算法加密,本人是用AES加密

2、把加密后的class文件放到固定的地方,把项目中的对应class删除

3、修改context.xml的指定上下文类加载器为自定义加载器

4、启动tomcat

另外:如果不想自定义tomcat的classload,也可以自定义classload继承URLClassLoader

代码方式类似,也是通过重写findClass方法。编写好自定义classload后,在项目中实例化该类。

package classload;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class MyClassLoader extends URLClassLoader {

/**
* 算法
*/
private String algorithmStr="AES/ECB/PKCS5Padding";

/**
* key
*/
private String keyStr="abcdefg123456789";

public MyClassLoader(URL[] urls){
super(urls);
}

public MyClassLoader(URL[] urls, ClassLoader parent){
super(urls,parent);
}

public static void main(String[] args) throws MalformedURLException {
URL url = new File("a.jar").toURI().toURL();
MyClassLoader classLoader = new MyClassLoader(new URL[]{url},Thread.currentThread().getContextClassLoader());
//设置自定义classload为环境上下文类加载器
Thread.currentThread().setContextClassLoader(classLoader);
}

public Class findClass(String name) throws ClassNotFoundException {
if (name.contains("com.A")
|| name.contains("com.B")
|| name.contains("com.C")) {
return findClassEncrypt(name);
} else {
return super.findClass(name);
}
}

/**
* 查找class
*
* @param name
* @return
* @throws ClassNotFoundException
*/
private Class findClassEncrypt(String name) throws ClassNotFoundException {
byte[] classBytes = null;
try {
classBytes = loadClassBytesEncrypt(name);
} catch (Exception e) {
e.printStackTrace();
}

Class cl = defineClass(name, classBytes, 0, classBytes.length);
if (cl == null) {
System.out.println("找不到该类:" + name);
throw new ClassNotFoundException(name);
}
return cl;
}

/**
* 加载加密后的class字节流
*
* @param name
* @return
* @throws Exception
*/
private byte[] loadClassBytesEncrypt(String name) throws Exception {
// 获取当前文件路径
// File directory = new File("");
// String parentPath = directory.getAbsolutePath()+File.separator;
String parentPath = "F:\\";
List<String> basepath = new ArrayList<String>();
basepath.add(parentPath + "A\\");// 项目物理地址
basepath.add(parentPath + "B\\");// 项目物理地址
basepath.add(parentPath + "C\\");// 项目物理地址
String cname = null;
File file = null;
for (String path : basepath) {
cname = path + name.replace('.', '\\') + ".cls";
file = new File(cname);
if (file.exists()) {
break;
}
}

if (file != null) {
if (!file.exists()) {
throw new Exception("File Not found:" + cname);
}
}
FileInputStream in = new FileInputStream(cname);
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] buf = new byte[1024 * 100];// 100KB
int len = 0;
while ((len = in.read(buf)) != -1) {
buffer.write(buf, 0, len);
}
in.close();
return decrypt(parseHexStr2Byte(new String(buffer.toByteArray())));

} finally {
in.close();
}
}

/**
* 解密
*
* @param content
* @return
*/
private byte[] decrypt(byte[] content) {
try {
byte[] keyBytes = this.keyStr.getBytes("utf-8");
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(this.algorithmStr);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}

/**
* 十六进制字符串转字节
*
* @param hexStr
* @return
*/
private byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}


项目中加载该类
URL url = new File("a.jar").toURI().toURL();
MyClassLoader classLoader = new MyClassLoader(new URL[]{url},Thread.currentThread().getContextClassLoader());
//设置自定义classload为环境上下文类加载器
Thread.currentThread().setContextClassLoader(classLoader);

构造函数必须传URL对象参数进去,所以要随便指定一个jar。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐