Netty SSL双向验证

news2024/11/17 13:54:59

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.netoday.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.netoday.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.netoday.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.netoday.service.netty.FrameDecoder;
import cn.netoday.service.netty.FrameEncoder;
import cn.netoday.service.netty.NettyMsg;
import cn.netoday.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)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

走进数字艺术的世界:一种创新的艺术表达方式

进入数字时代&#xff0c;计算机将我们生活的方方面面都进行了转化。当然艺术领域也不例外。随着数字技术和计算机程序的发展和普及&#xff0c;“数字艺术”的概念应试而生。那么&#xff0c;所谓的数字艺术到底是什么呢&#xff1f;数字艺术的作用是什么&#xff1f;新手如何…

Java基础:异常(三)

Java基础&#xff1a;异常&#xff08;三&#xff09; 文章目录 Java基础&#xff1a;异常&#xff08;三&#xff09;1. Java异常体系1.1 错误的分类1.2 异常的分类 2. 异常的捕获与处理2.1 try-catch2.2 finally 3. 异常的抛出4. 自定义异常 1. Java异常体系 Java的异常体系是…

醒图及国际版 v9.9.9/v3.9.0 解锁会员(让照片栩栩如生的神奇应用)

介绍 醒图App是一款专业的照片编辑工具&#xff0c;旨在帮助用户高效地处理和优化照片&#xff0c;使其更加引人注目。这款应用程序配备了多样化的功能&#xff0c;包括图像增强、滤镜应用以及色彩调整等&#xff0c;以满足各种编辑需求。其设计了一个直观的用户界面&#xff…

C# yolov8 TensorRT +ByteTrack Demo

C# yolov8 TensorRT ByteTrack Demo 目录 效果 说明 项目 代码 Form2.cs YoloV8.cs ByteTracker.cs 下载 参考 效果 说明 环境 NVIDIA GeForce RTX 4060 Laptop GPU cuda12.1cudnn 8.8.1TensorRT-8.6.1.6 版本和我不一致的需要重新编译TensorRtExtern.dll&…

保姆教程系列:小白也能看懂的 Linux 挂载磁盘实操

&#xff01;&#xff01;&#xff01;是的没错&#xff0c;胖友们&#xff0c;保姆教程系列又更新了&#xff01;&#xff01;&#xff01; 文章目录 前言简介一、磁盘分区二、文件系统三、实际操作1. 使用lsblk命令查看新加入的磁盘信息2. 使用fdisk或者cfdisk分区新磁盘&am…

工业制造企业为什么要进行数字化转型

人人都在谈数字化转型&#xff0c;政府谈数字化策略方针&#xff0c;企业谈数字化转型方案&#xff0c;员工谈数字化提效工具。互联网企业在谈&#xff0c;工业企业也在谈。 在这种大趋势下&#xff0c;作为一个从事TOB行业十年的老兵&#xff0c;今天就来给大家讲讲&#xff…

【RLHF个人笔记】RLHF:Reinforcement Learning from Human Feedback具体过程

【RLHF个人笔记】RLHF:Reinforcement Learning from Human Feedback具体过程 RLHF训练的三个步骤步骤1&#xff1a;收集数据与有监督训练策略步骤2&#xff1a;收集数据训练奖励模型步骤3&#xff1a;结合奖励模型利用强化学习算法如PPO算法来优化策略 参考内容 RLHF训练的三个…

236. 二叉树的最近公共祖先(C++)

文章目录 前言一、题目介绍二、解决方案三、优化总结 前言 在本篇文章中我们将会讲解二叉树中极为经典的题目236. 二叉树的最近公共祖先 一、题目介绍 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的…

BLE蓝牙模块在虚拟车钥匙上的运用—开启无钥匙驾驶新时代

随着科技的不断发展&#xff0c;人们对汽车的智能化需求也日益增长。在这个背景下&#xff0c;BLE蓝牙模块在虚拟车钥匙上的运用应运而生&#xff0c;为消费者带来更加便捷、智能的出行体验。本文将从以下几个方面阐述BLE蓝牙模块在虚拟车钥匙上的应用。   一、什么是BLE蓝牙…

精酿啤酒:品质与口感在啤酒行业竞争中的竞争优势

在啤酒行业中&#xff0c;竞争激烈&#xff0c;品牌众多。要想在竞争中脱颖而出&#xff0c;需要具备与众不同的竞争优势。对于Fendi club啤酒而言&#xff0c;其卓着的品质和与众不同的口感成为了其在竞争中取胜的关键。 品质是啤酒行业竞争中的核心要素。Fendi club啤酒在原料…

Redis中的数据结构与内部编码

本篇文章主要是对 Redis 常见的数据结构进行讲解&#xff0c;同时还对其所对应的不同的内部编码进行讲解。希望本篇文章会对你有所帮助。 文章目录 一、五大数据结构 二、数据结构对应的编码方式 String hash list set zset &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &…

node.js(express)+MongoDB快速搭建后端---新手教程

前言&#xff1a; Node.js是一个基于 Chrome V8引擎的JavaScript运行环境&#xff0c;是对于前端工程师来说学习成本最小的后端实现方法&#xff0c;本篇文章总结如何从0-1写一个后端的登录接口 一、检查node环境 先检查自己的node是否安装 一般来说前端工程师的电脑环境肯定…

长安链使用Golang编写智能合约教程(二)

本篇说的是长安链2.3.的版本的智能合约&#xff0c;虽然不知道两者有什么区别&#xff0c;但是编译器区分。 教程三会写一些&#xff0c;其他比较常用SDK方法的解释和使用方法 编写前的注意事项&#xff1a; 1、运行一条带有Doker_GoVM的链 2、建议直接用官方的在线IDE去写合…

【机器学习】Pandas中to_pickle()函数的介绍与机器学习中的应用

【机器学习】Pandas中to_pickle()函数的介绍和机器学习中的应用 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#…

【Android】【netd】网络相关调试技巧

网络调试技巧总结 ifconfig ifconfig 查看网卡信息 ifconfig -S tcpdump tcpdump -i any -n icmp 查看流量出入ip addr 上面的log 以及ifcong -S 信息可以知道&#xff0c;当前是从wlan0 网卡请求数据。 iptable iptable 部分指令 //禁止www.baidu.com 网址流量进入&a…

网易面试:手撕定时器

概述&#xff1a; 本文使用STL容器-set以及Linux提供的timerfd来实现定时器组件 所谓定时器就是管理大量定时任务&#xff0c;使其能按照超时时间有序地被执行 需求分析&#xff1a; 1.数据结构的选择&#xff1a;存储定时任务 2.驱动方式&#xff1a;如何选择一个任务并执…

在HTML和CSS当中运用显示隐藏

1.显示与隐藏 盒子显示:display:block;盒子隐藏: display:none:隐藏该元素并且该元素所占的空间也不存在了。 visibility:hidden:隐藏该元素但是该元素所占的内存空间还存在&#xff0c;即“隐身效果”。 2.圆角边框 在CSS2中添加圆角&#xff0c;我们不得不使用背景图像&am…

redis面试知识点

Redis知识点 Redis的RDB和AOF机制各是什么&#xff1f;它们有什么区别&#xff1f; 答&#xff1a;Redis提供了RDB和AOF两种数据持久化机制&#xff0c;适用于不同的场景。 RDB是通过在特定的时刻对内存中的完整的数据复制快照进行持久化的。 RDB工作原理&#xff1a; 当执行…

Python 机器学习 基础 之 无监督学习 【聚类(clustering)/k均值聚类/凝聚聚类/DBSCAN】的简单说明

Python 机器学习 基础 之 无监督学习 【聚类&#xff08;clustering&#xff09;/k均值聚类/凝聚聚类/DBSCAN】的简单说明 目录 Python 机器学习 基础 之 无监督学习 【聚类&#xff08;clustering&#xff09;/k均值聚类/凝聚聚类/DBSCAN】的简单说明 一、简单介绍 二、聚类…

Vue3兼容低版本浏览器(ie11,chrome63)

1、插件安装 为了使你的项目兼容 Chrome 63&#xff0c;你需要确保包含适当的 polyfills 和插件配置。你已经在使用 legacy 插件&#xff0c;但在代码中可能缺少一些配置或插件顺序有问题。以下是几个可能的改进&#xff1a; 安装 vitejs/plugin-legacy 插件&#xff1a; 确保…