作者:郭嘉
邮箱:[email protected]
博客:http://blog.csdn.net/allenwells
github:https://github.com/AllenWells3
代码加密也是对Java代码进行保护的一种重要方式,作为Java代码加密开篇的文章,本文先举例介绍,如何利用加密算法实现对.class文件进行加密。注意为说明基本原理,本文程序采用命令行进行操作,后续会给出具有UI界面的Java类加密软件。
代码如下所示:
首先写个工具类FileUtil.java用于文件读取和写入,源码如下所示:
package com.allenwells.codeencryption.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileUtil
{
/**
* 将文件读入byte数组
*
* @param fileName
* @return
* @throws IOException
*/
static public byte[] fileReadToByteArray(String fileName)
throws IOException
{
File file = new File(fileName);
long fileLength = file.length();
byte fileData[] = new byte[(int) fileLength];
FileInputStream fis = new FileInputStream(file);
int readLength = fis.read(fileData);
if (readLength != fileLength)
{
System.err.println("***Only read " + readLength + " of " + fileLength
+ " for file " + file + " ***");
}
fis.close();
return fileData;
}
/**
* 将byte数组写入到文件
*
* @param fileName
* @param data
* @throws IOException
*/
static public void byteArrayWriteToFile(String fileName, byte[] data)
throws IOException
{
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(data);
fos.close();
}
}
生成密钥文件,可由命令行传入密钥的名字,这里使用key.data,源码如下所示:
package com.allenwells.codeencryption;
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import com.allenwells.codeencryption.util.FileUtil;
public class GenerateKey
{
static public void main(String args[]) throws Exception
{
String keyFileName = args[0];
String algorithm = "DES";
/* 生成密钥 */
SecureRandom secureRandom = new SecureRandom();
KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
keyGenerator.init(secureRandom);
SecretKey secretKey = keyGenerator.generateKey();
/* 将密钥数据保存到文件 */
FileUtil.byteArrayWriteToFile(keyFileName, secretKey.getEncoded());
}
}
获取到密钥后就可以进行类的加密了,源码如下所示:
package com.allenwells.codeencryption;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
import com.allenwells.codeencryption.util.FileUtil;
public class EncryptClass
{
static public void main(String args[]) throws Exception
{
String keyFileName = args[0];
String algorithm = "DES";
/* 生成密钥 */
SecureRandom secureRandom = new SecureRandom();
byte rawKey[] = FileUtil.fileReadToByteArray(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 = 1; i < args.length; i++)
{
String fileName = args[i];
/* 读入类文件 */
byte classData[] = FileUtil.fileReadToByteArray(fileName);
/* 加密类文件 */
byte encryptedClassData[] = cipher.doFinal(classData);
/* 保存加密后的文件 */
FileUtil.byteArrayWriteToFile(fileName, encryptedClassData);
System.out.println("***Encrypted " + fileName + " ***");
}
}
}
因为类经过加密处理,所以要重写设计ClassLoader来进行加密类文件的加载,源码如所示:
package com.allenwells.codeencryption;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.security.SecureRandom;
import java.security.GeneralSecurityException;
import java.lang.reflect.Method;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import com.allenwells.codeencryption.util.FileUtil;
public class DecryptClassLoader extends ClassLoader
{
private SecretKey key;
private Cipher cipher;
/**
* 构造函数:设置解密所需要的对象
*
* @param key
* @throws GeneralSecurityException
* @throws IOException
*/
public DecryptClassLoader(SecretKey key) throws GeneralSecurityException,
IOException
{
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);
}
/**
* main过程: 1 读入密匙,创建DecodeClassLoader的实例,它就是定制ClassLoader。
* 2 设置好ClassLoader以后,用它装入应用实例,
* 3 最后,通过Java Reflection API调用应用实例的main方法
*
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception
{
String keyFilename = args[0];
String javaClassName = args[1];
/* 传递给应用本身的参数 */
String realArgs[] = new String[args.length - 2];
System.arraycopy(args, 2, realArgs, 0, args.length - 2);
/* 读取密匙 */
System.err.println("***DecryptClassLoader: reading key***");
byte rawKey[] = FileUtil.fileReadToByteArray(keyFilename);
DESKeySpec dks = new DESKeySpec(rawKey);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(dks);
/* 创建解密的ClassLoader */
DecryptClassLoader dcl = new DecryptClassLoader(key);
/* 创建应用主类的一个实例,通过ClassLoader装入它 */
System.err.println("***DecryptClassLoader: loading " + javaClassName
+ " ***");
Class clasz = dcl.loadClass(javaClassName, false);
/* 获取一个对main()的引用 */
String proto[] = new String[1];
Class mainArgs[] = { (new String[1]).getClass() };
Method main = clasz.getMethod("main", mainArgs);
/* 创建一个包含main()方法参数的数组 */
Object argsArray[] = { realArgs };
System.err.println("***DecryptClassLoader: running " + javaClassName
+ ".main()***");
/* 调用main() */
main.invoke(null, argsArray);
}
public Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
try
{
/* 要创建的Class对象 */
Class clasz = null;
/* 如果类已经在系统缓冲之中,不必再次装入它 */
clasz = findLoadedClass(name);
if (clasz != null)
return clasz;
try
{
/* 读取经过加密的类文件 */
byte classData[] = FileUtil
.fileReadToByteArray(name + ".class");
if (classData != null)
{
System.out
.println("***DecryptClassLoader: decode begin***");
/* 解密 */
byte decryptedClassData[] = cipher.doFinal(classData);
/* 再把它转换成一个类 */
clasz = defineClass(name, decryptedClassData, 0,
decryptedClassData.length);
System.err.println("***DecryptClassLoader: decrypting class "
+ name + " ***");
}
}
catch (FileNotFoundException fnfe)
{
}
/* 如果上面没有成功,尝试用默认的ClassLoader装入它 */
if (clasz == null)
clasz = findSystemClass(name);
/* 如有必要,则装入相关的类 */
if (resolve && clasz != null)
resolveClass(clasz);
return clasz;
}
catch (IOException ie)
{
throw new ClassNotFoundException(ie.toString());
}
catch (GeneralSecurityException gse)
{
throw new ClassNotFoundException(gse.toString());
}
}
}
cd D:\workplace_eclipse\CodeEncryption\bin
java com.allenwells.codeencryption.GenerateKey key
这时候在工程目录下已经生成了key,如下图所示:
包需要加密的class文件放入bin目录下,运行一下命令
java com.allenwells.codeencryption.EncryptClass key TestClass.class
运行完成后,此时的TestClass.class就是已经经过加密的类文件了,现在用jd-jui来验证一下.
首先放入未加密的类文件,如下图所示:
再放入加密后的类文件,如下图所示:
上面两张图表明已经加密成功,下面就来载入并运行加密后的类文件。
我们再来比较下加密前后class字节码的变化。
未加密的字节码:可以看到第一行前四组十六进制数为CA FE BA BE,这四个段标识了该文件为一个可以被Java虚拟机所识别的class文件。
加密后的字节码:
接下来就是用自定义的ClassLoader加载并运行加密后的类文件,命令如下所示:
java com.allenwells.codeencryption.DecryptClassLoader key TestClass
执行完毕后,会提示“TestClass run successfully”,即加密类加载并执行成功,全部的操作盒命令如下图所示:
Eclipse源码工程下载