分享

【Java代码保护探索之路系列:代码加密】之一:代码加密开篇

 WindySky 2017-12-01

作者:郭嘉
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
github:https://github.com/AllenWells3

代码加密也是对Java代码进行保护的一种重要方式,作为Java代码加密开篇的文章,本文先举例介绍,如何利用加密算法实现对.class文件进行加密。注意为说明基本原理,本文程序采用命令行进行操作,后续会给出具有UI界面的Java类加密软件。

一 编写Java代码

1.1 生成安全密钥

生成密钥的步骤

  1. 创建Cipher对象(Java密码扩展包含了Cipher类,这个类是所有加密算法的超类。)。
  2. 设置模式和密钥,生成密钥的步骤如下所示:
    1. 为加密算法获取KeyGenerator。
    2. 用随机源来初始化密钥发生器,如果密码块长度是可变的,还需要制定期望的密码块长度。
    3. 调用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
  • 1

2.2 运行GenerateKey,生成key。

java com.allenwells.codeencryption.GenerateKey key
  • 1
  • 2

这时候在工程目录下已经生成了key,如下图所示:

这里写图片描述

2.3 运行EncryptClasses,加密类文件

包需要加密的class文件放入bin目录下,运行一下命令


java com.allenwells.codeencryption.EncryptClass key TestClass.class
  • 1
  • 2
  • 3

运行完成后,此时的TestClass.class就是已经经过加密的类文件了,现在用jd-jui来验证一下.

首先放入未加密的类文件,如下图所示:

这里写图片描述

再放入加密后的类文件,如下图所示:

这里写图片描述

上面两张图表明已经加密成功,下面就来载入并运行加密后的类文件。

我们再来比较下加密前后class字节码的变化。

未加密的字节码:可以看到第一行前四组十六进制数为CA FE BA BE,这四个段标识了该文件为一个可以被Java虚拟机所识别的class文件。

这里写图片描述

加密后的字节码:

这里写图片描述

2.4 载入并运行加密后的类文件

接下来就是用自定义的ClassLoader加载并运行加密后的类文件,命令如下所示:

java com.allenwells.codeencryption.DecryptClassLoader key TestClass
  • 1

执行完毕后,会提示“TestClass run successfully”,即加密类加载并执行成功,全部的操作盒命令如下图所示:

这里写图片描述

Eclipse源码工程下载

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多