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

1、SSL/TLS简介

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

1.1、作用

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

窃听风险(eavesdropping): 第三方可以获知通信内容。 篡改风险(tampering): 第三方可以修改通信内容。 冒充风险(pretending): 第三方可以冒充他人身份参与通信。

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

保密: 在握手协议中定义了会话密钥后,所有的消息都被加密。 鉴别: 可选的客户端认证,和强制的服务器端认证。 完整性: 传送的消息包括消息完整性检查(使用MAC)。

1.2、工作原理

1.2.1、基本概念

  • Key: Key 是一个比特(bit)字符串,用来加密解密数据的,就像是一把开锁的钥匙。

  • 对称算法(symmetric cryptography): 就是需要双方使用一样的 key 来加密解密消息算法,常用密钥算法有 Data Encryption Standard(DES)、triple-strength DES(3DES)、Rivest Cipher 2 (RC2)和 Rivest Cipher 4(RC4)。因为对称算法效率相对较高,因此 SSL 会话中的敏感数据都用通过密钥算法加密。

  • 非对称算法(asymmetric cryptography): 就是 key 的组成是公钥私钥对 (key-pair),公钥传递给对方私钥自己保留。公钥私钥算法是互逆的,一个用来加密,另一个可以解密。常用的算法有 Rivest Shamir Adleman(RSA)、Diffie-Hellman(DH)。非对称算法计算量大比较慢,因此仅适用于少量数据加密,如对密钥加密,而不适合大量数据的通讯加密。

  • 公钥证书(public key certificate): 公钥证书类似数字护照,由受信机构颁发。受信组织的公钥证书就是 certificate authority(CA)。多证书可以连接成证书串,第一个是发送人,下一个是给其颁发证书实体,往上到根证书是世界范围受信组织,包括 VeriSign, Entrust, 和 GTE CyberTrust。公钥证书让非对称算法的公钥传递更安全,可以避免身份伪造,比如 C 创建了公钥私钥,对并冒充 A 将公钥传递给 B,这样 C 与 B 之间进行的通讯会让 B 误认是 A 与 B 之间通讯。

  • 加密哈希功能(Cryptographic Hash Functions): 加密哈希功能与 checksum 功能相似。不同之处在于,checksum 用来侦测意外的数据变化而前者用来侦测故意的数据篡改。数据被哈希后产生一小串比特字符串,微小的数据改变将导致哈希串的变化。发送加密数据时,SSL 会使用加密哈希功能来确保数据一致性,用来阻止第三方破坏通讯数据完整性。SSL 常用的哈希算法有 Message Digest 5(MD5)和 Secure Hash Algorithm(SHA)。

  • 消息认证码(Message Authentication Code): 消息认证码与加密哈希功能相似,除了它需要基于密钥。密钥信息与加密哈希功能产生的数据结合就是哈希消息认证码(HMAC)。如果 A 要确保给 B 发的消息不被 C 篡改,他要按如下步骤做 --A 首先要计算出一个 HMAC 值,将其添加到原始消息后面。用 A 与 B 之间通讯的密钥加密消息体,然后发送给 B。B 收到消息后用密钥解密,然后重新计算出一个 HMAC,来判断消息是否在传输中被篡改。SSL 用 HMAC 来保证数据传输的安全。

  • 数字签名(Digital Signature): 一个消息的加密哈希被创建后,哈希值用发送者的私钥加密,加密的结果就是叫做数字签名。

  • 1.2.2、认证类型:

    单向认证: 就是用户到服务器之间只存在单方面的认证,即客户端会认证服务器端身份,而服务器端不会去对客户端身份进行验证。首先,客户端发起握手请求,服务器收到握手请求后,会选择适合双方的协议版本和加密方式。然后,再将协商的结果和服务器端的公钥一起发送给客户端。客户端利用服务器端的公钥,对要发送的数据进行加密,并发送给服务器端。服务器端收到后,会用本地私钥对收到的客户端加密数据进行解密。然后,通讯双方都会使用这些数据来产生双方之间通讯的加密密钥。接下来,双方就可以开始安全通讯过程了。 双向认证: 就是双方都会互相认证,也就是两者之间将会交换证书。基本的过程和单向认证完全一样,只是在协商阶段多了几个步骤。在服务器端将协商的结果和服务器端的公钥一起发送给客户端后,会请求客户端的证书,客户端则会将证书发送给服务器端。然后,在客户端给服务器端发送加密数据后,客户端会将私钥生成的数字签名发送给服务器端。而服务器端则会用客户端证书中的公钥来验证数字签名的合法性。建立握手之后过程则和单向通讯完全保持一致。

    1.2.3、握手协议(Handshake protocol)

    握手流程:

    步骤 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连接提供两个服务:

    保密性: 使用握手协议定义的秘密密钥实现 完整性: 握手协议定义了MAC,用于保证消息完整性

    记录协议的过程:

    1.2.5、报警协议(Alert protocol)

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

    1.2.6、秘钥生成过程

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

    参考博客:

    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的类型

  • JKS和JCEKS是Java密钥库(KeyStore)的两种比较常见类型(我所知道的共有5种,JKS, JCEKS, PKCS12, BKS,UBER)。
  • JKS的Provider是SUN,在每个版本的JDK中都有,JCEKS的Provider是SUNJCE,1.4后我们都能够直接使用它。
  • JCEKS在安全级别上要比JKS强,使用的Provider是JCEKS(推荐),尤其在保护KeyStore中的私钥上(使用TripleDes)。
  • PKCS#12是公钥加密标准,它规定了可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件,在windows中可以直接导入到密钥区,注意,PKCS#12的密钥库保护密码同时也用于保护Key。
  • BKS 来自BouncyCastle Provider,它使用的也是TripleDES来保护密钥库中的Key,它能够防止证书库被不小心修改(Keystore的keyentry改掉1个 bit都会产生错误),BKS能够跟JKS互操作,读者可以用Keytool去TryTry。
  • UBER比较特别,当密码是通过命令行提供的时候,它只能跟keytool交互。整个keystore是通过PBE/SHA1/Twofish加密,因此keystore能够防止被误改、察看以及校验。以前,Sun JDK(提供者为SUN)允许你在不提供密码的情况下直接加载一个Keystore,类似cacerts,UBER不允许这种情况。
  • 2.2、keytool的命令

    -genkey: 在用户主目录中创建一个默认文件".keystore",还会产生一个mykey的别名,mykey中包含用户的公钥、私钥和证书,(默认情况下,keystore会存在用户系统默认目录,如:win系统,会生成在C:\Documents and Settings\用户名\文件名为“.keystore”); -keystore: 指定密钥库的名称(产生的各类信息将不在.keystore文件中); -keyalg: 指定密钥的算法 (如RSA、DSA,default:DSA); -validity: 指定创建的证书有效期多少天(default:90); -keysize: 指定密钥长度(default:1024,范围:512 ~ 2048); -storepass: 指定密钥库的密码(获取keystore信息所需的密码); -keypass: 指定别名条目的密码(私钥的密码); -dname: 指定证书拥有者信息。例如: "CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码"; -list: 显示密钥库中的证书信息。keytool -list -v -keystore 指定keystore -storepass 密码 -v 显示密钥库中的证书详细信息; -export: 将别名指定的证书导出到文件。keytool -export -alias 需要导出的别名 -keystore 指定keystore -file 指定导出的证书位置及证书名称 -storepass 密码; -delete: 删除密钥库中某条目。keytool -delete -alias 指定需删除的keystore别名 -keystore 指定keystore -storepass 密码; -printcert: 查看导出的证书信息。keytool -printcert -file yushan.crt; -keypasswd: 修改密钥库中指定条目口令。keytool -keypasswd -alias 需修改的别名 -keypass 旧密码 -new 新密码 -storepass keystore密码 -keystore sage; -storepasswd: 修改keystore口令。keytool -storepasswd -keystore e:\yushan.keystore(需修改口令的keystore) -storepass 123456(原始密码) -new yushan(新密码); -import: 将已签名数字证书导入密钥库。keytool -import -alias 别名 -keystore指定keystore -file需导入的证书;

    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
    证书指纹:
             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
    证书指纹:
             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
    证书指纹:
             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
    

    最终生成文件:

    conf/oneway/clientStore.jks:客户端的证书仓库(包含公钥、私钥、信任的证书仓库(服务端的证书)) conf/oneway/serverStore.jks:服务端的证书仓库(包含公钥、私钥、信任的证书仓库(无证书)) conf/oneway/server.cer:服务端字签名证书的导出文件

    3.1.2、源码示例

    (1)、代码目录

    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;
          //密钥管理器
          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;
        //信任库 
       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)、测试输出

    服务端输出:

    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、测试输出

    服务端输出:

    参考博客:

    https://blog.csdn.net/zhixinhuacom/article/details/79097737
    https://blog.csdn.net/zhixinhuacom/article/details/79126274
    https://my.oschina.net/xinxingegeya/blog/267804