├── .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 | [](https://github.com/puhaiyang)
3 | [](https://blog.csdn.net/puhaiyang)
4 | [](https://github.com/puhaiyang/easyHttpProxy/blob/master/LICENSE)
5 |
6 | support http/https proxy.类似于fiddler,由java编写,代码简单便于理解。支持http/https代理!
7 |
8 | ## 环境
9 | [](https://github.com/puhaiyang)
10 | [](https://github.com/puhaiyang)
11 | [](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 |
--------------------------------------------------------------------------------