原理就是 生成项目时将待加密的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也当成加密类进行多层加密。固然加密代码要在程序中运行,确定是有办法解密的,只是增长解密破解的难度和时间。