【正文】Java类加载器(  CLassLoader )死磕7: 

基于加密的自定义网络加载器

本小节目录

7.1. 加密传输Server端的源码

7.2. 加密传输Client端的源码

7.3. 使用亦或实现简单加密和解密算法

7. 网络加密SafeClassLoader的源码

7.5. SafeSocketLoader的使用

众所周知,java代码很容易被反编译,如果你需要把自己的代码进行加密,可以先将编译后的代码用某种加密算法加密,然后结合自己的网络类加载器,进行加密后的安全传输。

客户端接收到加密后的字节码后,负责将这段加密后的代码还原。

1.1.1. 加密传输Server端的源码

和文件传输Server端的源码基本一致,只有一行代码的差别。

简单粗暴,直接上码。

package com.crazymakercircle.classLoader;

import com.crazymakercircle.config.SystemConfig;

import com.crazymakercircle.util.DeEnCode;

import com.crazymakercircle.util.IOUtil;

import com.crazymakercircle.util.Logger;

import java.io.*;

import java.net.ServerSocket;

import java.net.Socket;

/**

 * 文件传输Server端<br>

 */

public class SafeSocketServer {

    ServerSocket serverSocket = null;

  static   String filePath = null;

    public SafeSocketServer() throws Exception {

        serverSocket = new ServerSocket(SystemConfig.SOCKET_SERVER_PORT);

        this.filePath = SystemConfig.CLASS_SERVER_PATH;

        startServer();

     }

    /**

     * 启动服务端

     * 使用线程处理每个客户端传输的文件

     *

     * @throws Exception

     */

    public void startServer() {

        while (true) {

            // server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的

            Logger.info("server listen at:" + SystemConfig.SOCKET_SERVER_PORT);

            Socket socket = null;

            try {

                socket = serverSocket.accept();

                // 每接收到一个Socket就建立一个新的线程来处理它

                new Thread(new Task(socket)).start();

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

    /**

     * 处理客户端传输过来的文件线程类

     */

    class Task implements Runnable {

        private Socket socket;

        private DataInputStream dis;

        private FileOutputStream fos;

        public Task(Socket socket) {

            this.socket = socket;

        }

        @Override

        public void run() {

            try {

                dis = new DataInputStream(socket.getInputStream());

                // 文件名和长度

                String fileName = dis.readUTF();

                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

                sendFile(fileName, dos);

            } catch (Exception e) {

                e.printStackTrace();

            } finally {

                IOUtil.closeQuietly(fos);

                IOUtil.closeQuietly(dis);

                IOUtil.closeQuietly(socket);

            }

        }

        private void sendFile(String fileName, DataOutputStream dos) throws Exception {

            fileName=classNameToPath(fileName);

            fileName = SafeSocketServer.filePath + File.separator + fileName;

            File file = new File(fileName);

            if (!file.exists()) {

                throw new Exception("file not found! :"+fileName);

            }

            long fileLen = file.length();

            dos.writeLong(fileLen);

            dos.flush();

            byte one= (byte) 0xff;

            FileInputStream fis = new FileInputStream(file);

            // 开始传输文件

            Logger.info("======== 开始传输文件 ========");

            byte[] bytes = new byte[1024];

            int length = 0;

            long progress = 0;

            while ((length = fis.read(bytes, 0, bytes.length)) != -1) {

                DeEnCode.encode(bytes,length);

                dos.write(bytes, 0, length);

                dos.flush();

                progress += length;

                Logger.info("| " + (100 * progress / fileLen) + "% |");

            }

            Logger.info("======== 文件传输成功 ========");

        }

    }

    private String classNameToPath(String className) {

        return  className.replace('.', '/') + ".class";

    }

    public static void main(String[] args) {

        try {

            SafeSocketServer socketServer = new SafeSocketServer();

            socketServer.startServer();

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

和文件传输Server端的源码基本一致,只有一行代码的差别。这个类仅仅增加的一行是:

DeEnCode.encode(bytes,length);

其目的是,在发送字节码之前,使用定义的加密函数,进行字节码加密。

源码比较长,建议运行main函数,先将服务端的源码跑起来,然后再阅读代码,这样阅读起来更加容易懂。

另外,在使用基于网络的类加载器之前,一定要确保服务端的代码先执行。否则客户端会报错。

案例路径:com.crazymakercircle.classLoader.SafeSocketServer

案例提示:无编程不创客、无案例不学习。一定要跑案例哦

运行的结果是:

        <clinit> |>  开始加载配置文件到SystemConfig

        loadFromFile |>  load properties: /system.properties

         startServer |>  server listen at:18899

看到以上结果,表示服务端开始启动。监听了18899端口,等待客户端的连接。

1.1.2. 加密传输Client端的源码

客户端的工作:

建立和服务器的TCP连接后,首先做的第一步工作,是发送文件名称给服务器端。

然后阻塞,直到服务器的数据过来。客户端开始接受服务器传输过来的数据。接受数据的工作由函数receivefile()完成。

整个的数据的读取工作分为两步,先读取文件的大小,然后读取传输过来的文件内容。

在传输文件内容的字节码时,需要对字节码进行解密。

简单粗暴,直接上源码。

/**

 * 文件传输Client端

 */

public class SafeSocketClient {

    private Socket client;

    private FileInputStream fis;

    private DataOutputStream dos;

    /**

     * 构造函数<br/>

     * 与服务器建立连接

     *

     * @throws Exception

     */

    public SafeSocketClient() throws IOException {

            this.client = new Socket(

                    SystemConfig.SOCKET_SERVER_IP,

                    SystemConfig.SOCKET_SERVER_PORT

            );

            Logger.info("Cliect[port:" + client.getLocalPort() + "] 成功连接服务端");

    }

    /**

     *向服务端去取得文件

     *

     * @throws Exception

     */

    public byte[] getFile(String fileName) throws Exception {

        byte[] result = null;

        try {

            dos = new DataOutputStream(client.getOutputStream());

            // 文件名和长度

            dos.writeUTF(fileName);

            dos.flush();

            DataInputStream dis = new DataInputStream(client.getInputStream());

            result = receivefile(dis);

            Logger.info("文件接收成功,File Name:" + fileName);

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            IOUtil.closeQuietly(fis);

            IOUtil.closeQuietly(dos);

            IOUtil.closeQuietly(client);

        }

        return result;

    }

    public byte[] receivefile(DataInputStream dis) throws Exception {

        int fileLength = (int) dis.readLong();

        ByteArrayOutputStream bos = new ByteArrayOutputStream(fileLength);

        long startTime = System.currentTimeMillis();

        Logger.info("block IO 传输开始:");

        // 开始接收文件

        byte[] bytes = new byte[1024];

        int length = 0;

        while ((length = dis.read(bytes, 0, bytes.length)) != -1) {

            DeEnCode.decode(bytes,length);

            bos.write(bytes, 0, length);

            bos.flush();

        }

        Logger.info(" Size:" + IOUtil.getFormatFileSize(fileLength));

        long endTime = System.currentTimeMillis();

        Logger.info("block IO 传输毫秒数:" + (endTime - startTime));

        bos.flush();

        byte[] result = bos.toByteArray();

        IOUtil.closeQuietly(bos);

        return result;

    }

}

与前面的基础案例SafeSocketClient 相比,只有一行代码的差别。这个类仅仅增加的一行是:

DeEnCode.decode(bytes,length);

其目的是,在接受字节码之后,使用定义的解密函数,进行字节码解密。

案例路径:com.crazymakercircle.classLoader.SafeSocketClient

此案例类没法独立运行,因为没有运行的入口。只能作为基础类,供其他类调用。

下面介绍一下加密和解密的算法。

1.1.3. 使用亦或实现简单加密和解密算法

加密和解密算法有很多,这里为为了演示方便,使用亦或的方式,实现简单加密和解密算法。

简单粗暴,直接上代码:

public class DeEnCode {

    private static final String key0 = "FECOI()*&<MNCXZPKL";

    private static final Charset charset = Charset.forName("UTF-8");

    private static byte[] keyBytes = key0.getBytes(charset);

    public static void encode(byte[] b, int length) {

        for (int i = 0, size =length; i < size; i++) {

            for (byte kb : keyBytes) {

                b[i] = (byte) (b[i] ^ kb);

            }

        }

    }

    public static void decode(byte[] dee, int length) {

        for (int i = 0, size =length; i < size; i++) {

            for (byte kb : keyBytes) {

                dee[i] = (byte) (dee[i] ^ kb);

            }

        }

    }

    public static void main(String[] args) {

        String s = "you are right ok 测试";

        byte[] sb = s.getBytes();

        encode(sb,sb.length);

        decode(sb, sb.length);

        Logger.info(new String(sb));

    }

}

案例路径:com.crazymakercircle.util.DeEnCode

无编程不创客、无案例不学习。一定要跑案例哦

运行上面的main方法,测试一下加密和解密。结果是:

main |>  you are right ok 测试

从结果可以看到,解密后的数据和加密前的数据是一致的。说明这组算法是没有问题的。

1.1.4. 网络加密SafeClassLoader的源码

与前面的网络类加载器SocketClassLoader,只有一行之差。

源码如下:

public class SafeClassLoader extends ClassLoader {

    public SafeClassLoader() {

    }

    protected Class<?> findClass(String name)

throws ClassNotFoundException {

       Logger.info("findClass name = " + name);

        byte[] classData = null;

        try {

            SafeSocketClient client = new SafeSocketClient();

            classData = client.getFile(name);

        } catch (Exception e) {

            e.printStackTrace();

            throw new ClassNotFoundException();

        }

        if (classData == null) {

            throw new ClassNotFoundException();

        } else {

            return defineClass(name, classData, 0, classData.length);

        }

    }

}

1.1.5. SafeSocketLoader的使用

简单粗暴,先上代码:

package com.crazymakercircle.classLoaderDemo.net;

import com.crazymakercircle.classLoader.SafeClassLoader;

import com.crazymakercircle.config.SystemConfig;

import com.crazymakercircle.petStore.pet.IPet;

import com.crazymakercircle.util.ClassLoaderUtil;

import com.crazymakercircle.util.Logger;

public class SafeNetDemo {

    public static void testLoader() {

        try {

            SafeClassLoader classLoader = new SafeClassLoader();

            String className = SystemConfig.PET_DOG_CLASS;

            Class dogClass = classLoader.loadClass(className);

            Logger.info("显示dogClass的ClassLoader =>");

            ClassLoaderUtil.showLoader4Class(dogClass);

            IPet pet = (IPet) dogClass.newInstance();

            pet.sayHello();

        } catch (ClassNotFoundException e) {

            e.printStackTrace();

        } catch (IllegalAccessException e) {

            e.printStackTrace();

        } catch (InstantiationException e) {

            e.printStackTrace();

        }

    }

    public static void main(String[] args) {

        testLoader();

    }

}

案例路径:com.crazymakercircle.classLoaderDemo.base.SafeSocketDemo

案例提示:无编程不创客、无案例不学习。一定要跑案例哦

运行的结果,与前面的SocketLoaderDemo结果是相同的,这里不在赘述。

源码:

代码工程:  classLoaderDemo.zip

下载地址:在疯狂创客圈QQ群文件共享。

疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得
QQ群链接:疯狂创客圈QQ群

无编程不创客,无案例不学习。 一定记得去跑一跑案例哦

类加载器系列全目录

1.导入

2. JAVA类加载器分类

3. 揭秘ClassLoader抽象基类

4. 神秘的双亲委托机制

5. 入门案例:自定义一个文件系统的classLoader

6. 基础案例:自定义一个网络类加载器

7. 中级案例:设计一个加密的自定义网络加载器

8. 高级案例1:使用ASM技术,结合类加载器,解密AOP原理

9. 高级案例2:上下文加载器原理和案例

Java类加载器( 死磕7)的更多相关文章

  1. Java类加载器(死磕 1-2)

      Java类加载器(  CLassLoader ) 死磕 1.2:  导入 & 类加载器分类 本小节目录 1.导入 1.1. 从class文件的载入开始 1.2. 什么是类加载器 2. JA ...

  2. Java类加载器(死磕5)

    Java类加载器(  CLassLoader )  死磕5:  自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...

  3. Java类加载器( 死磕9)

    [正文]Java类加载器(  CLassLoader ) 死磕9:  上下文加载器原理和案例 本小节目录 9.1. 父加载器不能访问子加载器的类 9.2. 一个宠物工厂接口 9.3. 一个宠物工厂管理 ...

  4. Java类加载器( 死磕8)

    [正文]Java类加载器(  CLassLoader ) 死磕 8:  使用ASM,和类加载器实现AOP 本小节目录 8.1. ASM字节码操作框架简介 8.2. ASM和访问者模式 8.3. 用于增 ...

  5. Java类加载器( 死磕 6)

    [正文]Java类加载器(  CLassLoader )死磕 6:  自定义网络类加载器 本小节目录 6.1. 自定义网络类加载器的类设计 6.2. 文件传输Server端的源码 6.3. 文件传输C ...

  6. Java类加载器( 死磕 4)

    [正文]Java类加载器(  CLassLoader ) 死磕 之4:  神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. ...

  7. Java类加载器(死磕3)

    [正文]Java类加载器(  CLassLoader ) 死磕3:  揭秘 ClassLoader抽象基类 本小节目录 3.1. 类的加载分类:隐式加载和显示加载 3.2. 加载一个类的五步工作 3. ...

  8. java笔记--理解java类加载器以及ClassLoader类

    类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...

  9. java类加载器深入研究

    看了下面几篇关于类的加载器的文章,豁然开朗.猛击下面的地址开始看吧. Java类加载原理解析      深入探讨 Java 类加载器 分析BootstrapClassLoader/ExtClassLo ...

随机推荐

  1. sublime text3 cssrem 快速px转rem插件

    今天试验了下cssrem  在移动端如果需要px->rem非常方便 比较之前我自己用gulp提供的函数unit(70/@base,rem);转方便很多 1.git clone https://g ...

  2. python3基础语法(标识符,注释,数据类型,获取用户输出,代码块,python保留字)

    编码 默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串. 当然你也可以为源码文件指定不同的编码: # -*- coding: cp-1252 -*- ...

  3. linux 下高精度时间

    今天在公司代码中看到了使用select函数的超时功能作定时器的用法,便整理了如下几个Linux下的微秒级别的定时器.在我的Ubutu10.10 双核环境中,编译通过. /* * @FileName:  ...

  4. AC日记——魔术球问题 洛谷 P2765

    题目描述 «问题描述: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球. (1)每次只能在某根柱子的最上面放球. (2)在同一根柱子中,任何2个相邻球的编号之和为完全 ...

  5. 蚂蚁金服CTO程立:金融级分布式交易的技术路径

    总结: 强一致的微服务 oceanbase里面的投票选举以及多中心多地部署 单元化市异地多活的基础.支付宝是异地多活和容灾结合,而容灾的基础也是单元化.基于单元化进行单元的调度.部署.容灾. 混合云架 ...

  6. [NSThread sleepForTimeInterval:3.0];

    在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)laun ...

  7. android -- 存储byte

    public static String byteArrayToHexStr(byte[] byteArray) { if (byteArray == null){ return null; } ch ...

  8. Android View 布局流程(Layout)完全解析

    前言 上一篇文章,笔者详细讲述了View三大工作流程的第一个,Measure流程,如果对测量流程还不熟悉的读者可以参考一下上一篇文章.测量流程主要是对View树进行测量,获取每一个View的测量宽高, ...

  9. 如何使用ssh远程编辑定时任务crontab?

    linxu定时任务使用crontab,编辑crontab可以直接编辑:crontab -e:也可以直接读取文件 crontab file.这两种操作都不需要特殊权限sudo.区别在于,crontab ...

  10. 最新最全的 Android 开源项目合集

    原文链接:https://github.com/opendigg/awesome-github-android-ui 在 Github 上做了一个很新的 Android 开发相关开源项目汇总,涉及到 ...