javaee加密部署專案通過tomcat使用自定義的classload解密
阿新 • • 發佈:2019-01-30
如上述文章所說,繼承tomcat的WebappClassLoader自定義自己的classload,並不適用於spring。
原因是spring載入類是用另外的方式來載入。
意思應該是是需要封裝的jar程式碼中不能有spring的註解(依賴注入、控制器宣告等等)
原理:
通過加密java程式碼的class檔案,實現對原始碼的保護,並通過自定義的classload檔案來解密class檔案,把類載入到專案中
自定義自己的classload,並重寫findClass方法。類載入器通過呼叫findClass方法,尋找並載入類檔案。
通過重寫findClass方法,可以把預先加密好的class檔案,解密後再載入到專案中
在tomcat的content.xml檔案中,配置自定義的classload為tomcat預設的classloadpackage 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; } }
在<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。