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