# EnDecryption **Repository Path**: fd-a/EnDecryption ## Basic Information - **Project Name**: EnDecryption - **Description**: 基于cglib,zookeeper,netty的加解密框架 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-04-03 - **Last Updated**: 2024-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # EnDecryption框架 ## 1. 介绍 EnDecryption框架采用多种设计模式实现高度可定制化,支持本地和远程两种加解密方式的加解密框架 ## 2. 软件架构 ### 2.1 模块介绍 该框架分为五个模块: 1. EnDecryption-algorithm 算法模块,该模块为其余四个模块的基础 实现其中的IEnDecryption接口或继承AbstractEnDecryptionWithKeyPair抽象类即可定制私人加解密算法 2. EnDecryption-common 通用基础模块,该模块封装了大量通用类和工具类 3. EnDecryption-api api调用模块,该模块为最重要的模块,采用动态代理,工厂模式,状态链等多种设计模式实现高度可定制化 有两种使用方式,分别为: 1. 使用com.qust.enDecrpytion.annotations包中的注解标记类,再使用com.qust.enDecrpytion.proxy.builder.EnDecryptionProxyBuilder$proxy(T target)方法即可实现加解密调用 2. 直接使用com.qust.enDecrpytion.proxy.builder.EnDecryptionProxyBuilder$proxy(T target, IEnDecryption enDecryption, Class[] handlers)方法也可以实现加解密调用 4. EnDecryption-netty-server 远程加解密模块,该模块借鉴hadoop,hbase的架构,采用netty+zookeeper搭建加解密集群,该模块扮演从节点 远程集群采用主从架构,将在下章节详细讲解 5. EnDecryption-manager 远程集群管理模块,扮演主节点,使用spring boot搭建 ### 2.2 EnDecryption-api模块架构 #### 两次代理操作 image-20240904141303352 原始类在经过一系列操作后会被放在内部加密层中,内部加密层外还有一层解密层 #### 加解密执行顺序 image-20240904142048016 在使用方法时 ​ 参数进行加解密的顺序为先进入解密层然后在进入加密层 ​ 返回值进行加解密的顺序为先进入加密层然后进入解密层 #### 缓存加解密参数位置 image-20240904142657260 在第一次进行使用被加解密修饰的方法时,会对原始类中需要加解密的参数位置进行进行,并存入到一个中Map>中,方便以后多次调用节省时间 #### TypeHandleContext 加解密状态链 image-20240904144229151 真正的加解密操作并不在加解密层执行,而是委托给了TypeHandleContext,它其中有一个handle链用于处理各种类型的加解密操作,目前只实现了两个类型,string类型和pojo类型,继承AbstractTypeHandle抽象类并在@EnDecryptionConfiguration$customHandlers中配置即可实现特定类型的加解密操作 #### 加解密算法 image-20240904145111817 加解密算法是在解析原始类时装配的,加解密状态链依赖加解密算法,加解密状态链的具体加解密方法为 ```java T handle(T target,boolean cache ,Class annotation, StatusHandleFunction function) throws IllegalAccessException, Exception; ``` * target :传入需要加解密的参数 * cache :是否需要缓存加解密的结果 * annotation : 执行操作为加密还是解密 * function : 该参数为函数,需要实现StatusHandleFunction$enDecryption(String value)方法 * 该参数的设置是为了简化加解密层的操作 * 在加密层中只需要传入s -> enDecryption.encryption(s)即可 * 在解密层中只需要传入s -> enDecryption.decryption(s)即可 **注意** ``` 在使用本地加解密算法时,一定要先使用com.qust.encryptionAndDecryption.KeyPair$generateKeyPair(Class encryptionAlgorithms, String savePath, String password)方法生成密钥 ``` #### 全局缓存 ![image-20240904150235985](assets/image-20240904150235985.png) LRU缓存为静态资源,在加解密状态链中可以选择是否缓存加解密结果 **注意:**建议不缓存加解密后的结果,缓存之后相同的原文只会有一种密文,容易遭受攻击 #### 动态代理解析过程 image-20240904150904533 动态代理解析的过程实际上时对@EnDecryptionConfiguration注解解析的过程,该注解相当于配置注解 ```java package com.qust.enDecrpytion.annotations; import com.qust.enDecrpytion.proxy.status.AbstractTypeHandle; import com.qust.enDecrpytion.proxy.status.PoJoTypeHandle; import com.qust.enDecrpytion.proxy.status.StringTypeHandle; import com.qust.encryptionAndDecryption.IEnDecryption; import java.lang.annotation.*; @Documented @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface EnDecryptionConfiguration { Class[] customHandlers() default {StringTypeHandle.class, PoJoTypeHandle.class}; // 加解密状态链 Local[] local() default {}; // 本地加解密 Remote[] remote() default {}; // 远程加解密 @Documented @Target({}) @Retention(RetentionPolicy.RUNTIME) @interface Local { // 该注解为内部注解,用于标注为采用本地加解密方法 Class enDecryption(); // 加解密算法 String keyPath(); // 密钥存储地址 String password(); // 解析密钥的密码 } @Documented @Target({}) @Retention(RetentionPolicy.RUNTIME) @interface Remote { // 用于表示采用远程加解密方法 String managerAddress() default "localhost:8086"; // 管理节点的地址 int algorithmCode(); // 算法编号 } // 如果两种注解都配置了,会首先选择本地方法 } ``` #### 总结 EnDecryption-api模块的架构如下图: image-20240904150904533 该模块提供了两个可拓展点,加解密算法和加解密状态链 接下来的远程加解密就是重写了加解密算法实现,本地架构于该模块相同,只是多了远程加解密集群 ### 2.3 远程加解密架构 #### 客户端 ##### channel连接池 ![image-20240904154041400](assets/image-20240904154041400.png) 在该框架中使用的连接池时基于循环队列的连接池,利用循环队列可以提高连接的利用率 可以设置loopool的核心连接数和最大连接数,同时还可以自定义拒绝策略 ##### RemoteEnDecryption ![image-20240905114453827](assets/image-20240905114453827.png) RemoteEnDecryption实现了IEnDecryption,是远程加解密的拓展,在该系统中扮演客户端的角色,通过配置RemoteEnDecryption即可实现远程加解密操作 其内部包含LooPool,通过LooPool发送请求 #### 服务端 ##### EnDecryption-netty-server ###### 协议 协议内容如下: * 魔数,4字节 byte[]{'f','a','n','!'} * 版本号,4字节 * 现在版本为1,为之后更新做准备 * 获取序列化方式,4字节 * 序列化方式有多种,可自定义,该系统使用json * 获取指令,4字节 * ```java @Getter public enum InstructionEnum { encryption(0), // 加密 decryption(1), // 解密 tokenAuthentication(2), // 权限,还未实现 transmissionAlgorithm(3), // 算法资源传输 response(4), // 回复 algorithmicResourceCatchUp(5), // 算法资源追赶是否成功 @Deprecated getMD5(6), // 获取metaData的md5码,已弃用 resourceCatchUp(7); // 请求资源追赶 private final int code; InstructionEnum(int code){ this.code = code; } } ``` * 获取序列化对象,4字节 * ```java @Getter public enum SerializationEnum { json(0), // json序列化 java(1), // java序列化 string(2), // string类型序列化 Byte(3); // byte类型序列化 private final int code; SerializationEnum(int code){ this.code = code; } } ``` * 写入长度,4字节 int类型 * 写入内容 * ```java @Data @AllArgsConstructor @NoArgsConstructor public class Message implements Serializable{ protected int instruction; // 指令,加密,解密,传输算法实例等等 protected int serialization; // 序列化方式,json,java序列化等 protected Object context; // 内容 } ``` ###### 集群组建过程 **假设所有子节点的算法资源都是完整的,不需要资源追赶阶段** ![image-20240905124859608](assets/image-20240905124859608.png) 每个server开启时会在/EnDecryption/server中注册一个**顺序临时节点** , 并且对/EnDecryption/server节点添加**PathChildrenCache监听器** * 为什么是**顺序临时节点**,为什么添加**PathChildrenCache监听器** * 顺序的目的是为了创建一个如图的链式结构,依次连接下一个节点,为**资源追赶**阶段做准备 * 当某一个节点挂掉之后,**临时节点**会消失,此时会触发**PathChildrenCache监听器**,该监听器会重新生成新的链式结构,保障除最后一个节点外的所有节点都有**NextNode** ###### 监控功能 该功能会另起一个线程,开启websocket实时传输此机器的状态 ##### EnDecryption-manager EnDecryption-manager有三个主要功能 ![image-20240905130135716](assets/image-20240905130135716.png) ###### 负载均衡 启动系统时应先启动EnDecryption-manager,之后再启动EnDecryption-netty-server **EnDecryption-manager先启动原因:** 1. EnDecryption-manager启动时,会对/EnDecryption/server添加**PathChildrenCache监听器** 2. 当有节点在/EnDecryption/server注册时,获取节点的名称和内容,并缓存 * 节点名称格式为: ip:port;001 * 节点内容格式为: * ```json { weigh:1, // 节点的权重,不变值 currentWeight:0, // 节点的currentWeight,可变 address:localhost:8086 // 节点的地址 } ``` 3. manager节点会缓存头节点的地址,并不会立马连接,而是等到需要连接时才连接 负载均衡采用WRR[负载均衡-WRR算法 | 胖虎de文库 (exceting.github.io)](https://exceting.github.io/2020/08/07/负载均衡-WRR算法/) , 每个节点的权重为该机器的cpu核心数 **RemoteEnDecryption(客户端)获取server节点过程** 1. EnDecryption-manager启动并且有server节点注册成功 2. 客户端解析@EnDecryptionConfiguration$remote()参数之后,获取managerAddress 3. 访问http://"+managerAddress+"/LoadBalance/"+numberOfCores,获取核心连接节点的地址 4. 采用懒汉模式,获取之后立即创建连接 5. 将连接缓存到LooPool中 ###### 资源追赶 **当新节点的算法资源少于最新的算法资源时,会进入资源追赶阶段** 资源追赶部分特点为: * 采用链式结构 * 此节点的算法资源只能由上一个节点进行传输 * 头节点的资源由manager节点传输 * 算法资源信息保存在mysql中,只能通过manager节点访问获得 * server节点的算法资源信息保存在,metaData.json中 **资源追赶过程:** 1. manager启动后会处理metaDate.json,并存储到/EnDecryption/config/meta中 2. server启动后会查询/EnDecryption/config/meta中的内容,并于自己本地的metaDate.json对比 3. 如果一致,不做任何处理,manager将节点内容缓存到负载均衡器中 4. 如果不一致,server节点计算缺失算法的id,并发送请求 * post请求 * http://+ manager +/algorithmController/catchUp * ```json { missing:set(), // 缺少的算法id address:"" // 需要追赶的地址 } ``` 5. 同时,该节点会拿到zookeeper的分布式锁 * **为什么要使用分布式锁** * 当有多个需要追赶的server节点注册之后会同时请求,但是上一个节点的资源还处于资源追赶阶段,所以无法传输算法资源,此时就容易导致错误 * 解决方案: 当该节点拿到分布式锁之后才会发送请求,此时它的上一个节点肯定已经到达最新资源位置 6. manager节点连接缓存的头节点,发送资源追赶指令,让其上一个节点发送算法资源 7. 当该节点的算法资源达到最新时,连接下一个节点(如果有的话),释放锁 8. manager将其写入负载均衡器 ###### 算法的上传和删除 **上传**: 1. 通过POST http:// + manager + /algorithmController接口上传给manager节点 2. manager节点在将该算法资源传给头节点 3. 通过链式结构挨个传输 4. server节点传输完成之后会回复成功给manager节点 5. manager节点回复完成传输 **删除**: 删除资源只是删除了metaData.json中的信息,并没有实际删除具体的jar包 ## 3. 使用教程 ```java package com.qust; import com.qust.enDecrpytion.annotations.Decryption; import com.qust.enDecrpytion.annotations.EnDecryptionConfiguration; import com.qust.enDecrpytion.annotations.EnableEnDecryption; import com.qust.enDecrpytion.annotations.Encryption; import com.qust.enDecrpytion.encryptionAndDecryption.local.SM2; import com.qust.enDecrpytion.proxy.builder.EnDecryptionProxyBuilder; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @EnDecryptionConfiguration( // remote = @EnDecryptionConfiguration.Remote( // algorithmCode = 17 // ), local = @EnDecryptionConfiguration.Local( enDecryption = SM2.class, keyPath = "EnDecryption-api/src/test/java/com/qust/fan.properties", password = "fan" ) ) public class Hello { private int id; public Hello(int id) { this.id = id; } public Hello() { } @EnableEnDecryption public @Encryption String hello(@Encryption String name, @Encryption Demo demo) { System.out.println("name:\n" + name); System.out.println("demo.id:\n" + demo.getId()); return "1"; } public static void main(String[] args) throws Exception { Hello hello = EnDecryptionProxyBuilder.proxy(new Hello(1)); System.out.println("hello返回值:\n" + hello.hello("asd",new Demo("asd", "dasd"))); System.out.println(hello); } @Data @NoArgsConstructor @AllArgsConstructor static class Demo { @Decryption private String name; @Encryption private String id; } } ``` 运行结果: ``` name: 04cf2201341f2979ff876d03531526b6953ac66556869a2c1b2a1ad8aca58ba8db44251c120cff8cc15e92b8585d12f357074bc7d83dd8f9764330adef3921bec8f8380ad41e2d2b63ffdb7e70772847a9c67e24843f7b700ef415b801b8f963b7c8fccb3c demo.id: 048092962fb270cefeaf66067781700dafbb2a4de827e389acc3226ec73562e550f88e29d8edc9f81938f3be53f8e48489c58bcc810f7221e3cc37678d8c7ba610e7c0c60d5a52c09b21372bcc45c9fd7a02bbf9e54bd39c9a2975bc295530e0bd529e6d3bbe340edd hello返回值: 04028171b828b288725cc5b090c03380f341cd18b91434a68058bee9767425f780b0ac792a24fb8af8b74d8651b0bfb2cb9fe4fae28e298241c3235ebb32d18422d7ec868610b03076a19d328844a6a998e2bec5270ed20fdf4336e211060f39433d4c7e12 Hello(id=1) ``` ## 使用说明 1. 测试版本,肯定会有许多bug,敬请原谅