由于HTTPS协议是由HTTP协议加上SSL/TLS协议组合而成,在阅读本文前可以先阅读一下HTTP服务器和SSL/TLS两篇博文,本文中的代码也是由这两篇博文中的代码组合而成。
HTTPS介绍
-
窃听隐私:使用明文传输的HTTP协议,传输过程中的信息都可能会被攻击者窃取到,例如你登录网站的用户名和密码、在电商的购买记录、搜索记录等,这就会造成例如账号被盗、各种隐私泄漏的风险。而使用HTTPS对通信内容加密过后,即使被攻击者窃取到也无法破解其中的内容。
-
篡改内容:HTTP使用明文传输,不但消息会被窃取,还可能被篡改,例如常见的运营HTTP商劫持。你是否曾经浏览http协议的百度时,时不时会在页面下方弹出小广告,这些小广告并不是百度放上去的,而是电信网通等运营商干的,运营商通过篡改服务器返回的页面内容,加入一段HTML代码就可以轻松实现小广告。而使用HTTPS的百度,就不再会出现这样的小广告,因为攻击者无法对传输内容解密和加密,就无法篡改。
-
冒充:例如DNS劫持,当你输入一个http网址在浏览器打开时,有可能打开的是一个假的网站,连的并不是真网站的服务器,假的网站可能给你弹出广告,还可能让你输入用户名密码来盗取账户。使用HTTPS的话,服务器都会有数字证书和私钥,数字证书公开的,私钥是网站服务器私密的,假网站如果使用假的证书,浏览器会拦截并提示,如果使用真的证书,由于没有私钥也无法建立连接。
生成私钥和证书
1 2
| 1openssl pkcs8 -topk8 -inform PEM -in 2_gw2.vsgames.cn.key -outform DER -nocrypt -out private.der
2 |
1 2
| 1openssl pkcs8 -topk8 -inform PEM -in 2_gw2.vsgames.cn.key -outform PEM -nocrypt -out private.pem
2 |
HTTPS服务器实现
MINA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| 1public class MinaServer {
2
3 public static void main(String[] args) throws Exception {
4
5 String certPath = "/Users/wucao/Desktop/https/1_gw2.vsgames.cn_bundle.crt"; // 证书
6 String privateKeyPath = "/Users/wucao/Desktop/https/private.der"; // 私钥
7
8 // 证书
9 // https://docs.oracle.com/javase/7/docs/api/java/security/cert/X509Certificate.html
10 InputStream inStream = null;
11 Certificate certificate = null;
12 try {
13 inStream = new FileInputStream(certPath);
14 CertificateFactory cf = CertificateFactory.getInstance("X.509");
15 certificate = cf.generateCertificate(inStream);
16 } finally {
17 if (inStream != null) {
18 inStream.close();
19 }
20 }
21
22 // 私钥
23 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Files.readAllBytes(new File(privateKeyPath).toPath()));
24 PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
25
26 KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
27 ks.load(null, null);
28 Certificate[] certificates = {certificate};
29 ks.setKeyEntry("key", privateKey, "".toCharArray(), certificates);
30
31 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
32 kmf.init(ks, "".toCharArray());
33
34 SSLContext sslContext = SSLContext.getInstance("TLS");
35 sslContext.init(kmf.getKeyManagers(), null, null);
36
37
38
39 IoAcceptor acceptor = new NioSocketAcceptor();
40 DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
41 chain.addLast("ssl", new SslFilter(sslContext)); // SslFilter + HttpServerCodec实现HTTPS
42 chain.addLast("codec", new HttpServerCodec());
43 acceptor.setHandler(new HttpServerHandle());
44 acceptor.bind(new InetSocketAddress(8080));
45 }
46}
47
48class HttpServerHandle extends IoHandlerAdapter {
49
50 @Override
51 public void exceptionCaught(IoSession session, Throwable cause)
52 throws Exception {
53 cause.printStackTrace();
54 }
55
56 @Override
57 public void messageReceived(IoSession session, Object message)
58 throws Exception {
59
60 if (message instanceof HttpRequest) {
61
62 // 请求,解码器将请求转换成HttpRequest对象
63 HttpRequest request = (HttpRequest) message;
64
65 // 获取请求参数
66 String name = request.getParameter("name");
67 if(name == null) {
68 name = "World";
69 }
70 name = URLDecoder.decode(name, "UTF-8");
71
72 // 响应HTML
73 String responseHtml = "<html><body>Hello, " + name + "</body></html>";
74 byte[] responseBytes = responseHtml.getBytes("UTF-8");
75 int contentLength = responseBytes.length;
76
77 // 构造HttpResponse对象,HttpResponse只包含响应的status line和header部分
78 Map<String, String> headers = new HashMap<String, String>();
79 headers.put("Content-Type", "text/html; charset=utf-8");
80 headers.put("Content-Length", Integer.toString(contentLength));
81 HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SUCCESS_OK, headers);
82
83 // 响应BODY
84 IoBuffer responseIoBuffer = IoBuffer.allocate(contentLength);
85 responseIoBuffer.put(responseBytes);
86 responseIoBuffer.flip();
87
88 session.write(response); // 响应的status line和header部分
89 session.write(responseIoBuffer); // 响应body部分
90 }
91 }
92}
93 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| 1public class NettyServer {
2
3 public static void main(String[] args) throws InterruptedException, SSLException {
4
5 File certificate = new File("/Users/wucao/Desktop/https/1_gw2.vsgames.cn_bundle.crt"); // 证书
6 File privateKey = new File("/Users/wucao/Desktop/https/private.pem"); // 私钥
7 final SslContext sslContext = SslContextBuilder.forServer(certificate, privateKey).build();
8
9 EventLoopGroup bossGroup = new NioEventLoopGroup();
10 EventLoopGroup workerGroup = new NioEventLoopGroup();
11 try {
12 ServerBootstrap b = new ServerBootstrap();
13 b.group(bossGroup, workerGroup)
14 .channel(NioServerSocketChannel.class)
15 .childHandler(new ChannelInitializer<SocketChannel>() {
16 @Override
17 public void initChannel(SocketChannel ch) throws Exception {
18 ChannelPipeline pipeline = ch.pipeline();
19
20 // 加入SslHandler实现HTTPS
21 SslHandler sslHandler = sslContext.newHandler(ch.alloc());
22 pipeline.addLast(sslHandler);
23
24 pipeline.addLast(new HttpServerCodec());
25 pipeline.addLast(new HttpServerHandler());
26 }
27 });
28 ChannelFuture f = b.bind(8080).sync();
29 f.channel().closeFuture().sync();
30 } finally {
31 workerGroup.shutdownGracefully();
32 bossGroup.shutdownGracefully();
33 }
34 }
35}
36
37class HttpServerHandler extends ChannelInboundHandlerAdapter {
38
39 @Override
40 public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
41
42 if (msg instanceof HttpRequest) {
43
44 // 请求,解码器将请求转换成HttpRequest对象
45 HttpRequest request = (HttpRequest) msg;
46
47 // 获取请求参数
48 QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
49 String name = "World";
50 if(queryStringDecoder.parameters().get("name") != null) {
51 name = queryStringDecoder.parameters().get("name").get(0);
52 }
53
54 // 响应HTML
55 String responseHtml = "<html><body>Hello, " + name + "</body></html>";
56 byte[] responseBytes = responseHtml.getBytes("UTF-8");
57 int contentLength = responseBytes.length;
58
59 // 构造FullHttpResponse对象,FullHttpResponse包含message body
60 FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(responseBytes));
61 response.headers().set("Content-Type", "text/html; charset=utf-8");
62 response.headers().set("Content-Length", Integer.toString(contentLength));
63
64 ctx.writeAndFlush(response);
65 }
66 }
67
68 @Override
69 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
70 cause.printStackTrace();
71 ctx.close();
72 }
73}
74 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 1# -*- coding:utf-8 –*-
2
3from twisted.internet import reactor, ssl
4from twisted.web import server, resource
5
6sslContext = ssl.DefaultOpenSSLContextFactory(
7 '/Users/wucao/Desktop/https/2_gw2.vsgames.cn.key', # 私钥
8 '/Users/wucao/Desktop/https/1_gw2.vsgames.cn_bundle.crt', # 证书
9)
10
11class MainResource(resource.Resource):
12
13 isLeaf = True
14
15 # 用于处理GET类型请求
16 def render_GET(self, request):
17
18 # name参数
19 name = 'World'
20 if request.args.has_key('name'):
21 name = request.args['name'][0]
22
23 # 设置响应编码
24 request.responseHeaders.addRawHeader("Content-Type", "text/html; charset=utf-8")
25
26 # 响应的内容直接返回
27 return "<html><body>Hello, " + name + "</body></html>"
28
29
30site = server.Site(MainResource())
31reactor.listenSSL(8080, site, sslContext)
32reactor.run()
33 |
客户端测试
1 2
| 1127.0.0.1 gw2.vsgames.cn
2 |
MINA、Netty、Twisted一起学系列
源码