一、证书
(本文只介绍windows版,其他系统只供参考)
1.生成ca证书
下载 openssl 并安装 未编译 编译好
在
openssl/bin
目录下打开命令行,输入
openssl req -new -x509 -keyout ca.key -out ca.crt -config openssl.cnf
在本目录得到 ca.key 和 ca.crt 文件
2.生成服务端和客户端私钥
命令行输入
openssl genrsa -des3 -out server.key 1024
openssl genrsa -des3 -out client.key 1024
密码自己设定,好几个密码,别弄乱了就好,分不清的话都设成一样的
3.根据 key 生成 csr 文件
openssl req -new -key server.key -out server.csr -config openssl.cnf
openssl req -new -key client.key -out client.csr -config openssl.cnf
4.根据 ca 证书 server.csr 和 client.csr 生成 x509 证书
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
5.将 key 文件进行 PKCS#8 编码
openssl pkcs8 -topk8 -in server.key -out pkcs8_server.key -nocrypt
openssl pkcs8 -topk8 -in client.key -out pkcs8_client.key -nocrypt
最后得到有用的文件分别为
服务器端: ca.crt、server.crt、pkcs8_server.key
客户端端: ca.crt、client.crt、pkcs8_client.key
二、服务器端代码
Main.java
public class Main {
private static final int m_port = 23333;
public void run() throws Exception {
File certChainFile = new File(".\\ssl\\server.crt");
File keyFile = new File(".\\ssl\\pkcs8_server.key");
File rootFile = new File(".\\ssl\\ca.crt");
SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile).trustManager(rootFile).clientAuth(ClientAuth.REQUIRE).build();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new Initializer(sslCtx));
ChannelFuture f = b.bind(m_port).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new Main().run();
}
}
Initializer.java
public class Initializer extends ChannelInitializer {
private final SslContext sslCtx;
public Initializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Handler());
}
}
Handler.java
public class Handler extends SimpleChannelInboundHandler {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
ctx.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
Channel incoming = ctx.channel();
System.out.println("收到消息" + s)
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println(incoming.remoteAddress() + "在线");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
ctx.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println(incoming.remoteAddress() + "掉线");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
Channel incoming = ctx.channel();
System.out.println(incoming.remoteAddress() + "异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
三、客户端代码
Main.java
public class Main {
private static String m_host = "127.0.0.1";
private static int m_prot = 23333;
public static void main(String[] args) throws Exception {
new Main().run();
}
public void run() throws Exception {
File certChainFile = new File(".\\ssl\\client.crt");
File keyFile = new File(".\\ssl\\pkcs8_client.key");
File rootFile = new File(".\\ssl\\ca.crt");
final SslContext sslCtx = SslContextBuilder.forClient().keyManager(certChainFile, keyFile).trustManager(rootFile).build();
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(new Initializer(sslCtx));
Channel ch = b.connect(m_host, m_prot).sync().channel();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
ch.writeAndFlush(in.readLine() + "\r\n");
}
} finally {
group.shutdownGracefully();
}
}
}
Initializer.java
public class Initializer extends ChannelInitializer{
private final SslContext sslCtx;
public Initializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Handler());
}
}
Handler.java
public class Handler extends SimpleChannelInboundHandler{
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println("收到:" + s);
}
}