基於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也當成加密類進行多層加密。固然加密代碼要在程序中運行,確定是有辦法解密的,只是增長解密破解的難度和時間。

相關文章
相關標籤/搜索