作者:郭嘉
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
github:https://github.com/AllenWells3
代码加密也是对Java代码进行保护的一种重要方式,作为Java代码加密开篇的文章,本文先举例介绍,如何利用加密算法实现对.class文件进行加密。注意为说明基本原理,本文程序采用命令行进行操作,后续会给出具有UI界面的Java类加密软件。
一 编写Java代码
1.1 生成安全密钥
生成密钥的步骤
- 创建Cipher对象(Java密码扩展包含了Cipher类,这个类是所有加密算法的超类。)。
- 设置模式和密钥,生成密钥的步骤如下所示:
- 为加密算法获取KeyGenerator。
- 用随机源来初始化密钥发生器,如果密码块长度是可变的,还需要制定期望的密码块长度。
- 调用generateKey方法。
代码如下所示:
首先写个工具类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();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
生成密钥文件,可由命令行传入密钥的名字,这里使用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());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
1.2 加密类文件
获取到密钥后就可以进行类的加密了,源码如下所示:
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 + " ***");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
1.3 加载加密后的类文件
因为类经过加密处理,所以要重写设计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());
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
二 进行加密和解密
2.1 进入工程的bin目录
cd D:\workplace_eclipse\CodeEncryption\bin
2.2 运行GenerateKey,生成key。
java com.allenwells.codeencryption.GenerateKey key
这时候在工程目录下已经生成了key,如下图所示:
2.3 运行EncryptClasses,加密类文件
包需要加密的class文件放入bin目录下,运行一下命令
java com.allenwells.codeencryption.EncryptClass key TestClass.class
运行完成后,此时的TestClass.class就是已经经过加密的类文件了,现在用jd-jui来验证一下.
首先放入未加密的类文件,如下图所示:
再放入加密后的类文件,如下图所示:
上面两张图表明已经加密成功,下面就来载入并运行加密后的类文件。
我们再来比较下加密前后class字节码的变化。
未加密的字节码:可以看到第一行前四组十六进制数为CA FE BA BE,这四个段标识了该文件为一个可以被Java虚拟机所识别的class文件。
加密后的字节码:
2.4 载入并运行加密后的类文件
接下来就是用自定义的ClassLoader加载并运行加密后的类文件,命令如下所示:
java com.allenwells.codeencryption.DecryptClassLoader key TestClass
执行完毕后,会提示“TestClass run successfully”,即加密类加载并执行成功,全部的操作盒命令如下图所示:
Eclipse源码工程下载
|