├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── github │ │ └── puhiayang │ │ ├── EasyHttpProxyServer.java │ │ ├── bean │ │ ├── ClientRequest.java │ │ └── Constans.java │ │ ├── handler │ │ ├── proxy │ │ │ ├── HttpProxyHandler.java │ │ │ ├── HttpsProxyHandler.java │ │ │ ├── IProxyHandler.java │ │ │ └── SocksProxyHandler.java │ │ └── response │ │ │ ├── HttpProxyResponseHandler.java │ │ │ └── SocksResponseHandler.java │ │ └── utils │ │ ├── HttpsSupport.java │ │ └── ProxyRequestUtil.java └── resources │ ├── ca.crt │ ├── ca.key │ ├── ca_private.der │ └── logback.xml └── test └── java └── com └── github └── puhaiyang └── EasyHttpProxyServerStart.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ping 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easyHttpProxy 2 | [![author](https://img.shields.io/badge/author-puhaiyang-brightgreen)](https://github.com/puhaiyang) 3 | [![blog](https://img.shields.io/badge/blog-csdn-brightgreen)](https://blog.csdn.net/puhaiyang) 4 | [![License](https://img.shields.io/github/license/puhaiyang/easyHttpProxy)](https://github.com/puhaiyang/easyHttpProxy/blob/master/LICENSE) 5 | 6 | support http/https proxy.类似于fiddler,由java编写,代码简单便于理解。支持http/https代理! 7 | 8 | ## 环境 9 | [![jdk](https://img.shields.io/badge/jdk-1.8%2B-brightgreen)](https://github.com/puhaiyang) 10 | [![jdk](https://img.shields.io/badge/netty-4.1%2B-brightgreen)](https://github.com/puhaiyang) 11 | [![jdk](https://img.shields.io/badge/maven-3.0%2B-brightgreen)](https://github.com/puhaiyang) 12 | - jdk 1.8+ 13 | - maven 3.0+ 14 | - netty 4.1+ 15 | 16 | ## 使用方法 17 | 添加依赖 18 | ``` 19 | 20 | com.github.puhaiyang 21 | easy-http-proxy 22 | 0.0.1 23 | 24 | ``` 25 | 启动代理服务器: 26 | > EasyHttpProxyServer.getInstace().start(6667); 27 | 28 | ## 生成ca根证书 29 | - 下载openssl工具 30 | > [https://slproweb.com/products/Win32OpenSSL.html][https://slproweb.com/products/Win32OpenSSL.html],在windows下默认会在C:\Program Files\Open SSL Win64目录下,需要将其配到path 31 | 32 | 说明 33 | ``` 34 | openssl genrsa 35 | 功能: 36 | 用于生成RSA私钥,不会生成公钥,因为公钥提取自私钥 37 | 使用参数: 38 | openssl genrsa [-out filename] [-passout arg] [-des] [-des3] [-idea] [numbits] 39 | 选项说明: 40 | -out filename :将生成的私钥保存至filename文件,若未指定输出文件,则为标准输出。 41 | -numbits :指定要生成的私钥的长度,默认为1024。该项必须为命令行的最后一项参数。 42 | -des|-des3|-idea:指定加密私钥文件用的算法,这样每次使用私钥文件都将输入密码,太麻烦所以很少使用。 43 | -passout args :加密私钥文件时,传递密码的格式,如果要加密私钥文件时单未指定该项,则提示输入密码。传递密码的args的格式见一下格式。 44 | a.pass:password :password表示传递的明文密码 45 | b.env:var :从环境变量var获取密码值 46 | c.file:filename :filename文件中的第一行为要传递的密码。若filename同时传递给"- 47 | passin"和"-passout"选项,则filename的第一行为"-passin"的值,第 48 | 二行为"-passout"的值 49 | d.stdin :从标准输入中获取要传递的密码 50 | ``` 51 | - 生成rsa私钥,输出ca.key文件 52 | > openssl genrsa -des3 -out ca.key 2048 53 | - 生成ca根证书,以ca.key作为私钥,输出ca.crt证书 54 | > openssl req -sha256 -new -x509 -days 36500 -key ca.key -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=haiyang/OU=study/CN=HaiyangProxy" 55 | - 将ca.key私钥转为der格式的私钥,输出ca_private.der文件 56 | > openssl pkcs8 -topk8 -nocrypt -inform PEM -outform DER -in ca.key -out ca_private.der 57 | 58 | [https://slproweb.com/products/Win32OpenSSL.html]: https://slproweb.com/products/Win32OpenSSL.html -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.puhaiyang 8 | easy-http-proxy 9 | 0.0.1 10 | jar 11 | 12 | easy-http-proxy 13 | support http/https proxy.类似于fiddler,由java编写,代码简单便于理解。支持http/https代理! 14 | https://github.com/puhaiyang/easyHttpProxy 15 | 16 | 17 | 18 | The MIT License (MIT) 19 | http://opensource.org/licenses/mit-license.php 20 | 21 | 22 | 23 | 24 | 25 | puhaiyang 26 | 761396462@qq.com 27 | 28 | 29 | 30 | 31 | scm:git:https://github.com/puhaiyang/easyHttpProxy.git 32 | scm:git:https://github.com/puhaiyang/easyHttpProxy.git 33 | https://github.com/puhaiyang/easyHttpProxy 34 | 35 | 36 | 37 | UTF-8 38 | 1.8 39 | 1.8 40 | 4.1.42.Final 41 | 1.58 42 | 3.9 43 | 44 | 45 | 46 | 47 | 48 | org.apache.commons 49 | commons-lang3 50 | ${commons-lang3.version} 51 | 52 | 53 | 54 | org.slf4j 55 | slf4j-api 56 | 1.7.25 57 | 58 | 59 | ch.qos.logback 60 | logback-core 61 | 1.2.0 62 | 63 | 64 | ch.qos.logback 65 | logback-classic 66 | 1.2.0 67 | 68 | 69 | 70 | 71 | io.netty 72 | netty-all 73 | ${netty.version} 74 | 75 | 76 | 77 | org.bouncycastle 78 | bcpkix-jdk15on 79 | ${bcpkix.version} 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-compiler-plugin 88 | 89 | 1.8 90 | 1.8 91 | 92 | 93 | 94 | 95 | 96 | 97 | release 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-compiler-plugin 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-source-plugin 109 | 110 | 111 | package 112 | 113 | jar-no-fork 114 | 115 | 116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-javadoc-plugin 122 | 123 | 124 | package 125 | 126 | jar 127 | 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-gpg-plugin 135 | 1.0 136 | 137 | 138 | sign-artifacts 139 | verify 140 | 141 | sign 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | sonatype-nexus-staging 151 | https://oss.sonatype.org/service/local/staging/deploy/maven2 152 | 153 | 154 | sonatype-nexus-snapshots 155 | https://oss.sonatype.org/content/repositories/snapshots 156 | 157 | 158 | 159 | 160 | exec 161 | 162 | 163 | 164 | org.apache.maven.plugins 165 | maven-assembly-plugin 166 | 2.5.5 167 | 168 | 169 | 170 | com.github.puhiayang.EasyHttpProxyServer 171 | 172 | 173 | 174 | jar-with-dependencies 175 | 176 | 177 | 178 | 179 | make-assembly 180 | package 181 | 182 | single 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/EasyHttpProxyServer.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang; 2 | 3 | 4 | import com.github.puhiayang.handler.proxy.HttpProxyHandler; 5 | import com.github.puhiayang.handler.proxy.HttpsProxyHandler; 6 | import com.github.puhiayang.handler.proxy.SocksProxyHandler; 7 | import io.netty.bootstrap.ServerBootstrap; 8 | import io.netty.channel.*; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import io.netty.handler.codec.http.HttpObjectAggregator; 12 | import io.netty.handler.codec.http.HttpRequestDecoder; 13 | import io.netty.handler.codec.http.HttpResponseEncoder; 14 | import io.netty.handler.logging.LogLevel; 15 | import io.netty.handler.logging.LoggingHandler; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | 20 | /** 21 | * EasyHttpProxyServer 22 | * created on 2019/10/25 14:44 23 | * 24 | * @author puhaiyang 25 | */ 26 | public class EasyHttpProxyServer { 27 | private Logger logger = LoggerFactory.getLogger(EasyHttpProxyServer.class); 28 | private static EasyHttpProxyServer instace = new EasyHttpProxyServer(); 29 | 30 | public static EasyHttpProxyServer getInstace() { 31 | if (instace == null) { 32 | instace = new EasyHttpProxyServer(); 33 | } 34 | return instace; 35 | } 36 | 37 | public static void main(String[] args) { 38 | System.out.println("start proxy server"); 39 | int port = 6667; 40 | if (args.length > 0) { 41 | port = Integer.valueOf(args[0]); 42 | } 43 | new EasyHttpProxyServer().start(port); 44 | } 45 | 46 | /** 47 | * 启动 48 | * 49 | * @param listenPort 监控的端口 50 | */ 51 | public void start(int listenPort) { 52 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 53 | EventLoopGroup workerGroup = new NioEventLoopGroup(2); 54 | try { 55 | ServerBootstrap b = new ServerBootstrap(); 56 | b.group(bossGroup, workerGroup) 57 | .channel(NioServerSocketChannel.class) 58 | .option(ChannelOption.SO_BACKLOG, 100) 59 | .option(ChannelOption.TCP_NODELAY, true) 60 | .handler(new LoggingHandler(LogLevel.INFO)) 61 | .childHandler(new ChannelInitializer() { 62 | 63 | @Override 64 | protected void initChannel(Channel ch) throws Exception { 65 | //ch.pipeline().addLast("httpCodec", new HttpServerCodec()); 66 | //接收客户端请求,将客户端的请求内容解码 67 | ch.pipeline().addLast("httpRequestDecoder", new HttpRequestDecoder()); 68 | //发送响应给客户端,并将发送内容编码 69 | ch.pipeline().addLast("httpResponseEncoder", new HttpResponseEncoder()); 70 | ch.pipeline().addLast("httpAggregator", new HttpObjectAggregator(65536)); 71 | ch.pipeline().addLast("httpProxyHandler", new HttpProxyHandler()); 72 | ch.pipeline().addLast("httpsProxyHandler", new HttpsProxyHandler()); 73 | ch.pipeline().addLast("socksProxyHandler", new SocksProxyHandler()); 74 | } 75 | }); 76 | logger.info("[EasyHttpProxyServer] proxy server start on {} port", listenPort); 77 | ChannelFuture f = b 78 | .bind(listenPort) 79 | .sync(); 80 | f.channel().closeFuture().sync(); 81 | } catch (Exception e) { 82 | e.printStackTrace(); 83 | } finally { 84 | bossGroup.shutdownGracefully(); 85 | workerGroup.shutdownGracefully(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/bean/ClientRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 客户端请求 7 | * 8 | * @author puhaiyang 9 | * created on 2019/10/25 22:37 10 | */ 11 | public class ClientRequest implements Serializable { 12 | private String host; 13 | private int port; 14 | private boolean isHttps; 15 | 16 | 17 | public ClientRequest(String host, int port) { 18 | this.host = host; 19 | this.port = port; 20 | } 21 | 22 | public boolean isHttps() { 23 | return isHttps; 24 | } 25 | 26 | public void setHttps(boolean https) { 27 | isHttps = https; 28 | } 29 | 30 | public String getHost() { 31 | return host; 32 | } 33 | 34 | public void setHost(String host) { 35 | this.host = host; 36 | } 37 | 38 | public int getPort() { 39 | return port; 40 | } 41 | 42 | public void setPort(int port) { 43 | this.port = port; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/bean/Constans.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.bean; 2 | 3 | import io.netty.handler.codec.http.HttpResponseStatus; 4 | import io.netty.util.AttributeKey; 5 | 6 | /** 7 | * 常量 8 | * 9 | * @author puhaiyang 10 | * created on 2019/10/25 22:51 11 | */ 12 | public class Constans { 13 | /** 14 | * connect类型请求名称 15 | */ 16 | public static final String CONNECT_METHOD_NAME = "CONNECT"; 17 | /** 18 | * https连接协议名 19 | */ 20 | public static final String HTTPS_PROTOCOL_NAME = "https"; 21 | /** 22 | * 代理握手成功响应status 23 | */ 24 | public final static HttpResponseStatus CONNECT_SUCCESS = new HttpResponseStatus(200, "Connection established"); 25 | /** 26 | * channel中的clientReuqest 27 | */ 28 | public static final AttributeKey CLIENTREQUEST_ATTRIBUTE_KEY = AttributeKey.valueOf("clientRequest"); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/handler/proxy/HttpProxyHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.handler.proxy; 2 | 3 | import com.github.puhiayang.bean.ClientRequest; 4 | import com.github.puhiayang.bean.Constans; 5 | import com.github.puhiayang.handler.response.HttpProxyResponseHandler; 6 | import com.github.puhiayang.utils.ProxyRequestUtil; 7 | import io.netty.bootstrap.Bootstrap; 8 | import io.netty.channel.*; 9 | import io.netty.handler.codec.http.*; 10 | import io.netty.util.Attribute; 11 | import io.netty.util.ReferenceCountUtil; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import static com.github.puhiayang.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY; 16 | 17 | /** 18 | * 对http请求进行代理 19 | * created on 2019/10/25 18:00 20 | * 21 | * @author puhaiyang 22 | */ 23 | public class HttpProxyHandler extends ChannelInboundHandlerAdapter implements IProxyHandler { 24 | private Logger logger = LoggerFactory.getLogger(HttpProxyHandler.class); 25 | 26 | @Override 27 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 28 | logger.debug("[HttpProxyHandler]"); 29 | if (msg instanceof HttpRequest) { 30 | HttpRequest httpRequest = (HttpRequest) msg; 31 | //获取客户端请求 32 | ClientRequest clientRequest = ProxyRequestUtil.getClientRequest(ctx.channel()); 33 | if (clientRequest == null) { 34 | //从本次请求中获取 35 | Attribute clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY); 36 | clientRequest = ProxyRequestUtil.getClientReuqest(httpRequest); 37 | //将clientRequest保存到channel中 38 | clientRequestAttribute.setIfAbsent(clientRequest); 39 | } 40 | //如果是connect代理请求,返回成功以代表代理成功 41 | if (sendSuccessResponseIfConnectMethod(ctx, httpRequest.method().name())) { 42 | logger.debug("[HttpProxyHandler][channelRead] sendSuccessResponseConnect"); 43 | ctx.channel().pipeline().remove("httpRequestDecoder"); 44 | ctx.channel().pipeline().remove("httpResponseEncoder"); 45 | ctx.channel().pipeline().remove("httpAggregator"); 46 | ReferenceCountUtil.release(msg); 47 | return; 48 | } 49 | if (clientRequest.isHttps()) { 50 | //https请求不在此处转发 51 | super.channelRead(ctx, msg); 52 | return; 53 | } 54 | sendToServer(clientRequest, ctx, msg); 55 | return; 56 | } 57 | super.channelRead(ctx, msg); 58 | } 59 | 60 | /** 61 | * 如果是connect请求的话,返回连接建立成功 62 | * 63 | * @param ctx ChannelHandlerContext 64 | * @param methodName 请求类型名 65 | * @return 是否为connect请求 66 | */ 67 | private boolean sendSuccessResponseIfConnectMethod(ChannelHandlerContext ctx, String methodName) { 68 | if (Constans.CONNECT_METHOD_NAME.equalsIgnoreCase(methodName)) { 69 | //代理建立成功 70 | //HTTP代理建立连接 71 | HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, Constans.CONNECT_SUCCESS); 72 | ctx.writeAndFlush(response); 73 | return true; 74 | } 75 | return false; 76 | } 77 | 78 | 79 | @Override 80 | public void sendToServer(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) { 81 | Bootstrap bootstrap = new Bootstrap(); 82 | bootstrap.group(ctx.channel().eventLoop()) 83 | // 注册线程池 84 | .channel(ctx.channel().getClass()) 85 | // 使用NioSocketChannel来作为连接用的channel类 86 | .handler(new ChannelInitializer() { 87 | @Override 88 | protected void initChannel(Channel ch) throws Exception { 89 | //添加接收远程server的handler 90 | ch.pipeline().addLast(new HttpRequestEncoder()); 91 | ch.pipeline().addLast(new HttpResponseDecoder()); 92 | ch.pipeline().addLast(new HttpObjectAggregator(6553600)); 93 | //代理handler,负责给客户端响应结果 94 | ch.pipeline().addLast(new HttpProxyResponseHandler(ctx.channel())); 95 | } 96 | }); 97 | 98 | //连接远程server 99 | ChannelFuture cf = bootstrap.connect(clientRequest.getHost(), clientRequest.getPort()); 100 | cf.addListener(new ChannelFutureListener() { 101 | @Override 102 | public void operationComplete(ChannelFuture future) throws Exception { 103 | if (future.isSuccess()) { 104 | //连接成功 105 | future.channel().writeAndFlush(msg); 106 | logger.debug("[operationComplete] connect remote server success!"); 107 | } else { 108 | //连接失败 109 | logger.error("[operationComplete] 连接远程server失败了"); 110 | ctx.channel().close(); 111 | } 112 | } 113 | }); 114 | } 115 | 116 | @Override 117 | public void sendToClient(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) { 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/handler/proxy/HttpsProxyHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.handler.proxy; 2 | 3 | import com.github.puhiayang.bean.ClientRequest; 4 | import com.github.puhiayang.handler.response.HttpProxyResponseHandler; 5 | import com.github.puhiayang.utils.HttpsSupport; 6 | import io.netty.bootstrap.Bootstrap; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.channel.*; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioSocketChannel; 11 | import io.netty.handler.codec.http.*; 12 | import io.netty.handler.ssl.SslContext; 13 | import io.netty.handler.ssl.SslContextBuilder; 14 | import io.netty.util.Attribute; 15 | import io.netty.util.ReferenceCountUtil; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import static com.github.puhiayang.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY; 20 | 21 | /** 22 | * 对https请求进行代理 23 | * created on 2019/10/25 18:00 24 | * 25 | * @author puhaiyang 26 | */ 27 | public class HttpsProxyHandler extends ChannelInboundHandlerAdapter implements IProxyHandler { 28 | private Logger logger = LoggerFactory.getLogger(HttpsProxyHandler.class); 29 | private ChannelFuture httpsRequestCf; 30 | 31 | @Override 32 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 33 | logger.debug("[HttpsProxyHandler]"); 34 | Attribute clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY); 35 | ClientRequest clientRequest = clientRequestAttribute.get(); 36 | if (msg instanceof HttpRequest) { 37 | sendToServer(clientRequest, ctx, msg); 38 | } else if (msg instanceof HttpContent) { 39 | logger.debug("[HttpsProxyHandler][HttpContent]不作处理!"); 40 | //content不做处理 41 | // ReferenceCountUtil.release(msg); 42 | } else { 43 | ByteBuf byteBuf = (ByteBuf) msg; 44 | // ssl握手 45 | if (byteBuf.getByte(0) == 22) { 46 | logger.debug("[HttpsProxyHandler][do hands]"); 47 | sendToClient(clientRequest, ctx, msg); 48 | } 49 | } 50 | } 51 | 52 | @Override 53 | public void sendToServer(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) { 54 | logger.debug("[HttpsProxyHandler][sendToServer] 发送https请求到server"); 55 | Channel clientChannel = ctx.channel(); 56 | Bootstrap bootstrap = new Bootstrap(); 57 | bootstrap.group(new NioEventLoopGroup(1)) 58 | // 注册线程池 59 | .channel(NioSocketChannel.class) 60 | // 使用NioSocketChannel来作为连接用的channel类 61 | .handler(new ChannelInitializer() { 62 | 63 | @Override 64 | protected void initChannel(Channel ch) throws Exception { 65 | //添加一个ssl处理器进行处理 66 | ch.pipeline().addLast( 67 | HttpsSupport.getInstance().getClientSslCtx().newHandler(ch.alloc(), 68 | clientRequest.getHost(), clientRequest.getPort())); 69 | ch.pipeline().addLast("httpCodec", new HttpClientCodec()); 70 | //添加响应处理器 71 | ch.pipeline().addLast("proxyClientHandle", new HttpProxyResponseHandler(clientChannel)); 72 | } 73 | }); 74 | httpsRequestCf = bootstrap.connect(clientRequest.getHost(), clientRequest.getPort()); 75 | //建立连接 76 | httpsRequestCf.addListener((ChannelFutureListener) future -> { 77 | if (future.isSuccess()) { 78 | future.channel().writeAndFlush(msg); 79 | logger.debug("[HttpsProxyHandler][sendToServer]目标连接创建成功,并已转发了数据包"); 80 | } else { 81 | logger.error("[HttpsProxyHandler][sendToServer]连接远程server失败"); 82 | } 83 | }); 84 | } 85 | 86 | @Override 87 | public void sendToClient(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) { 88 | try { 89 | logger.debug("[HttpsProxyHandler][sendToClient] 与客户端进行https握手"); 90 | SslContext sslCtx = SslContextBuilder 91 | .forServer(HttpsSupport.getInstance().getServerPriKey(), HttpsSupport.getInstance().getCert(clientRequest.getHost())).build(); 92 | //接收客户端请求,将客户端的请求内容解码 93 | ctx.pipeline().addFirst("httpRequestDecoder", new HttpRequestDecoder()); 94 | //发送响应给客户端,并将发送内容编码 95 | ctx.pipeline().addFirst("httpResponseEncoder", new HttpResponseEncoder()); 96 | //http聚合 97 | ctx.pipeline().addLast("httpAggregator", new HttpObjectAggregator(65536)); 98 | //ssl处理 99 | ctx.pipeline().addFirst("sslHandle", sslCtx.newHandler(ctx.alloc())); 100 | // 重新过一遍pipeline,拿到解密后的的http报文 101 | ctx.pipeline().fireChannelRead(msg); 102 | Attribute clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY); 103 | clientRequest.setHttps(true); 104 | clientRequestAttribute.set(clientRequest); 105 | } catch (Exception e) { 106 | logger.error("[sendToServer] err:{}", e.getMessage()); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/handler/proxy/IProxyHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.handler.proxy; 2 | 3 | import com.github.puhiayang.bean.ClientRequest; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * 代理handler 8 | * 9 | * @author puhaiyang 10 | * created on 2019/10/25 23:07 11 | */ 12 | public interface IProxyHandler { 13 | /** 14 | * 发送到server 15 | * 16 | * @param clientRequest 客户端请求 17 | * @param ctx ChannelHandlerContext 18 | * @param msg 数据 19 | */ 20 | void sendToServer(ClientRequest clientRequest, final ChannelHandlerContext ctx, final Object msg); 21 | 22 | /** 23 | * 发送到client 24 | */ 25 | void sendToClient(ClientRequest clientRequest, final ChannelHandlerContext ctx, final Object msg); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/handler/proxy/SocksProxyHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.handler.proxy; 2 | 3 | import com.github.puhiayang.bean.ClientRequest; 4 | import com.github.puhiayang.handler.response.SocksResponseHandler; 5 | import io.netty.bootstrap.Bootstrap; 6 | import io.netty.channel.*; 7 | import io.netty.util.Attribute; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import static com.github.puhiayang.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY; 12 | 13 | 14 | /** 15 | * socks的代理handler 16 | * 17 | * @author puhaiyang 18 | * created on 2019/10/25 20:56 19 | */ 20 | public class SocksProxyHandler extends ChannelInboundHandlerAdapter implements IProxyHandler { 21 | private Logger logger = LoggerFactory.getLogger(HttpsProxyHandler.class); 22 | 23 | private ChannelFuture notHttpReuqstCf; 24 | 25 | @Override 26 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 27 | logger.debug("[SocksProxyHandler]"); 28 | Attribute clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY); 29 | ClientRequest clientRequest = clientRequestAttribute.get(); 30 | sendToServer(clientRequest, ctx, msg); 31 | } 32 | 33 | @Override 34 | public void sendToServer(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) { 35 | //不是http请求就不管,全转发出去 36 | if (notHttpReuqstCf == null) { 37 | //连接至目标服务器 38 | Bootstrap bootstrap = new Bootstrap(); 39 | bootstrap.group(ctx.channel().eventLoop()) 40 | // 复用客户端连接线程池 41 | .channel(ctx.channel().getClass()) 42 | // 使用NioSocketChannel来作为连接用的channel类 43 | .handler(new ChannelInitializer() { 44 | @Override 45 | protected void initChannel(Channel ch) throws Exception { 46 | ch.pipeline().addLast(new SocksResponseHandler(ctx.channel())); 47 | } 48 | }); 49 | notHttpReuqstCf = bootstrap.connect(clientRequest.getHost(), clientRequest.getPort()); 50 | notHttpReuqstCf.addListener(new ChannelFutureListener() { 51 | @Override 52 | public void operationComplete(ChannelFuture future) throws Exception { 53 | if (future.isSuccess()) { 54 | future.channel().writeAndFlush(msg); 55 | } else { 56 | ctx.channel().close(); 57 | } 58 | } 59 | }); 60 | } else { 61 | notHttpReuqstCf.channel().writeAndFlush(msg); 62 | } 63 | } 64 | 65 | @Override 66 | public void sendToClient(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) { 67 | 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/handler/response/HttpProxyResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.handler.response; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import io.netty.handler.codec.http.*; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.nio.charset.Charset; 11 | 12 | 13 | /** 14 | * https代理responseHandler 15 | * created on 2019/10/28 15:00 16 | * 17 | * @author puhaiyang 18 | */ 19 | public class HttpProxyResponseHandler extends ChannelInboundHandlerAdapter { 20 | private Logger logger = LoggerFactory.getLogger(HttpProxyResponseHandler.class); 21 | private Channel clientChannel; 22 | 23 | public HttpProxyResponseHandler(Channel clientChannel) { 24 | this.clientChannel = clientChannel; 25 | } 26 | 27 | @Override 28 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 29 | if (msg instanceof FullHttpResponse) { 30 | FullHttpResponse response = (FullHttpResponse) msg; 31 | logger.debug("[channelRead][FullHttpResponse] 接收到远程的数据1 content:{}", response.content().toString(Charset.defaultCharset())); 32 | } else if (msg instanceof DefaultHttpResponse) { 33 | DefaultHttpResponse response = (DefaultHttpResponse) msg; 34 | logger.debug("[channelRead][FullHttpResponse] 接收到远程的数据 content:{}", response.toString()); 35 | } else if (msg instanceof DefaultHttpContent) { 36 | DefaultHttpContent httpContent = (DefaultHttpContent) msg; 37 | logger.debug("[channelRead][DefaultHttpContent] 接收到远程的数据 content:{}", httpContent.content().toString(Charset.defaultCharset())); 38 | } else { 39 | logger.debug("[channelRead] 接收到远程的数据 " + msg.toString()); 40 | } 41 | //发送给客户端 42 | clientChannel.writeAndFlush(msg); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/handler/response/SocksResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.handler.response; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | 10 | /** 11 | * socks代理responseHandler 12 | * created on 2019/10/28 15:56 13 | * 14 | * @author puhaiyang 15 | */ 16 | public class SocksResponseHandler extends ChannelInboundHandlerAdapter { 17 | private Logger logger = LoggerFactory.getLogger(SocksResponseHandler.class); 18 | private Channel clientChannel; 19 | 20 | public SocksResponseHandler(Channel clientChannel) { 21 | this.clientChannel = clientChannel; 22 | } 23 | 24 | @Override 25 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 26 | //直接返回给客户端 27 | ctx.channel().writeAndFlush(msg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/utils/HttpsSupport.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.utils; 2 | 3 | import io.netty.handler.ssl.SslContext; 4 | import io.netty.handler.ssl.SslContextBuilder; 5 | import io.netty.handler.ssl.util.InsecureTrustManagerFactory; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.bouncycastle.asn1.x500.X500Name; 8 | import org.bouncycastle.asn1.x509.Extension; 9 | import org.bouncycastle.asn1.x509.GeneralName; 10 | import org.bouncycastle.asn1.x509.GeneralNames; 11 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 12 | import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; 13 | import org.bouncycastle.operator.ContentSigner; 14 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 15 | 16 | import java.io.ByteArrayOutputStream; 17 | import java.io.InputStream; 18 | import java.math.BigInteger; 19 | import java.security.*; 20 | import java.security.cert.CertificateFactory; 21 | import java.security.cert.X509Certificate; 22 | import java.security.spec.EncodedKeySpec; 23 | import java.security.spec.PKCS8EncodedKeySpec; 24 | import java.util.*; 25 | import java.util.stream.Collectors; 26 | import java.util.stream.IntStream; 27 | 28 | /** 29 | * https支持工具类 30 | * 31 | * @author puhaiyang 32 | * created on 2019/10/25 22:27 33 | */ 34 | public class HttpsSupport { 35 | /** 36 | * 证书 37 | */ 38 | private SslContext clientSslCtx; 39 | /** 40 | * 证书使用者 41 | */ 42 | private String issuer; 43 | /** 44 | * 证书开始时间 45 | */ 46 | private Date caNotBefore; 47 | /** 48 | * 证书结束时间 49 | */ 50 | private Date caNotAfter; 51 | /** 52 | * ca私钥 53 | */ 54 | private PrivateKey caPriKey; 55 | /** 56 | * 服务端私钥 57 | */ 58 | private PrivateKey serverPriKey; 59 | /** 60 | * 服务端公钥 61 | */ 62 | private PublicKey serverPubKey; 63 | 64 | /** 65 | * 证书cahce 66 | */ 67 | private Map certCache = new HashMap<>(); 68 | /** 69 | * 70 | */ 71 | private KeyFactory keyFactory = null; 72 | 73 | private HttpsSupport() { 74 | initHttpsConfig(); 75 | } 76 | 77 | private static HttpsSupport httpsSupport; 78 | 79 | public static HttpsSupport getInstance() { 80 | if (httpsSupport == null) { 81 | httpsSupport = new HttpsSupport(); 82 | } 83 | return httpsSupport; 84 | } 85 | 86 | private void initHttpsConfig() { 87 | try { 88 | keyFactory = KeyFactory.getInstance("RSA"); 89 | //信任客户端的所有证书,不进行校验 90 | setClientSslCtx(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build()); 91 | //加载证书 92 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 93 | //从项目目录加入ca根证书 94 | X509Certificate caCert = loadCert(classLoader.getResourceAsStream("ca.crt")); 95 | //从项目目录加入ca私钥 96 | PrivateKey caPriKey = loadPriKey(classLoader.getResourceAsStream("ca_private.der")); 97 | setCaPriKey(caPriKey); 98 | //从证书中获取使用者信息 99 | setIssuer(getSubjectByCert(caCert)); 100 | //设置ca证书有效期 101 | setCaNotBefore(caCert.getNotBefore()); 102 | setCaNotAfter(caCert.getNotAfter()); 103 | //生产一对随机公私钥用于网站SSL证书动态创建 104 | KeyPair keyPair = genKeyPair(); 105 | //server端私钥 106 | setServerPriKey(keyPair.getPrivate()); 107 | //server端公钥 108 | setServerPubKey(keyPair.getPublic()); 109 | } catch (Exception e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | 114 | /** 115 | * 生成RSA公私密钥对,长度为2048 116 | */ 117 | private KeyPair genKeyPair() throws Exception { 118 | Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 119 | KeyPairGenerator caKeyPairGen = KeyPairGenerator.getInstance("RSA", "BC"); 120 | caKeyPairGen.initialize(2048, new SecureRandom()); 121 | return caKeyPairGen.genKeyPair(); 122 | } 123 | 124 | /** 125 | * 获取证书中的subject信息 126 | */ 127 | private String getSubjectByCert(X509Certificate certificate) { 128 | //读出来顺序是反的需要反转下 129 | List tempList = Arrays.asList(certificate.getIssuerDN().toString().split(", ")); 130 | return IntStream.rangeClosed(0, tempList.size() - 1) 131 | .mapToObj(i -> tempList.get(tempList.size() - i - 1)).collect(Collectors.joining(", ")); 132 | } 133 | 134 | /** 135 | * 加载ca的私钥 136 | * 137 | * @param inputStream ca私钥文件流 138 | */ 139 | private PrivateKey loadPriKey(InputStream inputStream) throws Exception { 140 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 141 | byte[] bts = new byte[1024]; 142 | int len; 143 | while ((len = inputStream.read(bts)) != -1) { 144 | outputStream.write(bts, 0, len); 145 | } 146 | inputStream.close(); 147 | outputStream.close(); 148 | return loadPriKey(outputStream.toByteArray()); 149 | } 150 | 151 | /** 152 | * 从文件加载RSA私钥 openssl pkcs8 -topk8 -nocrypt -inform PEM -outform DER -in ca.key -out 153 | * ca_private.der 154 | */ 155 | private PrivateKey loadPriKey(byte[] bts) 156 | throws Exception { 157 | EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(bts); 158 | return keyFactory.generatePrivate(privateKeySpec); 159 | } 160 | 161 | /** 162 | * 加载ca根证书 163 | * 164 | * @param inputStream 证书文件流 165 | */ 166 | private X509Certificate loadCert(InputStream inputStream) throws Exception { 167 | CertificateFactory cf = CertificateFactory.getInstance("X.509"); 168 | return (X509Certificate) cf.generateCertificate(inputStream); 169 | } 170 | 171 | public SslContext getClientSslCtx() { 172 | return clientSslCtx; 173 | } 174 | 175 | public void setClientSslCtx(SslContext clientSslCtx) { 176 | this.clientSslCtx = clientSslCtx; 177 | } 178 | 179 | public String getIssuer() { 180 | return issuer; 181 | } 182 | 183 | public void setIssuer(String issuer) { 184 | this.issuer = issuer; 185 | } 186 | 187 | public Date getCaNotBefore() { 188 | return caNotBefore; 189 | } 190 | 191 | public void setCaNotBefore(Date caNotBefore) { 192 | this.caNotBefore = caNotBefore; 193 | } 194 | 195 | public Date getCaNotAfter() { 196 | return caNotAfter; 197 | } 198 | 199 | public void setCaNotAfter(Date caNotAfter) { 200 | this.caNotAfter = caNotAfter; 201 | } 202 | 203 | public PrivateKey getCaPriKey() { 204 | return caPriKey; 205 | } 206 | 207 | public void setCaPriKey(PrivateKey caPriKey) { 208 | this.caPriKey = caPriKey; 209 | } 210 | 211 | public PrivateKey getServerPriKey() { 212 | return serverPriKey; 213 | } 214 | 215 | public void setServerPriKey(PrivateKey serverPriKey) { 216 | this.serverPriKey = serverPriKey; 217 | } 218 | 219 | public PublicKey getServerPubKey() { 220 | return serverPubKey; 221 | } 222 | 223 | public void setServerPubKey(PublicKey serverPubKey) { 224 | this.serverPubKey = serverPubKey; 225 | } 226 | 227 | 228 | /** 229 | * 获取证书 230 | * 231 | * @param host host 232 | * @return host对应的证书 233 | */ 234 | public X509Certificate getCert(String host) throws Exception { 235 | if (StringUtils.isBlank(host)) { 236 | return null; 237 | } 238 | X509Certificate cacheCert = certCache.get(host); 239 | if (cacheCert != null) { 240 | //将缓存的证书返回 241 | return cacheCert; 242 | } 243 | //生成新的证书,并将它放到缓存中去 244 | host = host.trim().toLowerCase(); 245 | String hostLowerCase = host.trim().toLowerCase(); 246 | X509Certificate cert = genCert(getIssuer(), getCaPriKey(), getCaNotBefore(), getCaNotAfter(), getServerPubKey(), hostLowerCase); 247 | //添加到缓存 248 | certCache.put(host, cert); 249 | return certCache.get(host); 250 | } 251 | 252 | /** 253 | * 动态生成服务器证书,并进行CA签授 254 | * 255 | * @param issuer 颁发机构 256 | */ 257 | /** 258 | * @param issuer 颁发机构 259 | * @param caPriKey ca私钥 260 | * @param certStartTime 证书开始时间 261 | * @param certEndTime 证书结束时间 262 | * @param serverPubKey server证书的公钥 263 | * @param hosts host,支持同时生成多个host 264 | * @return 证书 265 | * @throws Exception Exception 266 | */ 267 | public static X509Certificate genCert(String issuer, PrivateKey caPriKey, Date certStartTime, 268 | Date certEndTime, PublicKey serverPubKey, 269 | String... hosts) throws Exception { 270 | //根据CA证书subject来动态生成目标服务器证书的issuer和subject 271 | String subject = "C=CN, ST=SC, L=CD, O=hai, OU=study, CN=" + hosts[0]; 272 | JcaX509v3CertificateBuilder jv3Builder = new JcaX509v3CertificateBuilder(new X500Name(issuer), 273 | //序列号,需要唯一;ElementaryOS上证书不安全问题(serialNumber为1时证书会提示不安全),避免serialNumber冲突,采用时间戳+4位随机数生成 274 | BigInteger.valueOf(System.currentTimeMillis() + (long) (Math.random() * 10000) + 1000), 275 | certStartTime, 276 | certEndTime, 277 | new X500Name(subject), 278 | serverPubKey); 279 | //SAN扩展证书支持的域名,否则浏览器提示证书不安全 280 | GeneralName[] generalNames = new GeneralName[hosts.length]; 281 | for (int i = 0; i < hosts.length; i++) { 282 | generalNames[i] = new GeneralName(GeneralName.dNSName, hosts[i]); 283 | } 284 | GeneralNames subjectAltName = new GeneralNames(generalNames); 285 | //添加多域名支持 286 | jv3Builder.addExtension(Extension.subjectAlternativeName, false, subjectAltName); 287 | //SHA256 用SHA1浏览器可能会提示证书不安全 288 | ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPriKey); 289 | return new JcaX509CertificateConverter().getCertificate(jv3Builder.build(signer)); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/com/github/puhiayang/utils/ProxyRequestUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.puhiayang.utils; 2 | 3 | import com.github.puhiayang.bean.ClientRequest; 4 | import com.github.puhiayang.bean.Constans; 5 | import io.netty.channel.Channel; 6 | import io.netty.handler.codec.http.HttpRequest; 7 | import io.netty.util.Attribute; 8 | 9 | import static com.github.puhiayang.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY; 10 | 11 | /** 12 | * 代理请求工具类 13 | * 14 | * @author puhaiyang 15 | * created on 2019/10/25 23:12 16 | */ 17 | public class ProxyRequestUtil { 18 | private ProxyRequestUtil() { 19 | } 20 | 21 | /** 22 | * 获取代理请求 23 | * 24 | * @param httpRequest http请求 25 | */ 26 | public static ClientRequest getClientReuqest(HttpRequest httpRequest) { 27 | //从header中获取出host 28 | String host = httpRequest.headers().get("host"); 29 | //从host中获取出端口 30 | String[] hostStrArr = host.split(":"); 31 | int port = 80; 32 | if (hostStrArr.length > 1) { 33 | port = Integer.parseInt(hostStrArr[1]); 34 | } else if (httpRequest.uri().startsWith(Constans.HTTPS_PROTOCOL_NAME)) { 35 | port = 443; 36 | } 37 | return new ClientRequest(hostStrArr[0], port); 38 | } 39 | 40 | /** 41 | * 从channel中获取clientRequest 42 | */ 43 | public static ClientRequest getClientRequest(Channel channel) { 44 | //将clientRequest保存到channel中 45 | Attribute clientRequestAttribute = channel.attr(CLIENTREQUEST_ATTRIBUTE_KEY); 46 | return clientRequestAttribute.get(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDozCCAougAwIBAgIUBQF2Bhl7oADw1uU+uI2ojP5+2gowDQYJKoZIhvcNAQEL 3 | BQAwYDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEQMA4G 4 | A1UECgwHaGFpeWFuZzEOMAwGA1UECwwFc3R1ZHkxFTATBgNVBAMMDEhhaXlhbmdQ 5 | cm94eTAgFw0xOTEwMjgwMjM5MzVaGA8yMTE5MTAwNDAyMzkzNVowYDELMAkGA1UE 6 | BhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEQMA4GA1UECgwHaGFpeWFu 7 | ZzEOMAwGA1UECwwFc3R1ZHkxFTATBgNVBAMMDEhhaXlhbmdQcm94eTCCASIwDQYJ 8 | KoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSJ38cAgfgOIjruYWdgUJnZZNqZ2o6m 9 | 6pvFWWa0BB/wqiPSqPch2vCsQEJNzlREQC/79yiPNXHPGq2HMKPgBIMqU03Q71AF 10 | BJyokOk+YTXRbIZ37VR/E3LC+Yo972cD+B/aY3UCeIws7NDRFKPuE7DjwI2xMsFD 11 | krhPUvvSvkXdiCK0b6x2LNKsn4vaGWqYKiddBX2gHGjk/gOkj6TUl1FxPbcoWT9D 12 | 3H6EUeY3bpwa+Cw1hq6A9aEqndPKU3k4HN+EYViqcIQRYfgX0uhYC3lUK/Y1iPWu 13 | VQAg1wlwsEfLO0oNqY8Jtiil2vc6A5QYciwM35i7mnhSiInR9ylia3cCAwEAAaNT 14 | MFEwHQYDVR0OBBYEFNxe/ofuhqM0GOz3o9pkin6d7aePMB8GA1UdIwQYMBaAFNxe 15 | /ofuhqM0GOz3o9pkin6d7aePMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL 16 | BQADggEBAJ+gsH+I5EIMYEpjh9VkVT6YvhuQVDh9vTNBmgBRMEj4q3vj1xClHULn 17 | d+a+CqGst2w+DIcaanXxcTbH6uEVxdhnenj8xTD2hqnoGCRqgsnXTgxNLxBKM8CF 18 | +03R5fOao7IBC/XqYvMYZkLP+FW9p5+8R9G7B8IpI7b6m+hj/27sYEiU+QsCpu6U 19 | ZBSJ4GMr+drLyqdcB+NNYNmUxheyTVQtTAFXAFB3An7SvVx3P86bmaMHWDJXjdBS 20 | vgcdn0CUJAGYYuUeq/kmOhxLAwhAwGeFM52MjMQa9BK73sQPtGoUh4n/NjYlCr1c 21 | SrMOWJX5NPx4weV2f5qtKULorhtRctM= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /src/main/resources/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,240D08134142D7DA 4 | 5 | vQBu4tyQ8OG2T/237YrVyCaivil32nBNVyiv1raLcCG+BMlGOTjzMQZiUubClIMR 6 | DAtnqczzyBN+JnXDwDU6nNU0pvehDzJ6THYhvPJsCirCfz5503haqwSTloiAhz+R 7 | E//8iwCr4dKvnXjq+cnPtqx4/KsuabVOc7ednfWoATNwusj4t0625yZb529xrqyV 8 | u+YqHk4sv502ADtKQdDmju858vHQ5VIVjxXBb3i/UASDm0QHLiUeDsNzEVds0bys 9 | 9CDfU8Qhku6XpruNuEBtYiBX022kV9A93Y0kbyuCb6VmLhg7e+QAfKdZhDWLOoAe 10 | ExK9H74AU3wpABujRd8Q0srw67RM+xIC/9rgo+YF4IQeS7L2EqJ2hSCf7tG+nmpu 11 | eZHyC0odPLm6xmhIhWanbZKq+q/SujZ+VNYN0ITYmvlUD6BeHQ15RT6iiJ9J94Vv 12 | yf7aWslCyxSM5hMT+PzHbCA1rzAiv7NVmUtVkPcjozt3qgs7RiFG3K2tgyca+kHk 13 | M98Dnc0l8GbBNZZxrNwJbKhtqRn0Ml9aN0JpidFGvZwp4kUaIn5XTK8E/gL+GdXT 14 | uEqdP6AqagyhihPdo5G2fyp8liydNOvXP8omCprH/lIFfOW0ff5NXiMvfOKaJ7li 15 | NJwt2B77OyAnNccOUJIMGvuEn7pW3qdHf9F3hvog9YCbIFT4iTmhBZ7PUfBaG5lh 16 | 30SQxgXxIqfekXGUo4iTGolAXeP0NYY1L9KWL0X79poymkclcE9PZAdonXL+7+1I 17 | W3AVYoq+Jl+VMU9AiwlbaOtO/Qbw6RXqmAFVebjnjV/pNQD9MPEy30z+jmYsEYIb 18 | pLRn3EsR7qfIBOvIBRFIA4pAT/eL4ncvC7+Xnv7lLKbrIM7Ahohd7BjmSEn7a+/T 19 | 0yT+KoSFRdyXrqTQE4QT5+2MsY00+NTByqvfqC4th9TZarmTvxk4VZZ/o90SORgl 20 | CbE9hJj+KLKsRbNoQBS/By4GUTgGBjZUl+EBBb2Ew+58isKS0mWMu/GZr/bpt2il 21 | 6BuRazQpTT6w3xyPzg7WpEVIsQJTUNuYYQEGQW8wZaYwmRJ7u7sRgf8MUUw7HKPN 22 | agjT3HrCMv8lW8xJm5XdfCKFJhFyK/aRl82tb45C/5ZizKh5U4CYuZVi0apypxo4 23 | H2j+MzqSpDmgbRDtMVxD8WVyZHDnKt/kCa0/+zY3MEgiAx0LHuk8A7XbcGOBXLZB 24 | PV3rJLH2lAwAN+W24lJRE/NmQ3Vv43W5CDNBNzMrr7qPXpZ/YrNCk/i0ITomBcIb 25 | NOVi0nVXSX8PDYNHkZP015kXD/nVffynZ0VuXtXydPV8GzamkwUXKOvehDumH8Ue 26 | fS5FsghhwAd2A+kXvBLwxFoX+ezNpZk7WiYqcR9VvJTitRPECtfIriVMtX2v4zZf 27 | hfXK1AP96jHyJOJdqhLCqdJdffFIpZlwJo0ZQ2RpCCIF4UiWgsHkXjwIn17flcju 28 | HVCXjNm6a1w41duNh8cpYfgJCBLL9czt2d2TxoGWYNrHMBGS17nYZoRmxkPUzgSj 29 | WsG9pH5PVNb/9Wb2sFi9K6ryNnCOlGzXDO54/jg3L1ePUiEk2Z4xwbC6BueQkNj7 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /src/main/resources/ca_private.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puhaiyang/easyHttpProxy/3f922e8f936f61870b116349c06539f65cd4907c/src/main/resources/ca_private.der -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | [%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/test/java/com/github/puhaiyang/EasyHttpProxyServerStart.java: -------------------------------------------------------------------------------- 1 | package com.github.puhaiyang; 2 | 3 | import com.github.puhiayang.EasyHttpProxyServer; 4 | 5 | /** 6 | * 启动类 7 | * 8 | * @author puhaiyang 9 | * created on 2019/10/25 22:34 10 | */ 11 | public class EasyHttpProxyServerStart { 12 | public static void main(String[] args) { 13 | EasyHttpProxyServer.getInstace().start(6667); 14 | } 15 | } 16 | --------------------------------------------------------------------------------