bugstac...

Netty应用示例(二)SSL/TLS应用示例

2019-01-05  本文已影响34人  桥头放牛娃

1、SSL/TLS简介

协议是Web浏览器与Web服务器之间安全交换信息的协议,提供两个基本的安全服务:鉴别与保密。

1.1、作用

不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。

SSL/TLS协议是为了解决这三大风险而设计的,希望达到:

1.2、工作原理

1.2.1、基本概念

1.2.2、认证类型:

1.2.3、握手协议(Handshake protocol)

握手流程:

握手流程.png

步骤 1:ClientHello – 客户端发送所支持的 SSL/TLS 最高协议版本号和所支持的加密算法集合及压缩方法集合等信息给服务器端。
步骤 2.:ServerHello – 服务器端收到客户端信息后,选定双方都能够支持的 SSL/TLS 协议版本和加密方法及压缩方法,返回给客户端。
步骤 3.:SendCertificate(可选) – 服务器端发送服务端证书给客户端。
步骤 4: RequestCertificate(可选) – 如果选择双向验证,服务器端向客户端请求客户端证书。
步骤 5.:ServerHelloDone – 服务器端通知客户端初始协商结束。
步骤 6.:ResponseCertificate(可选) – 如果选择双向验证,客户端向服务器端发送客户端证书。
步骤 7.:ClientKeyExchange – 客户端使用服务器端的公钥,对客户端公钥和密钥种子进行加密,再发送给服务器端。
步骤 8:CertificateVerify(可选) – 如果选择双向验证,客户端用本地私钥生成数字签名,并发送给服务器端,让其通过收到的客户端公钥进行身份验证。
步骤 9: CreateSecretKey – 通讯双方基于密钥种子等信息生成通讯密钥。
步骤 10: ChangeCipherSpec – 客户端通知服务器端已将通讯方式切换到加密模式。
步骤 11:Finished – 客户端做好加密通讯的准备。
步骤 12:ChangeCipherSpec – 服务器端通知客户端已将通讯方式切换到加密模式。
步骤 13:Finished – 服务器做好加密通讯的准备。
步骤 14:Encrypted/DecryptedData – 双方使用客户端密钥,通过对称加密算法对通讯内容进行加密。
步骤 15:ClosedConnection – 通讯结束后,任何一方发出断开 SSL 连接的消息。

1.2.4、记录协议(Record protocol)

记录协议在客户机和服务器握手成功后使用,即客户机和服务器鉴别对方和确定安全信息交换使用的算法后,进入SSL记录协议,记录协议向SSL连接提供两个服务:

记录协议的过程:

记录协议的过程.png

1.2.5、报警协议(Alert protocol)

客户机和服务器发现错误时,向对方发送一个警报消息。如果是致命错误,则算法立即关闭SSL连接,双方还会先删除相关的会话号,秘密和密钥。每个警报消息共2个字节,第1个字节表示错误类型,如果是警报,则值为1,如果是致命错误,则值为2;第2个字节制定实际错误类型。

1.2.6、秘钥生成过程

这样握手协议完成,下面看下什么是预备主密钥,主密钥是怎么生成的。为了保证信息的完整性和机密性,SSL需要有六个加密秘密:四个密钥和两个IV。为了信息的可信性,客户端需要一个密钥(HMAC),为了加密要有一个密钥,为了分组加密要一个IV,服务也是如此。SSL需要的密钥是单向的,不同于那些在其他方向的密钥。如果在一个方向上有攻击,这种攻击在其他方向是没影响的。生成过程如下:

从预备主秘钥计算主秘钥.png 从主秘钥计算秘钥材料.png
从秘钥材料提取加密秘钥.png

参考博客:

http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
https://segmentfault.com/a/1190000002554673
https://www.ibm.com/developerworks/cn/java/j-lo-ssltls/index.html
https://www.cnblogs.com/zhuqil/archive/2012/10/06/ssl_detail.html
https://www.cnblogs.com/zhuqil/archive/2012/10/06/ssl_detail.html

2、keytool简介

Java自带的keytool工具是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥(以证书形式)。 keytool 将密钥和证书储存在一个所谓的密钥仓库(keystore)中。缺省的密钥仓库实现将密钥仓库实现为一个文件。它用口令来保护私钥。

2.1、Java KeyStore的类型

2.2、keytool的命令

2.3、keytool使用示例

2.3.1、keystone的生成

分阶段生成:

命令:

keytool -genkey -alias zhaozhou -keypass zhaozhou -keyalg RSA -keysize 1024 -validity 365 -keystore testStore.keystore -storepass 666666

您的名字与姓氏是什么?
[Unknown]: zhaozhou
您的组织单位名称是什么?
[Unknown]: jd
您的组织名称是什么?
[Unknown]: jd
您所在的城市或区域名称是什么?
[Unknown]: beijing
您所在的省/市/自治区名称是什么?
[Unknown]: beijing
该单位的双字母国家/地区代码是什么?
[Unknown]: ch
CN=zhaozhou, OU=jd, O=jd, L=beijing, ST=beijing, C=ch是否正确?
[否]: y

一次性生成:
命令:

keytool -genkey -alias zhaozhou -keypass zhaozhou -keyalg RSA -keysize 1024 -validity 365 -keystore testStore.keystore -storepass 666666 -dname "CN=zhaozhou, OU=jd, O=jd, L=beijing, ST=beijing, C=ch"

2.3.2、keystore信息的查看

命令:

keytool -list -v -keystore testStore.keystore -storepass 666666

输出:

密钥库类型: JKS
密钥库提供方: SUN

您的密钥库包含 1 个条目

别名: zhaozhou
创建日期: 2019-1-5
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: CN=zhaozhou, OU=jd, O=jd, L=beijing, ST=beijing, C=ch
发布者: CN=zhaozhou, OU=jd, O=jd, L=beijing, ST=beijing, C=ch
序列号: ab89854
有效期开始日期: Sat Jan 05 14:59:54 CST 2019, 截止日期: Sun Jan 05 14:59:54 CST
2020
证书指纹:
         MD5: 9D:F0:98:56:3F:F7:9C:38:58:FC:30:3C:2B:28:24:2E
         SHA1: AC:74:9E:A5:41:AD:D6:F7:E1:6E:5E:F9:FD:96:49:FD:E4:E0:D9:C5
         SHA256: CE:77:51:60:00:CB:DF:D5:AE:EA:5D:4F:5C:85:47:14:D9:5A:56:72:66:
F1:A8:E8:8D:1D:E9:50:CC:3B:81:1A
         签名算法名称: SHA256withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 19 0A A6 EB 67 E9 B8 5B   F5 58 03 2D B9 5D E8 93  ....g..[.X.-.]..
0010: CD 4B B1 98                                        .K..
]
]



*******************************************
*******************************************

2.3.3、证书导出

命令:

keytool -export -alias zhaozhou -keystore testStore.keystore -storepass 666666 -file testStore.cer

输出:

存储在文件 <testStore.cer> 中的证书

2.3.4、证书确认

命令:

keytool -printcert -file testStore.cer

输出:

所有者: CN=zhaozhou, OU=jd, O=jd, L=beijing, ST=beijing, C=ch
发布者: CN=zhaozhou, OU=jd, O=jd, L=beijing, ST=beijing, C=ch
序列号: ab89854
有效期开始日期: Sat Jan 05 14:59:54 CST 2019, 截止日期: Sun Jan 05 14:59:54 CST
2020
证书指纹:
         MD5: 9D:F0:98:56:3F:F7:9C:38:58:FC:30:3C:2B:28:24:2E
         SHA1: AC:74:9E:A5:41:AD:D6:F7:E1:6E:5E:F9:FD:96:49:FD:E4:E0:D9:C5
         SHA256: CE:77:51:60:00:CB:DF:D5:AE:EA:5D:4F:5C:85:47:14:D9:5A:56:72:66:
F1:A8:E8:8D:1D:E9:50:CC:3B:81:1A
         签名算法名称: SHA256withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 19 0A A6 EB 67 E9 B8 5B   F5 58 03 2D B9 5D E8 93  ....g..[.X.-.]..
0010: CD 4B B1 98                                        .K..
]
]

2.3.5、证书导入

命令:

keytool -import -alias client -file client.cer -keystore testStore.keystore -storepass 666666 -keypass 666666

输出:

所有者: CN=localhost
发布者: CN=localhost
序列号: 5b1bd334
有效期开始日期: Fri Jan 04 20:26:42 CST 2019, 截止日期: Sat Jan 04 20:26:42 CST
2020
证书指纹:
         MD5: A1:F3:B4:96:42:E9:62:2B:8C:FB:83:3B:E2:EE:A3:29
         SHA1: 33:01:EA:A6:12:EF:E3:8E:4C:CA:11:EF:D9:4D:DF:3D:85:93:7D:29
         SHA256: 1F:EE:BF:D3:15:78:E2:34:F5:53:0E:49:43:58:0E:BB:3E:A5:EE:22:0E:
D1:82:42:95:CD:19:FA:B1:03:7D:74
         签名算法名称: SHA256withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 23 9D 32 A9 0F C6 C1 1B   95 57 16 88 20 3E B7 0C  #.2......W.. >..
0010: 2B 8D 21 59                                        +.!Y
]
]

是否信任此证书? [否]:  y
证书已添加到密钥库中

2.3.6、删除证书

命令:

keytool -delete -alias zhaozhou -keystore testStore.keystore -storepass 666666

输出:

参考博客:

http://tutorials.jenkov.com/java-cryptography/keystore.html
https://www.cnblogs.com/littleatp/p/5922362.html
https://czj4451.iteye.com/blog/1487684
https://my.oschina.net/suyewanwan/blog/164108
https://my.oschina.net/u/2502527/blog/596425
https://my.oschina.net/xinxingegeya/blog/264635
https://www.chinassl.net/ssltools/keytool-commands.html
http://www.cnblogs.com/xdp-gacl/p/3750965.html
https://blog.csdn.net/liumiaocn/article/details/61921014

3、Netty+TLS的加密通信

3.1、单向认证

3.1.1、秘钥及签名证书生成

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

keytool -genkey -alias server -keysize 2048 -validity 3650 -keyalg RSA -dname "CN=localhost" -keypass nettyDemo -storepass nettyDemo -keystore serverStore.jks

说明:
-alias server:服务器证书仓库关联的别名为
-keypass nettyDemo:服务器私钥密码
-storepass nettyDemo:服务器秘钥库密码
-keystore serverStore.jks:服务器秘钥库的文件名(默认放在用户主目录下)

(2)导出Netty服务端签名证书:

keytool -export -alias server -keystore serverStore.jks -storepass nettyDemo -file server.cer

(3)生成Netty客户端的公钥、私钥和证书仓库:

keytool -genkey -alias client -keysize 2048 -validity 3650 -keyalg RSA -dname "CN=localhost" -keypass nettyDemo -storepass nettyDemo -keystore clientStore.jks

说明:

-alias client:客户端证书仓库关联的别名;

-keypass nettyDemo:客户端私钥密码;

-storepass nettyDemo:客户端秘钥库密码

-keystore clientStore.jks:客户端秘钥库的文件名(默认放在用户主目录下)

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

keytool -import -trustcacerts -alias server -file server.cer -storepass nettyDemo -keystore clientStore.jks

最终生成文件:

3.1.2、源码示例

(1)、代码目录

源码目录.png

clientStore.jks为客户端的秘钥库,serverStore.jks为服务端的秘钥库,server.cer为服务端的公钥;

单向认证示例源码:https://github.com/zhaozhou11/netty-demo.git

(2)、服务端SSLContext生成

public static SSLContext getServerContext(String pkPath){
   if(SERVER_CONTEXT!=null) return SERVER_CONTEXT;
   InputStream in =null;
   
   try{
      //密钥管理器
      KeyManagerFactory kmf = null;
      if(pkPath!=null){
         //密钥库KeyStore
         KeyStore ks = KeyStore.getInstance("JKS");
         //加载服务端证书
         in = new FileInputStream(pkPath);
         //加载服务端的KeyStore  ;sNetty是生成仓库时设置的密码,用于检查密钥库完整性的密码
         ks.load(in, "nettyDemo".toCharArray());
         
         kmf = KeyManagerFactory.getInstance("SunX509");
         //初始化密钥管理器
         kmf.init(ks, "nettyDemo".toCharArray());
      }
      //获取安全套接字协议(TLS协议)的对象
      SERVER_CONTEXT= SSLContext.getInstance(PROTOCOL);
      //初始化此上下文
      //参数一:认证的密钥      参数二:对等信任认证  参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null
      SERVER_CONTEXT.init(kmf.getKeyManagers(), null, null);
      
   }catch(Exception e){
      throw new Error("Failed to initialize the server-side SSLContext", e);
   }finally{
      if(in !=null){
         try {
            in.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
      
   }
   return SERVER_CONTEXT;
 }

(3)、客户端SSLContext生成:

public static SSLContext getClientContext(String caPath){
 if(CLIENT_CONTEXT!=null) return CLIENT_CONTEXT;
 
 InputStream tIN = null;
 try{
    //信任库 
   TrustManagerFactory tf = null;
   if (caPath != null) {
      //密钥库KeyStore
       KeyStore tks = KeyStore.getInstance("JKS");
       //加载客户端证书
       tIN = new FileInputStream(caPath);
       tks.load(tIN, "nettyDemo".toCharArray());
       tf = TrustManagerFactory.getInstance("SunX509");
       // 初始化信任库  
       tf.init(tks);
   }
    
    CLIENT_CONTEXT = SSLContext.getInstance(PROTOCOL);
    //设置信任证书
    CLIENT_CONTEXT.init(null,tf == null ? null : tf.getTrustManagers(), null);
    
 }catch(Exception e){
    throw new Error("Failed to initialize the client-side SSLContext");
 }finally{
    if(tIN !=null){
         try {
            tIN.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
 }
 
 return CLIENT_CONTEXT;
}

(4)服务端ChannelHandler初始化:

ChannelPipeline pipeline = sc.pipeline();
String sChatPath = (System.getProperty("user.dir")+ "/src/main/java/com/zhaozhou/netty/demo/ssl/conf/oneway/serverStore.jks");

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

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

// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new LineBasedFrameDecoder(1024, false, false));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());

// and then business logic.
pipeline.addLast("handler", new SslOneWayServerHandler());

(5)、服务端ChannelHandler初始化:

ChannelPipeline pipeline = ch.pipeline();
String cChatPath =  System.getProperty("user.dir")+"/src/main/java/com/zhaozhou/netty/demo/ssl/conf/oneway/clientStore.jks";

SSLEngine engine = SslOneWayContextFactory.getClientContext(cChatPath)
      .createSSLEngine();//创建SSLEngine
engine.setUseClientMode(true);//客户方模式
pipeline.addLast("ssl", new SslHandler(engine));

// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new LineBasedFrameDecoder(1024, false, false));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());

// and then business logic.
pipeline.addLast("handler", new SslOneWayClientHandler());

(6)、测试输出

服务端输出:

单向服务端输出.png

客户端输出:

单向客户端输出.png

3.2、双向认证

3.2.1、秘钥及签名证书生成

双向认证的前面4步和单向认证的一样,双向认证多两步,需导出客户端公钥并将客户端公钥导入服务端的证书仓库:

(5)、导出Netty的客户端的自签名证书:

keytool -export -alias client -keystore clientStore.jks -storepass nettyDemo -file client.cer

(6)、将客户端的自签名证书导入到服务器的证书仓库中:

keytool -import -trustcacerts -alias client -file client.cer -storepass nettyDemo -keystore serverStore.jks

3.2.2、源码示例

双向认证的源码示例与单向认证基本相同,在此不多赘述。

双向认证示例源码:https://github.com/zhaozhou11/netty-demo.git

3.2.3、测试输出

服务端输出:

双向服务端输出.png

客户端输出:

双向客户端输出.png

参考博客:

https://blog.csdn.net/zhixinhuacom/article/details/79097737
https://blog.csdn.net/zhixinhuacom/article/details/79126274
https://my.oschina.net/xinxingegeya/blog/267804
https://www.cnblogs.com/liuroy/p/7435356.html
https://dwj147258.iteye.com/blog/2339934
https://dwj147258.iteye.com/blog/2360535
https://blog.csdn.net/virgilli/article/details/42836063
https://waylau.gitbooks.io/essential-netty-in-action/CORE%20FUNCTIONS/Securing%20Netty%20applications%20with%20SSLTLS.html

上一篇 下一篇

猜你喜欢

热点阅读