Netty SSL双向验证

Netty SSL双向验证

  • 1. 环境说明
  • 2. 生成证书
    • 2.1. 创建根证书 密钥+证书
    • 2.2. 生成请求证书密钥
    • 2.3. 生成csr请求证书
    • 2.4. ca证书对server.csr、client.csr签发生成x509证书
    • 2.5. 请求证书PKCS#8编码
    • 2.6. 输出文件
  • 3. Java代码
    • 3.1. Server端
    • 3.2. Client端
    • 3.3. 证书存放
  • 4. 运行效果
    • 4.1. SSL客户端发送消息:
    • 4.2. 服务器收到SSL客户端消息:
    • 4.3. 非SSL客户端发送消息:
    • 4.4. 服务器收到非SSL客户端消息:
  • 5. References:

1. 环境说明

  • 本例使用windows10 + Win64OpenSSL-3_3_0(完整版,不是lite),netty版本4.1.77.Final,JDK-17
  • openssl官方推荐合作下载地址:https://slproweb.com/download/Win64OpenSSL-3_3_0.exe
  • ${openssl_home}是openssl的安装目录
  • 所有命令在${openssl_home}/bin目录下执行
  • windows下openssl的配置文件是${openssl_home}/bin/openssl.cfg,linux下是${openssl_home}/bin/openssl.conf,注意替换后缀名
  • 需要手动按照openssl.cfg的配置创建好各种目录、文件

2. 生成证书

2.1. 创建根证书 密钥+证书

openssl genrsa -des3 -out demoCA/private/ca.key 4096

openssl req -new -x509 -days 3650 -key demoCA/private/ca.key -out demoCA/certs/ca.crt

2.2. 生成请求证书密钥

openssl genrsa -des3 -out demoCA/private/server.key 2048

openssl genrsa -des3 -out demoCA/private/client.key 2048

2.3. 生成csr请求证书

openssl req -new -key demoCA/private/server.key -out demoCA/certs/server.csr -config openssl.cfg

openssl req -new -key demoCA/private/client.key -out demoCA/certs/client.csr -config openssl.cfg

2.4. ca证书对server.csr、client.csr签发生成x509证书

openssl x509 -req -days 3650 -in demoCA/certs/server.csr -CA demoCA/certs/ca.crt -CAkey demoCA/private/ca.key -CAcreateserial -out demoCA/certs/server.crt

openssl x509 -req -days 3650 -in demoCA/certs/client.csr -CA demoCA/certs/ca.crt -CAkey demoCA/private/ca.key -CAcreateserial -out demoCA/certs/client.crt

2.5. 请求证书PKCS#8编码

openssl pkcs8 -topk8 -in demoCA/private/server.key -out demoCA/private/pkcs8_server.key -nocrypt

openssl pkcs8 -topk8 -in demoCA/private/client.key -out demoCA/private/pkcs8_client.key -nocrypt

2.6. 输出文件

server端:ca.crt、server.crt、pkcs8_server.key

client端:ca.crt、client.crt、pkcs8_client.key

3. Java代码

3.1. Server端

  • ServiceMain.java
public class ServiceMain implements CommandLineRunner {
    @Value("${netty.host}")
    private String host;
    @Value("${netty.port}")
    private int port;

    @Resource
    private NettyServer nettyServer;

    public static void main(String[] args) {
        SpringApplication.run(ServiceMain.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        InetSocketAddress address = new InetSocketAddress(host, port);
        ChannelFuture channelFuture = nettyServer.bind(address);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> nettyServer.destroy()));
        channelFuture.channel().closeFuture().syncUninterruptibly();
    }
}
  • NettyServer.java
package cn.a.service.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;

import javax.annotation.Resource;
import java.io.File;
import java.net.InetSocketAddress;

@Slf4j
@Component("nettyServer")
public class NettyServer {

    private final EventLoopGroup parentGroup = new NioEventLoopGroup();
    private final EventLoopGroup childGroup = new NioEventLoopGroup();
    private Channel channel;

    @Resource
    ApplicationContext applicationContext;

    /**
     * 绑定端口
     *
     * @param address
     * @return
     */
    public ChannelFuture bind(InetSocketAddress address) {
        ChannelFuture channelFuture = null;
        try {
            File certChainFile = ResourceUtils.getFile("classpath:server.crt");
            File keyFile = ResourceUtils.getFile("classpath:pkcs8_server.key");
            File rootFile = ResourceUtils.getFile("classpath:ca.crt");
            SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile)
                    .trustManager(rootFile)
                    .clientAuth(ClientAuth.REQUIRE).build();

            ServerBootstrap b = new ServerBootstrap();
            b.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new NettyChannelInitializer(applicationContext, sslCtx));
            channelFuture = b.bind(address).syncUninterruptibly();
            channel = channelFuture.channel();
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (null != channelFuture && channelFuture.isSuccess()) {
                log.info("netty server start done.");
            } else {
                log.error("netty server start error.");
            }
        }
        return channelFuture;
    }


    /**
     * 销毁
     */
    public void destroy() {
        if (null == channel) return;
        channel.close();
        parentGroup.shutdownGracefully();
        childGroup.shutdownGracefully();
    }


    /**
     * 获取通道
     *
     * @return
     */
    public Channel getChannel() {
        return channel;
    }
}
  • NettyChannelInitializer.java
package cn.a.service.netty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslContext;
import org.springframework.context.ApplicationContext;

public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {

    private final ApplicationContext applicationContext;
    private final SslContext sslContext;

    public NettyChannelInitializer(ApplicationContext applicationContext, SslContext sslCtx) {
        this.applicationContext = applicationContext;
        this.sslContext = sslCtx;
    }

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        // 添加SSL安装验证
        channel.pipeline().addLast(sslContext.newHandler(channel.alloc()));
        //发送时编码
        channel.pipeline().addLast(new FrameEncoder());
        //接收时解码
        channel.pipeline().addLast(new FrameDecoder());
        //业务处理器
        channel.pipeline().addLast(new NettyMsgHandler(applicationContext));
    }
}

3.2. Client端

  • TestClientApp.java
package cn.a.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.a.service.netty.FrameDecoder;
import cn.a.service.netty.FrameEncoder;
import cn.a.service.netty.NettyMsg;
import cn.a.service.netty.Session;
import io.netty.bootstrap.Bootstrap;
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.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.File;
import java.util.Scanner;

@Slf4j
@SpringBootApplication
public class TestClientApp {

    private static final Session session = new Session().setId(IdUtil.randomUUID());

    public static void main(String[] args) {
        new Thread(new TestThread("127.0.0.1", 7890)).start();
    }

    private static class TestThread implements Runnable {
        private final String serverHost;
        private final int serverPort;

        public TestThread(String serverHost, int serverPort) {
            this.serverHost = serverHost;
            this.serverPort = serverPort;
        }

        @Override
        public void run() {
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                final String certsDir = "D:\\GIT\\secim_service\\service\\src\\main\\resources\\";
                File certChainFile = new File(certsDir + "client.crt");
                File keyFile = new File(certsDir + "pkcs8_client.key");
                File rootFile = new File(certsDir + "ca.crt");
                SslContext sslCtx = SslContextBuilder.forClient().keyManager(certChainFile, keyFile).trustManager(rootFile).build();

                Bootstrap b = new Bootstrap();

                b.group(group)
                        .channel(NioSocketChannel.class)
                        .option(ChannelOption.TCP_NODELAY, true)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            protected void initChannel(SocketChannel ch) throws Exception {
                                // 添加SSL安装验证
                                ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()));
                                ch.pipeline().addLast(new FrameEncoder());
                                ch.pipeline().addLast(new FrameDecoder());
                                ch.pipeline().addLast(new TestClientHandler(session));
                            }
                        });

                // 发起异步连接操作
                ChannelFuture f = b.connect(serverHost, serverPort);
                f.addListener(future -> {
                    startConsoleThread(f.channel(), session);
                }).sync();
                // 等待客户端连接关闭
                f.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 优雅退出,释放NIO线程组
                group.shutdownGracefully();
            }
        }
    }

    /**
     * 开启控制台线程
     *
     * @param channel
     */
    private static void startConsoleThread(Channel channel, Session session) {
        new Thread(() -> {
            while (!Thread.interrupted()) {
                log.info("输入指令:");
                Scanner scanner = new Scanner(System.in);
                String input;
                while (!"exit".equals((input = scanner.nextLine()))) {
                    log.info("输入的命令是:{}", input);
                    if (!NumberUtil.isInteger(input)) {
                        log.error("输入的指令有误,请重新输入");
                        continue;
                    }
                    NettyMsg nettyMsg;
                    switch (Integer.parseInt(input)) {
                        case 1:
                            nettyMsg = TestMsgBuilder.buildIdentityMsg(session);
                            break;
                        default:
                            log.error("无法识别的指令:{},请重新输入指令", input);
                            nettyMsg = null;
                            break;
                    }
                    if (null != nettyMsg) {
                        channel.writeAndFlush(nettyMsg);
                    }
                }
            }
        }).start();
    }
}

3.3. 证书存放

在这里插入图片描述

4. 运行效果

4.1. SSL客户端发送消息:

在这里插入图片描述

4.2. 服务器收到SSL客户端消息:

在这里插入图片描述

4.3. 非SSL客户端发送消息:

在这里插入图片描述

4.4. 服务器收到非SSL客户端消息:

在这里插入图片描述

5. References:

2020-07-14 15:01:55 小傅哥:netty案例,netty4.1中级拓展篇十三《Netty基于SSL实现信息传输过程中双向加密验证》

2017-07-04 11:44 骏马金龙:openssl ca(签署和自建CA)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/666484.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ 多重继承的内存布局和指针偏移

在 C 程序里&#xff0c;在有多重继承的类里面。指向派生类对象的基类指针&#xff0c;其实是指向了派生类对象里面&#xff0c;该基类对象的起始位置&#xff0c;该位置相对于派生类对象可能有偏移。偏移的大小&#xff0c;等于派生类的继承顺序表里面&#xff0c;排在该类前面…

Python中Web开发-Django框架

大家好&#xff0c;本文将带领大家进入 Django 的世界&#xff0c;探索其强大的功能和灵活的开发模式。我们将从基础概念开始&#xff0c;逐步深入&#xff0c;了解 Django 如何帮助开发人员快速构建现代化的 Web 应用&#xff0c;并探讨一些最佳实践和高级技术。无论是初学者还…

SM2259XT2、SM2259XT3量产工具开启“调整不对称CH/CE组态”功能

自己摸索的SM2259XT2、SM2259XT3量产工具开启“调整不对称CH/CE组态”功能。在量产部落下载SM2259XT2量产工具后&#xff0c;解压量产工具压缩包&#xff0c;找到并打开量产工具文件夹中的“UFD_MP”文件夹&#xff0c;用记事本或者Notepad打开“Setting.set”文件&#xff0c;…

Vue3实战笔记(53)—奇怪+1,VUE3实战模拟股票大盘工作台

文章目录 前言一、实战模拟股票大盘工作台二、使用步骤总结 前言 实战模拟股票大盘工作台 一、实战模拟股票大盘工作台 接上文&#xff0c;这两天封装好的组件直接应用,上源码&#xff1a; <template><div class"smart_house pb-5"><v-row ><…

Docker管理工具Portainer忘记admin登录密码

停止Portainer容器 docker stop portainer找到portainer容器挂载信息 docker inspect portainer找到目录挂载信息 重置密码 docker run --rm -v /var/lib/docker/volumes/portainer_data/_data:/data portainer/helper-reset-password生成新的admin密码&#xff0c;使用新密…

若依分页问题排查

无限分页数据返回 一、问题排查1.1 代码排查1.2 sql排查1.3 原因分析 二、问题修复 项目使用了 若依的框架&#xff0c;前端反馈了一个问题&#xff0c;总记录条数只有 48条的情况下&#xff0c;传入的 页数时从6~~无穷大&#xff0c;每页大小为10, 此时还能返回数据&#xff0…

SpringMVC框架学习笔记(四):模型数据 以及 视图和视图解析器

1 模型数据处理-数据放入 request 说明&#xff1a;开发中, 控制器/处理器中获取的数据如何放入 request 域&#xff0c;然后在前端(VUE/JSP/...)取出显 示 1.1 方式 1: 通过 HttpServletRequest 放入 request 域 &#xff08;1&#xff09;前端发送请求 <h1>添加主人…

浅谈线性化

浅谈线性化 原文&#xff1a;浅谈线性化 - 知乎 (zhihu.com) All comments and opinions expressed on Zhihu are mine alone and do not necessarily reflect those of my employers, past or present. 本文内容所有内容仅代表本人观点&#xff0c;和Mathworks无关 (这里所说…

视频汇聚EasyCVR视频监控云平台对接GA/T 1400视图库对象和对象集合XMLSchema描述

GA/T 1400协议主要应用于公安系统的视频图像信息应用系统&#xff0c;如警务综合平台、治安防控系统、交通管理系统等。在城市的治安监控、交通管理、案件侦查等方面&#xff0c;GA/T 1400协议都发挥着重要作用。 以视频汇聚EasyCVR视频监控资源管理平台为例&#xff0c;该平台…

探索UWB模块的多功能应用——UWB技术赋能智慧生活

超宽带&#xff08;Ultra-Wideband, UWB&#xff09;技术&#xff0c;凭借其高精度、低功耗和强抗干扰能力&#xff0c;正在成为智能家居领域的一项关键技术。UWB模块的应用不仅提高了智能家居设备的性能&#xff0c;还为家庭安全、设备管理和用户体验带来了显著的改善。 UWB模…

java基础-chapter15(io流)

io流&#xff1a;存储和读取数据的解决方案 I:input O:output io流的作用&#xff1a;用于读写数据&#xff08;本地文件,网络&#xff09; io流按照流向可以分为&#xff1a; 输出流&#xff1a;程序->文件 输入流&#xff1a;文件->程序 io流按照操作文件…

Qt Creator(Qt 6.6)拷贝一行

Edit - Preference - Environment&#xff1a; 可看到&#xff0c;拷贝一行的快捷键是&#xff1a; ctrl Ins

使用KEPServer连接欧姆龙PLC获取对应标签数据(标签值类型改为字符串型)

1.创建通道&#xff08;通道&#xff09;&#xff0c;&#xff08;选择对应的驱动&#xff0c;跟当前型号PLC型号对应&#xff09;。 2.创建设备&#xff0c;&#xff08;填入IP地址以及欧姆龙的默认端口号&#xff1a;44818&#xff09; 3.创建对应的标签。这里关键讲诉下字…

医院该如何应对网络安全?

在线医生咨询受到很多人的关注&#xff0c;互联网医疗行业的未来发展空间巨大&#xff0c;但随着医院信息化建设高速发展 医院积累了大量的患者基本信息、化验结果、电子处方、生产数据和运营信息等数据 这些数据涉及公民隐私、医院运作和发展等多因素&#xff0c;医疗行业办…

[C/C++]_[初级]_[在Windows平台上导出DLL函数接口的一些疑问]

场景 最近看了《COM本质论》里关于如何设计基于抽象基类作为二进制接口,把编译器和链接器的实现隐藏在这个二进制接口中,从而使用该DLL时不需要重新编译。在编译出C接口时,发现接口名直接是函数名,比如BindNativePort,怎么不是_BindNativePort?说明 VC++导出的函数默认是使…

Notepad++ 常用

File Edit search view Encoding Language Settings Tools Macro Run Plugins Window 文件 编辑 搜索 视图 编码 语言 设置 工具 宏 运行 插件 窗口 快捷方式 定位行 &#xff1a;CTRL g查找&#xff1a; CTRL F替换&am…

精准检测,安全无忧:安全阀检测实践指南

安全阀作为一种重要的安全装置&#xff0c;在各类工业系统和设备中发挥着举足轻重的作用。 它通过自动控制内部压力&#xff0c;有效防止因压力过高而引发的设备损坏和事故风险&#xff0c;因此&#xff0c;对安全阀进行定期检测&#xff0c;确保其性能完好、工作可靠&#xf…

Ubuntu安装GCC编译器

GCC编译器安装 GCC编译器安装切换软件源(换成国内的服务器)1 、创建一个文本文档并命名为“sources.list”2 、复制软件源列表清华源:阿里源:3 、把修改之后的.list 文件覆盖原有的文件4 、更新软件列表5 、安装6 、检查是否安装成功7、GCC 编译器:GCC编译器安装 这里演示…

HackTheBox-Machines--Nibbles

Nibbles 测试过程 1 信息收集 NMAP 80 端口 网站出了打印出“Hello world&#xff01;”外&#xff0c;无其他可利用信息&#xff0c;但是查看网页源代码时&#xff0c;发现存在一个 /nibbleblog 文件夹 检查了 http://10.129.140.63/nibbleblog/ &#xff0c;发现了 /index.p…

数据库开发-MySQL01

目录 前言 1. MySQL概述 1.1 安装 1.1.1 版本 1.1.2 安装 1.1.3 连接 1.1.4 企业使用方式(了解) 1.2 数据模型 1.3 SQL简介 1.3.1 SQL通用语法 1.3.2 分类 2. 数据库设计-DDL 2.1 项目开发流程 2.2 数据库操作 2.2.1 查询数据库 2.2.2 创建数据库 2.2.3 使用数…