Netty Tls实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/changyayun/article/details/78170271

Netty Tls实现

TLS作用

如果没有使用TLS的话会存在一下风险

(1) 窃听风险(eavesdropping):第三方可以获知通信内容。

(2) 篡改风险(tampering):第三方可以修改通信内容。

(3) 冒充风险(pretending):第三方可以冒充他人身份参与通信。

TLS发展历史

1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。

1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。

1996年,SSL 3.0版问世,得到大规模应用。

1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS1.0版。

2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版

TLS基本原理

简单的阐述

(1) 客户端向服务器端索要并验证公钥。

(2) 双方协商生成”对话密钥”。

(3) 双方采用”对话密钥”进行加密通信。

具体实现分为四步如下图所示
这里写图片描述

具体过程

客户端发出请求(ClientHello)

这一步,客户端向服务器提供一下信息

(1) 支持的协议版本,比如TLS 1.0版。

(2) 一个客户端生成的随机数,稍后用于生成”对话密钥”。

(3) 支持的加密方法,比如RSA公钥加密。

(4) 支持的压缩方法。

服务器回复ServerHello

这一步,服务器向客户端回应一下信息

(1) 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。

(2) 一个服务器生成的随机数,稍后用于生成”对话密钥”。

(3) 确认使用的加密方法,比如RSA公钥加密。

(4) 服务器证书。

客户端回应

这一步客户端校验服务器的证书是否可信,并提取证书中的公钥

(1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。

(2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

(3) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。

服务器最后回应

(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。

证书生成

生成Netty服务端私钥和证书仓库命令:

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

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

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

生成客户端的密钥对和证书仓库,用于将服务端的证书保存到客户端的授信证书仓库中,命令如下:

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

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

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

查看证书信息可以通过

keytool -list -v -keystore cChat.jks -storepass cNetty
别名: securechat
创建日期: 2017-9-19
条目类型: trustedCertEntry

所有者: CN=localhost
发布者: CN=localhost
序列号: 2088aec6
有效期开始日期: Tue Sep 19 15:48:52 CST 2017, 截止日期: Wed Sep 19 15:48:52 CST 2018
证书指纹:
     MD5: 59:C6:7E:54:41:9F:F0:E8:87:02:CD:5B:37:13:02:53
     SHA1: 78:F7:63:A9:5B:A0:4F:BC:86:25:87:E9:72:4A:D1:BA:12:23:E7:5D
     SHA256: 0F:8B:0F:88:33:25:5D:67:48:D4:30:A8:AD:A3:5A:6B:CE:8D:2B:04:DF:FE:75:14:F4:00:62:DF:49:48:12:02
     签名算法名称: SHA256withRSA
     版本: 3

扩展: 

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A7 F3 D3 80 56 2C B2 AB   51 EC BC 98 4F C5 D3 79  ....V,..Q...O..y
0010: 5A 51 53 48                                        ZQSH
]
]

Netty 代码实现

服务端实现

服务端需要加载自己的证书仓库文件,在客户端链接是下发证书及公钥

    private ChannelHandler createSslHandler(SSLContext sslContext, boolean needsClientAuth) {
        SSLEngine sslEngine = sslContext.createSSLEngine();
        sslEngine.setUseClientMode(false);
        if (needsClientAuth) {
            sslEngine.setNeedClientAuth(true);
        }
        return new SslHandler(sslEngine);
    }

    private SSLContext initSSLContext() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {

        KeyStore ks = KeyStore.getInstance("JKS");
        InputStream ksInputStream = new FileInputStream("/Users/changyayun/sChat.jks");
        ks.load(ksInputStream, "sNetty".toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, "sNetty".toCharArray());
        SSLContext sslContext = SSLContext.getInstance("TLS");
        try {
            sslContext.init(kmf.getKeyManagers(), null, null);
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return sslContext;
    }

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

客户端代码

客户端加载自己的可信证书仓库,校验服务器下发的证书是否在可信证书中

   private ChannelHandler createSslHandler(SSLContext sslContext) {
        SSLEngine sslEngine = sslContext.createSSLEngine();
        sslEngine.setUseClientMode(true);
        return new SslHandler(sslEngine);
    }
    public SSLContext getClientSSLContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException, CertificateException {
        KeyStore trustKeyStore= KeyStore.getInstance("JKS");// 访问Java密钥库,JKS是keytool创建的Java密钥库
        InputStream keyStream = new FileInputStream("/Users/changyayun/cChat.jks");//打开证书文件(.jks格式)
        char keyStorePass[]="cNetty".toCharArray();  //证书密码
        trustKeyStore.load(keyStream,keyStorePass);


        TrustManagerFactory trustManagerFactory =   TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustKeyStore );//保存服务端的授权证书

        SSLContext   clientContext = SSLContext.getInstance( "TLS");
        clientContext.init(null, trustManagerFactory.getTrustManagers(), null);

        return clientContext;
    }

客户端SSLContext初始化完成之后,创建SSLEngine并将其设置为客户端工作模式.

最后将得到的SslHandler加入pipeline

ch.pipeline().addLast("tsl", createSslHandler(getClientSSLContext()));

调试观测

![Pasted Graphic](../Desktop/Pasted Graphic.tiff)
这里写图片描述

如上图所示在InteliJ开发环境中通过设置Run/Debug Configurations

VM option -Djavax.net.debug=ssl,handshake

Program arguments CA

猜你喜欢

转载自blog.csdn.net/changyayun/article/details/78170271