Netty SSL 安全认证

标签: netty

1、Netty面临的安全风险

作为一个高性能的NIO通讯框架,基于Netty的行业应用非常广泛,面临的安全挑战也不同。
下面分析Netty面临的安全挑战。

应用场景一:目前高性能的NIO框架成为RPC的分布式服务框架的基石,内部的各个模块需要进行高性能通信,各模块之间往往采用长链接通信,通过心跳检测保证链路的可靠性。由于RPC框架通常是在内部各模块之间使用,运行在授信的内部安全域中,不直接对外开放接口。因此不需要做握手、黑白名单、SSL/TLS等。
     在这种应用场景下,Netty的安全性是依托企业的防火墙、安全加固操作系统等系统级别安全来保障,它本身并不需要做额外的安全性保护工作。

应用场景二:对于第三方开放的通信框架。如果使用Netty做RPC框架或者私有协议栈,RPC框架面向非授信的第三方开放,例如将内部的一些功能通过服务对外开放出去,此时需要进行安全认证,如果开放的是公网IP,对于安全性要求非常高的服务,例如在线支付、订购等,需要通过SSL/TLS进行通信。原理图如下:
这里写图片描述

对第三方开放通信框架的接口调用存在三种场景:
1. 在企业内网,开放给内部其它模块调用的服务,通常不需要进行安全认证和SSL/TLS传输;
2. 在企业内网,被外部其它模块调用的服务,往往需要利用IP黑名单、握手登录等方式进行安全认证,认证通过后双方使用普通的Socket进行通信,如果认证失败,则拒绝客户端连接;
3. 开放给企业外部第三方应用访问的服务,往往需要监听公网IP(通常是防火墙的IP地址),由于对第三方服务调用者的监管存在诸多困难,或者无法有效监听,这些第三方应用实际是非授信的,为了有效应对安全风险,对于敏感的服务往往通过SSL/TSL进行安全传输。

应用场景三:应用层协议的安全性,作为高性能、异步事件驱动的NIO框架,Netty非常适合构建上层的应用层协议。由于绝大多数应用层协议都是公有的,这意味着底层的Netty需要向上层提供通信层的安全传输,也就是需要支持SSL/TLS。

2、Netty SSL安全特性

Netty通过SslHandler提供了对SSL的支持,它支持的SSL协议类型包括:SSL V2、SSL V3和TLS

2.1、SSL单向认证

单向认证:客户端只验证服务端的合法性,服务端不验证客户端。

2.1.1、利用JDK的keytool工具生成自签名证书

对应keytool的使用及命令说明可参考JDK自带工具keytool生成ssl证书搭建tomcat+https协议

(1)生成Netty服务器私钥和证书仓库:

keytool -genkey -alias securechat -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore sChat.jks

(2)生成Netty服务端自签名证书:

keytool -genkey -alias smcc -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass cNetty -storepass cNetty -keystore cChat.jks

(3)生成客户端的**对和证书仓库,用于服务端的证书保存到客户端的授信证书仓库中:

keytool -export -alias securechat -keystore sChat.jks -storepass sNetty -file sChat.cer

(4)将Netty服务端的证书导入到客户端的证书仓库中:

keytool -import -trustcacerts -alias securechat -file sChat.cer -storepass cNetty -keystore cChat.jks

2.1.2、核心代码

服务端

(1)因为是客户端认证服务端,因此服务端需要设置和加载私钥仓库KeyStore,创建服务端的上下文(SSLContext)

//**管理器
KeyManagerFactory kmf = null;
if(pkPath!=null){
    //**库KeyStore
    KeyStore ks = KeyStore.getInstance("JKS");
    //加载客户端证书
    in = new FileInputStream(pkPath);
    //加载服务端的KeyStore  ;sNetty是生成仓库时设置的密码,用于检查**库完整性的密码
    ks.load(in, "sNetty".toCharArray());

    kmf = KeyManagerFactory.getInstance("SunX509");
    //初始化**管理器
    kmf.init(ks, "sNetty".toCharArray());
}
    //获取安全套接字协议(TLS协议)的对象
    SERVER_CONTEXT= SSLContext.getInstance(PROTOCOL);
    //初始化此上下文
    //参数一:认证的**      参数二:对等信任认证  参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null
    SERVER_CONTEXT.init(kmf.getKeyManagers(), null, null);

(2)服务端的SSLContext创建完成后,利用SSLContext创建SSL引擎SSLEngine,设置SSLEngine为服务器模式。由于不需要对客户端进行认证,因此NeedClientAuth不需要额外设置,使用默认值false。

engine.setUseClientMode(false);//设置为服务器模式
//engine.setNeedClientAuth(false);//不需要客户端认证,默认为false,故不需要写这行。

(3)把SslHandler添加到管道中

pipeline.addLast("ssl", new SslHandler(engine));

客户端

(1)客户端和服务端类似,由于是客户端认证服务端,因此,客户端只需要加载存放服务端CA的证书仓库即可。
加载证书仓库完成后,初始化SSLContext,设置信任证书TrustManager。

 //信任库 
TrustManagerFactory tf = null;
if (caPath != null) {
    //**库KeyStore
    KeyStore tks = KeyStore.getInstance("JKS");
    //加载客户端证书
    tIN = new FileInputStream(caPath);
    tks.load(tIN, "cNetty".toCharArray());
    tf = TrustManagerFactory.getInstance("SunX509");
    // 初始化信任库  
    tf.init(tks);
}

 CLIENT_CONTEXT = SSLContext.getInstance(PROTOCOL);
 //设置信任证书
 CLIENT_CONTEXT.init(null,tf == null ? null : tf.getTrustManagers(), null);

(2)客户端的SSLContext创建完成后,利用SSLContext创建SSL引擎SSLEngine,设置SSLEngine为客户方模式。

engine.setUseClientMode(true);//客户方模式

(3)把SslHandler添加到管道中

pipeline.addLast("ssl", new SslHandler(engine));

2.1.3、 测试

这里写图片描述

分别运行服务端、客户端即可。

2.1.4、单向认证原理

  1. SSL客户端向服务端传送客户端SSL协议的版本号、支持的加密算法种类、产生的随机数、以及其他可选信息;
  2. 服务端返回握手答应,向客户端传送确认SSL协议的版本号、加密算法的种类、随机数以及其他相关信息;
  3. 服务端向客户端发送自己的公钥;
  4. 客户端对服务端的证书进行认证,服务端的合法性校验包含:证书是否过期、发行服务器证书的CA是否可靠、发行者证书的公钥能否正确解开服务器的“发行者的数组签名”、服务器证书上的域名是否和服务器的实际域名相匹配等;
  5. 客户端随机生成一个用于后面通讯的“对称密码”,然后用服务端的公钥对其加密,将加密后的“预主密码”传给服务端;
  6. 服务端将自己的私钥解开加密的“预主密码”,然后执行一系列步骤来产生主密码;
  7. 客户端向服务端发出信息,指明后面的数据通讯将使用主密码为对称**,同时通知服务器客户端的握手过程结束;
  8. 服务端向客户端发出信息,指明后面的数据通讯将使用主密码为对称**,同时通知客户端服务器端的握手过程结束;
  9. SSL的握手部分结束,SSL安全通道建立,客户端和服务端开始使用相同的对称**对数据进行加密,然后通过Socket进行传输

2.1、SSL双向认证

与单向认证不同的是服务端也需要对客户端进行安全认证,这就意味着客户端的自签名证书
也需要导入到服务器的数组证书仓库中。

2.2.1、利用JDK的keytool工具生成自签名证书

前面的生成和单向一样,然后继续
(1)生成客户端的自签名证书:

keytool -export -alias smcc -keystore cChat.jks -storepass cNetty -file cChat.cer

(2)将客户端的自签名证书导入到服务端的信任证书仓库

keytool -import -trustcacerts -alias smcc -file cChat.cer -storepass sNetty -keystore sChat.jks  

2.2.2、服务端

(1)由于服务端需要对客户端进行验证,所以在初始化服务端SSLContext的时候需要加载证书仓库。

//信任库 
TrustManagerFactory tf = null;
if (caPath != null) {
    KeyStore tks = KeyStore.getInstance("JKS");
    tIN = new FileInputStream(caPath);
    tks.load(tIN, "sNetty".toCharArray());
    tf = TrustManagerFactory.getInstance("SunX509");
    tf.init(tks);
}

SERVER_CONTEXT= SSLContext.getInstance(PROTOCOL);
//初始化此上下文
//参数一:认证的**      参数二:对等信任认证  参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null
SERVER_CONTEXT.init(kmf.getKeyManagers(),tf.getTrustManagers(), null);

(2)、创建SSLEngine后,设置客户端认证

engine.setUseClientMode(false);//设置服务端模式        
engine.setNeedClientAuth(true);//需要客户端验证

2.2.3、客户端

(1)由于服务端需要认证客户端的证书,因此,需要初始化和加载私钥仓库,向服务端发送公钥。初始化KeyStore的代码如下:

KeyManagerFactory kmf = null;
if (pkPath != null) {
    KeyStore ks = KeyStore.getInstance("JKS");
    in = new FileInputStream(pkPath);
    ks.load(in, "cNetty".toCharArray());
    kmf = KeyManagerFactory.getInstance("SunX509");
    kmf.init(ks, "cNetty".toCharArray());
}

SERVER_CONTEXT= SSLContext.getInstance(PROTOCOL);
//初始化此上下文
//参数一:认证的**      参数二:对等信任认证  参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null   
CLIENT_CONTEXT.init(kmf.getKeyManagers(),tf.getTrustManagers(), null);

除了以上得代码,双向认证的其余代码与单向基本一样。

2.2.4、双向认证原理

10、SSL双向认证相比单向认证,多了一步服务端发送认证请求消息给客户端,客户端发送自签名证书给服务端进行安全认证的过程。

  1. 相比于客户端,服务端在发送时携带了要求客户端认证的请求信息。
  2. 客服端接收到服务端要求客户端认证的请求消息之后,发送自己的证书信息给服务端。
  3. 服务端对客户端的自签名证书进行认证。

1、单向认证完整代码
2、双向认证完整代码

版权声明:本文为zhixinhuacom原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhixinhuacom/article/details/79126274

智能推荐

第三方库PNChart的使用

PNChart 是一个强大的带动画的图表库   要是用这个库可以使用pods,也可以直接将库导入项目中,必须引入"PNChart.h"头文件   下面我们来看一下代码!      转载于:https://www.cnblogs.com/aeronfay/articles/4929223.html...

LeetCode刷题笔记(10)-BFS广度优先搜索

LeetCode刷题笔记(10)-BFS广度优先搜索 127、单词接龙 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: 每次转换只能改变一个字母。 转换过程中的中间单词必须是字典中的单词。 输入: beginWord = “hit”, endWord = &ldqu...

线性筛选素数的方法及基于线性筛选素数的欧拉函数筛选莫比乌斯函数筛选

欧拉筛法线性求素数 回忆一下经典的埃式筛法求素数。时间复杂度是为O(nloglogn)(我之前一直以为是O(n))O(nloglogn)(我之前一直以为是O(n)) 显然,当一个数是素数的时候,那么他的倍数肯定是合数,筛选标记即可 我们来举个列子 筛选2-10的素数 首先2是素数,然后把其倍数删去,我们标记下删去的次数用一个斜杠表示删去了一次 之后3是素数,继续删去倍数,这个时候发现6被重复删去两...

JEECG 页面多个用户选择器只显示最后一个

在一个页面当中,我们可能会需要有多个的用户选择器进行快速的录入,此时会发现所有的输入都会在最后一个用户选择器的地方显示 查看页面代码当中的ID也是不一致: 查看通过标签生成之后的源码,可以发现所有的用户选择器都会生成一个 点击输入框调用的js方法都是一样的,看到这里也就可以明白为什么都是只有最后一个用户选择输入框当中有数据了。 因此我们对后台当中标签代码生成部分进行改造,让其支持多个用户选择器: ...

力扣47 全排列ii 回溯加剪枝

 这题难点在于先排序,然后重复的数字112 used[i-1]= fase 代表之前我用过了,就是我俩 1前面的用过了现在这一个我就直接跳过去    ...

猜你喜欢

arp协议

arp协议叫做地址解析协议,通常与ip地址共同使用,将ip地址转换成硬件地址(MAC地址)。arp既可以放在网络层,也可以放在数据链路层,因为它做了两层的工作。 当一个主机向另一个主机发送数据报时,通过arp协议,向局域网中发送arp请求,所有在局域网中的主机都可以收到,但会在网络层丢弃,只有一台符合目的ip的主机会发送给源主机arp响应包含自己mac地址,因此源主机就可以向目的主机发送报文。 l...

模拟登陆改版后的知乎(最新版)

今天,想着看看视频,把模拟登陆这一块学习学习,以后弄把梯子,去爬爬FaceBook什么的。就拿知乎练练手吧,可曾想,知乎竟然改版了!!之前的教程书籍对现在的知乎来说,都是扯淡,连页面都找不到了。下面一起谈谈改版后的纸糊的模拟登陆吧。 页面分析 抓包 首先,打开页面:https://www.zhihu.com/signup?next=%2F(登录网址都变了…),F12,输入账号密码(记...

干货分享——比微信域名防封防举报更牛逼的防封方案

从微信兴起到现在,微信的流量就一直居高不下,在淘宝时代和传统的电商时代,只要把广告打出去,别人通过搜索就可以找到我们,所以,移动互联网的到来,在微信爆棚的今天,都想在微信里面推广自己的产品,借力微信,达到客户沉淀,营销宣传,传播影响的目的,可以由于微信种种机制,有人腾讯要维护自身的利益,也有人说是微信要营造一个健康的生态圈,种种限制,比如分享次数过多,域名会被微信拦截屏蔽封杀,还比如微信中的链接不...

mysql的事务提交(java)

个人理解的一个事务:是一个Connection一系列的操作过程,如果是两个Connection连接在操作,那就是两个事务。 事务的前提:数据库的存储引擎是innodb。 事务的目的:保证数据的安全性。 事务安全:  1.自动提交事务:每执行一条sql语句,就同步到数据库中。  2.手动提交事务:执行一系列的sql语句后一起同步到数据库中。 事务的四大特性:  A(at...

使用宏批量修改word中图片大小

                word中使用宏修改图片大小,图片大小相同 1、打开word中视图选项卡,选择“宏”->“输入宏名”->“创建” 2、编辑宏 输入下面...