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();
        }
    }
}
相关推荐
马尚来16 小时前
【韩顺平】尚硅谷Netty视频教程
后端·netty
马尚道3 天前
【韩顺平】尚硅谷Netty视频教程
netty
马尚道3 天前
Netty核心技术及源码剖析
源码·netty
moxiaoran57533 天前
java接收小程序发送的protobuf消息
websocket·netty·protobuf
马尚来4 天前
尚硅谷 Netty核心技术及源码剖析 Netty模型 详细版
源码·netty
马尚来4 天前
Netty核心技术及源码剖析
后端·netty
失散1310 天前
分布式专题——35 Netty的使用和常用组件辨析
java·分布式·架构·netty
hanxiaozhang201812 天前
Netty面试重点-1
网络·网络协议·面试·netty
mumu1307梦22 天前
SpringAI 实战:解决 Netty 超时问题,优化 OpenAiApi 配置
java·spring boot·netty·超时·timeout·openapi·springai
9527出列1 个月前
Netty源码分析--Reactor线程模型解析(二)
netty