1. 程式人生 > 其它 >netty 實現https伺服器

netty 實現https伺服器

0 概述
netty 通過JDK的SSLEngine,以SslHandler的方式提供對SSL/TLS 安全傳輸的支援,極大的簡化了開發工作。本文主要講述如何使用netty實現簡單的https伺服器。

1 SSL單向認證
所謂的單向認證,即客戶端只驗證服務端的合法性,服務端不會驗證客戶端。
單向認證過程的總結如下:
1.SSL客戶端(瀏覽器)向服務端傳送客戶端 SSL協議的版本號、支援的加密演算法種類、產生的隨機數,以及其他可選資訊。
2.服務端返回握手應答,向客戶端傳送確認SSL協議的版本號、加密演算法種類、隨機數以及其他資訊。
3.服務端向客戶端傳送自己的證書,這個證書其實就是公鑰,只是包含了很多資訊,如證書的頒發機構,過期時間等等
4.客戶端對服務端的證書進行認證,服務端的合法性校驗包括:證書是否過期、發行伺服器證書CA是否可靠、發行者證書的公鑰能否解開伺服器證書的“發行者數字簽名”等
5.客戶端隨機產生一個用於後續通訊的對稱金鑰,然後用服務端的公鑰加密傳輸給服務端,通知服務端客戶端的握手結束。
6.服務端解密獲取客戶端的對稱金鑰,同時通知客戶端服務端的握手結束。
7.SSL的握手部分結束,SSL安全通道建立,客戶端和服務端開始使用相同的對稱金鑰對資料進行加密,然後通過socket進行傳輸。

2 具體實現
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.15.Final</version>
</dependency>
 
利用JDK的keytool 工具,生成服務端私鑰和證書倉庫。
執行如下命令:
keytool -genkey -keysize 2048 -validity 365 -keyalg RSA -dname “CN=localhost” -keypass hsc123 -storepass hsc123 -keystore local.jks

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ssl.SslHandler;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

/**
* Created by apple on 17/10/21.
*/
public class HttpsServer {

public static void start(final int port) throws Exception {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.group(boss, worker)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
SSLEngine sslEngine = SSLContextFactory.getSslContext().createSSLEngine();
sslEngine.setUseClientMode(false);
ch.pipeline().addLast(new SslHandler(sslEngine));
ch.pipeline().addLast("http-decoder", new HttpServerCodec());
ch.pipeline().addLast(new HttpsSeverHandler());
}
});
ChannelFuture future = serverBootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}

}

public static void main(String[] args) throws Exception {
start(7000);
}

}
 
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;

/**
* Created by apple on 17/10/21.
*/
public class SSLContextFactory {

public static SSLContext getSslContext() throws Exception {
char[] passArray = "hsc123".toCharArray();
SSLContext sslContext = SSLContext.getInstance("TLSv1");
KeyStore ks = KeyStore.getInstance("JKS");
//載入keytool 生成的檔案
FileInputStream inputStream = new FileInputStream("/Users/apple/local.jks");
ks.load(inputStream, passArray);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, passArray);
sslContext.init(kmf.getKeyManagers(), null, null);
inputStream.close();
return sslContext;

}
 
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;

/**
* Created by apple on 17/10/21.
*/
public class HttpsSeverHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
boolean keepaLive = HttpUtil.isKeepAlive(request);
System.out.println("method" + request.method());
System.out.println("uri" + request.uri());
FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
httpResponse.content().writeBytes("https".getBytes());
httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
httpResponse.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, httpResponse.content().readableBytes());
if (keepaLive) {
httpResponse.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.writeAndFlush(httpResponse);
} else {
ctx.writeAndFlush(httpResponse).addListener(ChannelFutureListener.CLOSE);
}
}
}

3 測試
啟動服務 輸入https://127.0.0.1:7000

 

 


————————————————
版權宣告:本文為CSDN博主「huangshanchun」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/huangshanchun/article/details/78305964