Netty使用SslHandler实现加密通信-双向认证篇

"不积跬步,无以至千里。"

说明

其实Netty使用SslHandler实现加密通信单向认证和双向认证在代码上区别不大,下面是双向认证的代码示例

引入依赖

xml 复制代码
<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.100.Final</version>
</dependency>

生成keystore.jks文件

shell 复制代码
keytool -genkeypair -alias your_alias -keyalg RSA -keystore keystore.jks -keysize 2048

Server端

java 复制代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.CharsetUtil;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;

public class NettySslServer {

    private static final int PORT = 8888;

    public static void main(String[] args) throws Exception {
        // 加载SSL证书
        String serverKeyStorePath = "/home/admin/server/keystore.jks";
        String clientKeyStorePath = "/home/admin/client/keystore.jks";
        String serverKeyStorePassword = "happya";
        String clientKeyStorePassword = "happya";

        // 创建SSL上下文
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.getServerSessionContext().setSessionCacheSize(1);
        sslContext.getServerSessionContext().setSessionTimeout(60);
        
        // 配置密钥库
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new FileInputStream(serverKeyStorePath), serverKeyStorePassword.toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, serverKeyStorePassword.toCharArray());
        
        // 配置信任库
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(new FileInputStream(clientKeyStorePath), clientKeyStorePassword.toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);
        
        // 初始化SSLContext
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        // 创建EventLoopGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 创建服务器Bootstrap
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 在ChannelPipeline中添加SSL处理器
                            SSLEngine sslEngine = sslContext.createSSLEngine();
                            sslEngine.setUseClientMode(false);
                            pipeline.addLast(new SslHandler(sslEngine));
                            // 添加加密通信处理器
                            pipeline.addLast(new SecureChatServerHandler());
                        }
                    })
                    .childOption(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 启动服务器
            System.out.println("======Begin to start ssl server======");
            ChannelFuture future = serverBootstrap.bind(PORT).sync();
            System.out.println("======Ssl server started======");
            future.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static class SecureChatServerHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            // 当连接建立时,发送欢迎消息
            System.out.println("Server channel active : " + ctx.channel().toString());
            ctx.channel().writeAndFlush(Unpooled.wrappedBuffer("Welcome to the secure chat server!\n".getBytes(StandardCharsets.UTF_8)));
            ctx.channel().writeAndFlush(Unpooled.wrappedBuffer("Your connection is protected by SSL.\n".getBytes(StandardCharsets.UTF_8)));
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf byteBuf= (ByteBuf) msg;
            System.out.println("Server received message: " + byteBuf.toString(CharsetUtil.UTF_8));
            super.channelRead(ctx, msg);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            // 处理异常
            cause.printStackTrace();
            ctx.close();
        }
    }
}

Client端

java 复制代码
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.CharsetUtil;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;

public class NettySslClient {

    private static final String HOST = "localhost";
    private static final int PORT = 8888;

    public static void main(String[] args) throws Exception {
        // 加载SSL证书
        String serverKeyStorePath = "/home/admin/server/keystore.jks";
        String clientKeyStorePath = "/home/admin/client/keystore.jks";
        String serverKeyStorePassword = "happya";
        String clientKeyStorePassword = "happya";

        // 创建SSL上下文
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.getServerSessionContext().setSessionCacheSize(1);
        // session cache timeout (sessionTimeout) 's default value is 86400s (24hr), and reduced to 60s to reduce IO block issue.
        sslContext.getServerSessionContext().setSessionTimeout(60);

        // 配置密钥库
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new FileInputStream(clientKeyStorePath), clientKeyStorePassword.toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, clientKeyStorePassword.toCharArray());
        
        // 配置信任库
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(new FileInputStream(serverKeyStorePath), serverKeyStorePassword.toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);
        
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        // 创建EventLoopGroup
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            // 创建客户端Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 在ChannelPipeline中添加SSL处理器
                            SSLEngine sslEngine = sslContext.createSSLEngine();
                            sslEngine.setUseClientMode(true);
                            pipeline.addLast(new SslHandler(sslEngine));
                            // 添加加密通信处理器
                            pipeline.addLast(new SecureChatClientHandler());
                        }
                    });

            // 连接服务器
            System.out.println("======Begin to start ssl client======");
            ChannelFuture future = bootstrap.connect(HOST, PORT).sync();
            System.out.println("======Ssl client started======");
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static class SecureChatClientHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            // 连接建立时,发送一条消息给服务器
            System.out.println("Client channel active : " + ctx.channel().toString());
            ctx.channel().writeAndFlush(Unpooled.wrappedBuffer("Hello from client!\n".getBytes(StandardCharsets.UTF_8)));
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf byteBuf = (ByteBuf) msg;
            System.out.println("Client received message: \n" + byteBuf.toString(CharsetUtil.UTF_8));
            super.channelRead(ctx, msg);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            // 处理异常
            cause.printStackTrace();
            ctx.close();
        }
    }
}
相关推荐
livemetee9 天前
netty单线程并发量评估对比tomcat
java·tomcat·netty
冷环渊15 天前
Finish技术生态计划: FinishRpc
java·后端·nacos·rpc·netty
你熬夜了吗?22 天前
spring中使用netty-socketio部署到服务器(SSL、nginx转发)
服务器·websocket·spring·netty·ssl
异常君22 天前
Netty Reactor 线程模型详解:构建高性能网络应用的关键
java·后端·netty
次元22 天前
初识Netty的奇经八脉
netty
南客先生22 天前
马架构的Netty、MQTT、CoAP面试之旅
java·mqtt·面试·netty·coap
异常君25 天前
一文吃透 Netty 处理粘包拆包的核心原理与实践
java·后端·netty
猫吻鱼1 个月前
【Netty4核心原理】【全系列文章目录】
netty
用户90555842148051 个月前
AdaptiveRecvByteBuAllocator 源码分析
netty
菜菜的后端私房菜1 个月前
深入剖析 Netty 中的 NioEventLoopGroup:架构与实现
java·后端·netty