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

参考文章: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。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值