基于ClassLoader的java代码加密的经验分享

 

   原理就是 生成项目时将待加密的java class文件经过加密算法转换生成加密的二进制文件,此文件不会被JD-GUI等反编译工具直接解密。 项目在启动时,用自定义的ClassLoader将加密的二进制文件进行解密并载入到jvm中,经过反射实例化该java类(最好单例),其余代码就能够调用它的方法了。java

 

1. 好比 待加密的java类命名为 CAU.java,到时生成加密的二进制文件更名为CAU.lib,可让人很难想到这个lib实际上是个加密的class文件。算法

     先定义一个java接口类,让待加密的CAU类实现该接口,也能够再定义个Factory类,项目中的其余代码只引用该接口,调用DemoFactory的getRunInter方法获得RunInter实例,而DemoFactory的setRunInter在生成的项目中是没法直接找到的。dom

     演示项目是maven项目,RunInter和DemoFactory是在src/main/java里,而CAU.java是放在src/test/java(生成的项目包里不会有CAU.class,只有CAU.lib)。jvm

/**
 * 待加密类接口
 * @author hxt
 *
 */
public interface RunInter {

	public void runMethod(String str);
	
}

/**
 *  工厂类
 * @author hxt
 *
 */
public class DemoFactory {

	private static RunInter runInter;

        public static RunInter getRunInter() {
return runInter;
      }
// 加密类中传入
       public static void setRunInter(RunInter runInter) {
		DemoFactory.runInter = runInter;
	}
}

 

/**
 *  被加密的类,生成的项目里是CAU.lib
 * @author hxt
 *
 */
public class CAU implements RunInter{

      	public CAU(){
		// 将RunInter实例传入DemoFactory
		DemoFactory.setRunInter(this);
	}

	@Override
	public void runMethod(String str) {
		
		System.out.println("run str: "+str);
		
	}

}

2. 生成密钥key文件
 maven

/**
	  * 生成密钥key文件,这里使用des算法加密
	  * @throws Exception
	  */
	  //@Test
	   public void testGenerateKey() throws Exception {
		  	String keyFileName ="key";
	        String algorithm = "DES";
	        String savePath="src/main/resources/"; 
	        /* 生成密钥 */
	        SecureRandom secureRandom = new SecureRandom();
	        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
	        keyGenerator.init(secureRandom);
	        SecretKey secretKey = keyGenerator.generateKey();

	        /* 将密钥数据保存到文件 */
	        FileUtil.byteArrayWriteToFile(savePath+keyFileName, secretKey.getEncoded());
		  
	  }

3. 将CAU.class 转化成加密的CAU.libide

/**
	   * 将class文件 转化成加密的class
	   * @throws Exception
	   */
	  @Test
	   public void testEncryptClass() throws Exception {
		 
		  String classPath="target/test-classes/"; 
		  String savePath="src/main/resources/"; 
		  
		  	String[] args=new String[]{"CAU.class"};   // 待加密的class
	        String keyFileName = "key";  //密钥key文件
	        String algorithm = "DES";
	
	        /* 生成密钥 */
	        SecureRandom secureRandom = new SecureRandom();
	        byte rawKey[] = FileUtil.fileReadToByteArray(savePath+keyFileName);
	        DESKeySpec desKeySpec = new DESKeySpec(rawKey);
	        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
	        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
	
	        /* 建立用于实际加密的Cipher对象 */
	        Cipher cipher = Cipher.getInstance(algorithm);
	        cipher.init(Cipher.ENCRYPT_MODE, secretKey, secureRandom);
	
	        /* 加密命令行中指定的每一类 */
	        for (int i = 0; i < args.length; i++)
	        {
	            String fileName = args[i];
	            /* 读入类文件 */
	            byte classData[] = FileUtil.fileReadToByteArray(classPath+fileName);
	            /* 加密类文件 */
	            byte encryptedClassData[] = cipher.doFinal(classData);
	            String saveFileName=fileName.replace(".class", ".lib");
	            /* 保存加密后的文件 */
	            FileUtil.byteArrayWriteToFile(savePath+saveFileName, encryptedClassData);
	            System.out.println("***Encrypted " + saveFileName + " ***");
	        }
		  
	  }

 

4.函数

自定义的ClassLoader类工具

 

/**
 * 经过ClassLoader将加密的class还原载入
 * @author hxt
 *
 */
public class DecryptClassLoader extends ClassLoader
{
    private SecretKey key;
    private Cipher cipher;
    private String path;
    private String keyFilename; //密钥文件

    /**
     * 构造函数:设置解密所须要的对象
     * 
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public DecryptClassLoader(String keyFilename) throws Exception
    {
    	this.keyFilename=keyFilename;
    	//this.path=DecryptClassLoader.class.getResource("/").getPath();
    	this.path="/";
    	//InputStream is=DecryptClassLoader.class.getResourceAsStream("/key");
    	init();
    }
    
    /**
     * 构造函数:设置解密所须要的对象
     * 
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public DecryptClassLoader(String keyFilename,String path) throws Exception
    {
    	//path=DecryptClassLoader.class.getResource("/").getPath();
    	this.keyFilename=keyFilename;
    	this.path=path;
    	//System.out.println(path);
        /* 读取密匙 */
        //System.err.println("***DecryptClassLoader: reading key***");
    	init();
    }
    
    public void init() throws Exception{
    	//System.out.println(path);
        byte rawKey[] = FileUtil.resourceReadToByteArray(path+keyFilename);
        DESKeySpec dks = new DESKeySpec(rawKey);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey key = keyFactory.generateSecret(dks);
    	
        this.key = key;
        String algorithm = "DES";
        SecureRandom sr = new SecureRandom();
        //System.err.println("***DecryptClassLoader: creating cipher***");
        cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key, sr);
    	
    }
    
    /**
     * 得到加密的class
     * @param javaClassName  class名,而且要和路径一致
     * @return
     * @throws Exception
     */
    public Class decodeClass(String javaClassName) throws Exception{
    	
        /* 建立应用主类的一个实例,经过ClassLoader装入它 */
        Class clasz = this.loadClass(javaClassName, false);

    	return clasz;
    	
    }


    public Class loadClass(String name, boolean resolve) throws ClassNotFoundException     
    {
    	if(name.indexOf(".")>-1){
    		return DecryptClassLoader.class.getClassLoader().loadClass(name);
    	}	
        try
        {
            /* 要建立的Class对象 */
            Class clasz = null;

            /* 若是类已经在系统缓冲之中,没必要再次装入它 */
            clasz = findLoadedClass(name);
            if (clasz != null)
                return clasz;

            try
            {
                /* 读取通过加密的类文件 */
                byte classData[] = FileUtil
                        .resourceReadToByteArray(path+name + ".lib");
                if (classData != null)
                {
                    /* 解密 */
                    byte decryptedClassData[] = cipher.doFinal(classData);
                    //System.out.println(decryptedClassData.length);
                   // FileUtil.byteArrayWriteToFile("dd.class", decryptedClassData);
                    /* 再把它转换成一个类 */
                    clasz = defineClass(name, decryptedClassData, 0,
                            decryptedClassData.length);
                }
            }
            catch (IOException fnfe)
            {
            	//fnfe.printStackTrace();
            }

            /* 若是上面没有成功,尝试用默认的ClassLoader装入它 */
            if (clasz == null)
                clasz = findSystemClass(name);

            /* 若有必要,则装入相关的类 */
            if (resolve && clasz != null)
                resolveClass(clasz);

            return clasz;

        }
        catch (Exception gse)
        {
        	throw new ClassNotFoundException(gse.toString());
        }
    
    }

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}
    
    
}

 

5. 在项目启动时的初始化方法里加入this

DecryptClassLoader dcl= new DecryptClassLoader("key");
Class clasz =dcl.decodeClass("CAU"); 
//能够把CAU字符串也作个转换,好比简单的base64下,这样全局搜索这个字符串不会找到,增长点破解难度
clasz.newInstance();

 

6. 项目的其余代码里调用该加密类的方法加密

 

   DemoFactory.getRunInter().runMethod("demoStr");

 

7. 其余说明

   项目中DecryptClassLoader类没有被加密,破解者能够修改启动将解密后的类文件导出来。能够把DecryptClassLoader也当成加密类进行多层加密。固然加密代码要在程序中运行,确定是有办法解密的,只是增长解密破解的难度和时间。

相关文章
相关标签/搜索