自定义classloader加密java程序

大概想法是这样的:
1. 生成密钥用于在des算法中加密。
2. classloader类中动态的解密class,并且通过反射机制执行main方法。
3. 对classloader类进行高质量的混淆。

首先需要生成des算法中的key:



import java.io.File;
import java.io.FileOutputStream;
import java.security.SecureRandom;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

class Key {

private String keyName;

public Key() {

}

public Key(String keyName) {
this.keyName = keyName;
}

public void createKey(String keyName) throws Exception {

SecureRandom sr = new SecureRandom();
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(sr);
SecretKey key = kg.generateKey();
System.out.println(key.toString());
byte rawKeyData[] = key.getEncoded();
FileOutputStream fo = new FileOutputStream(new File(keyName));
fo.write(rawKeyData);
fo.close();
}

public static void main(String args[]) {
try {
new Key("").createKey("d:/key.txt");

} catch (Exception e) {
e.printStackTrace();
}

}
}



有了key之后就可以对class进行加密了:

package com.hitachi.crypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

public class Crypt {

public static void main(String[] args) throws Exception {

SecureRandom sr = new SecureRandom();
FileInputStream fi = new FileInputStream(new File("d:/key.txt"));
byte rawKeyData[] = new byte[fi.available()];
fi.read(rawKeyData);
fi.close();
DESKeySpec dks = new DESKeySpec(rawKeyData);
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key, sr);
FileInputStream fi2 = new FileInputStream(new File("d:/HelloWorld.class"));
byte data[] = new byte[fi2.available()];
fi2.read(data);
fi2.close();
byte encryptedData[] = cipher.doFinal(data);
FileOutputStream fo = new FileOutputStream(new File("d:/HelloWorld.class"));
fo.write(encryptedData);
fo.close();
}
}


然后还是关键的classloader类:

package com.hitachi.classloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class MyClassLoader extends ClassLoader {

private static String myClasspath = new String("");
private static Hashtable<String, Class<?>> loadClassHashTable = new Hashtable<String, Class<?>>();
private static Hashtable<String, Long> loadClassTime = new Hashtable<String, Long>();

public MyClassLoader() {

}

/** */
/**
* create a classloader and specify a classpath.
*
* @param myClasspath
* the specified classpath name.
*/
public MyClassLoader(String myClasspath) {
if (!myClasspath.endsWith("\\")) {
myClasspath = myClasspath + "\\";
}
MyClassLoader.myClasspath = myClasspath;
}

/** */
/**
* set the classpath
*
* @param myClasspath
* the specified classpath name
*/
public void SetmyClasspath(String myClasspath) {
if (!myClasspath.endsWith("\\")) {
myClasspath = myClasspath + "\\";
}
MyClassLoader.myClasspath = myClasspath;
}

/** */
/**
* Loads the class with the specified binary name. This method searches for
* classes in the same manner as the loadClass(String, boolean) method.
* Invoking this method is equivalent to invoking {loadClass(name,false)}.
*
* @param className
* The binary name of the class.
*
* @return The resulting <tt>Class</tt> object.
*
* @throws ClassNotFoundException
* If the class was not found.
*/
@SuppressWarnings("unchecked")
public Class loadClass(String className) throws ClassNotFoundException {
return loadClass(className, false);
}

/** */
/**
* Loads the class with the specified binary name. The default
* implementation of this method searches for classes in the following
* order:
*
* Invoke {findLoadedClass(String)} to check if the class has already been
* loaded.
*
* Invoke {findSystemClass(String)} to load the system class.
*
* Invoke the {findClass(String)} method to find the class.
*
* If the class was found using the above steps, and the resolve flag is
* true, this method will then invoke the {resolveClass(Class)} method on
* the resulting Class object.
*
* @param name
* The binary name of the class.
*
* @param resolve
* If true then resolve the class.
*
* @return The resulting Class object.
*
* @throws ClassNotFoundException
* If the class could not be found.
*/
@SuppressWarnings("unchecked")
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {

try {
Class foundClass = findLoadedClass(name);

// check if the class has already been loaded.
if (foundClass != null) {
System.out.println("Complete to load the class: " + name);
return foundClass;
}

// if the class is systemClass, load the system class by system
if (name.startsWith("java.")) {
foundClass = findSystemClass(name);
loadClassHashTable.put(name, foundClass);
System.out.println("System is loading the class: " + name);
return foundClass;
}

// invoke the findClass() method to load the class
try {
foundClass = findClass(name);
} catch (Exception fnfe) {
}

if (resolve && (foundClass != null)) {
resolveClass(foundClass);
}
return foundClass;
} catch (Exception e) {
throw new ClassNotFoundException(e.toString());
}
}

/** */
/**
* Finds the class with the specified binary name.The default implementation
* throws a ClassNotFoundException.
*
* @param className
* The binary name of the class.
*
* @return The resulting Class object.
*
* @throws ClassNotFoundException
* If the class could not be found.
*/
@SuppressWarnings("unchecked")
public Class findClass(String className) {

byte[] classData = null;
try {
classData = loadClassData(className);
} catch (IOException e) {
e.printStackTrace();
}
if (classData == null) {
return null;
}

System.out.println("MyClassLoader is loading : " + className + "");
Class c = defineClass(className, classData, 0, classData.length);
MyClassLoader.loadClassHashTable.put(className, c);
System.out.println("Complete to load the class :" + className);
return c;
}

/** */
/**
* Loads the classData with the specified binary name. This method searches
* for classes in the specified classpath as
* searchFile(myClasspath,className) method.
*
* @param name
* The binary name of the class
*
* @return The resulting the classData of the class object by byte[]
*
* @throws IOException
* if have some failed or interrupted I/O operations.
*/
private byte[] loadClassData(String className) throws IOException {

String filePath = searchFile(myClasspath, className + ".class");

if (!(filePath == null || filePath == "")) {

System.out.println("It have found the file : " + className
+ ". Begin to read the data and load the class。");
FileInputStream inFile = new FileInputStream(filePath);
byte[] classData = new byte[inFile.available()];
inFile.read(classData);
inFile.close();
loadClassTime.put(className, new File(filePath).lastModified());
return classData;
} else {

filePath = searchFile(myClasspath, className + ".java");
if (!(filePath == null || filePath == "")) {
System.out.println("It have found the file : " + filePath
+ ". Begin to translate");
Runtime.getRuntime().exec("javac " + filePath);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Translate it over : " + filePath);
return loadClassData(className);
} else {
System.out
.println("Haven't found the file, and fail to read the classData!");
return null;
}
}
}

/** */
/**
* Loads the class with the specified binary name.The default implementation
* throws a ClassNotFoundException.
*
* @param classData
* The data of the class.
* @param className
* The binary name of the class.
*
* @return The resulting Class object.
*
* @throws ClassNotFoundException
* If the class could not be found.
*/
public Class loadClass(byte[] classData, String className)
throws ClassNotFoundException {

System.out.println("MyClassLoader is loading : " + className + "");
Class c = defineClass(className, classData, 0, classData.length);
loadClassHashTable.put(className, c);
System.out.println("Complete to load the class :" + className);

return c;
}

/** */
/**
* Loads the class with the specified binary name.The default implementation
* throws a ClassNotFoundException.
*
* @param className
* The binary name of the class.
* @param jarName
* The binary name of the jar that search the class from it.
*
* @return The resulting Class object.
*
* @throws ClassNotFoundException
* If the class could not be found.
*/
protected Class loadClass(String className, String jarName)
throws ClassNotFoundException {

String jarPath = searchFile(myClasspath, jarName + ".jar");
JarInputStream in = null;

if (!(jarPath == null || jarPath == "")) {

try {
in = new JarInputStream(new FileInputStream(jarPath));
JarEntry entry;
while ((entry = in.getNextJarEntry()) != null) {
String outFileName = entry.getName().substring(
entry.getName().lastIndexOf("/") + 1,
entry.getName().length());
if (outFileName.equals(className + ".class")) {
if (entry.getSize() == -1) {
System.err.println("error : can't read the file!");
return null;
}
byte[] classData = new byte[(int) entry.getSize()];
System.out
.println("It have found the file : "
+ className
+ ". Begin to read the data and load the class。");
in.read(classData);
return loadClass(classData, className);
}
}
System.out.println("Haven't found the file " + className
+ " in " + jarName + ".jar.");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
System.out.println("Haven't found the jarFile: " + jarName
+ ".jar.");
return null;
}
return null;
}

/** */
/**
* Reloads the class with the specified binary name. Needn't have to restart
* JVM then reload the class.
*
* @param className
* The binary name of the class need to reload .
*
* @return The resulting Class object.
*
* @throws ClassNotFoundException
* If the class was not found.
*/
public Class reload(String fileName) {

String filePath = searchFile(myClasspath, fileName + ".class");
Long a = new File(filePath).lastModified();

if (!a.equals(loadClassTime.get(fileName))) {
loadClassHashTable.remove(fileName);
loadClassTime.remove(fileName);
try {
MyClassLoader mc2 = new MyClassLoader(myClasspath);
mc2.loadClass(fileName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else {
System.out
.println("The class is the newest version , needn't reloading.");
}
return null;
}

/** */
/**
* search the file with the specified binary name. Needn't have to restart
* JVM then reload the class.
*
* @param classpath
* the specified path where we search.
* @param fileName
* The binary name of the file that want to search.
*
* @return The resulting file path.
*/
public String searchFile(String classpath, String fileName) {

String cut = fileName.substring(fileName.lastIndexOf('.'), fileName
.length());
String path = fileName.substring(0, fileName.lastIndexOf('.')).replace(
'.', '/')
+ cut;

File f = new File(classpath + path);
if (f.isFile()) {
return f.getPath();
} else {
String objects[] = new File(classpath).list();
for (int i = 0; i < objects.length; i++) {
if (new File(classpath + File.separator + objects[i])
.isDirectory()) {
String s = searchFile(classpath + objects[i]
+ File.separator, fileName);
if (s == null || s == "") {
continue;
} else {
return s;
}
}
}
}
return null;
};

}




最后解密,并且通过反射机制执行:

package com.hitachi.crypt;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import com.hitachi.classloader.MyClassLoader;
public class Decrypt {

public static void main(String[] args) throws Exception {

SecureRandom sr = new SecureRandom();
FileInputStream fi = new FileInputStream(new File("d:/key.txt"));
byte rawKeyData[] = new byte[fi.available()];
fi.read(rawKeyData);
fi.close();
DESKeySpec dks = new DESKeySpec(rawKeyData);
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key, sr);
FileInputStream fi2 = new FileInputStream(new File(
"D:/HelloWorld.class"));
byte encryptedData[] = new byte[fi2.available()];
fi2.read(encryptedData);
fi2.close();
byte decryptedData[] = cipher.doFinal(encryptedData);
MyClassLoader mcl = new MyClassLoader("D:/");
Class cl = mcl.loadClass(decryptedData, "HelloWorld");
Method mainMethod = cl.getMethod("sayHello");
mainMethod.invoke(null, null);
}
}


其中源文件是这样的:


public class HelloWorld {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello world");
}

public static void sayHello() {
System.out.println("Hello");
}

}


就可以了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值