使用TLS加密netty应用

问题描述

自己基于Netty做了一个类IM项目,没有使用Spring框架,用到了应用层协议HTTP和websocket,在大致框架完成后,想对传输内容进行加密,便用到了TLS协议,查阅博文,发现基本都是使用自签证书的例子,很少是使用申请的正规SSL证书。因此用此博文记录自己的加密过程以及踩的坑。

SSL/TLS协议

在HTTP协议中,信息都是明文传输的,因此,存在以下三个问题:

  • 窃听风险(第三方可以获取明文信息的内容)
  • 篡改风险(第三方可以篡改信息的内容,而通信双方无法发觉)
  • 冒充风险(第三方可以冒充他人参与通信)

为了解决以上问题,提出了SSL/TLS协议,希望该协议能实现以下功能:

  • 所有信息都是加密传输的,第三方即使截获后也无法解析
  • 具有校验机制,一旦消息被篡改,可以立刻发现
  • 配备身份证书,可以防止第三方的冒充

TLS可以看作是SSL的升级版,是在SSL的基础上发展而来的。目前应用最广泛的是TLS1.0,也被称为SSL3.1,TLS1.1被称为SSL3.2,TLS1.2被称为SSL3.3。
SSL/TLS协议既用到了对称加密,也用到了非对称加密。关于对称加密和非对称加密,可以看看之前的文章。简单来说,对称加密速度快,但是安全性不高;而非对称加密虽然安全性比对称加密高,但是计算量大。因此,SSL/TLS采用了以下的方式来解决该问题:

  • 客户端向服务端索要并验证公钥
  • 双方协商生成“对话密钥”(采用对称加密的方式)
  • 双方采用“对话密钥”进行加密通信

TLS协议握手的详细过程如下:

1. 客户端发出请求

客户端向服务端发出请求加密通信的请求,并携带以下信息:

  • 支持的协议版本,例如TLS1.0
  • 一个随机数(后续用于生成对称密钥)
  • 支持的加密方式(非对称加密方式)
  • 支持的压缩方式
2. 服务器回应

收到消息后,服务端向客户端发出回应,并携带以下信息:

  • 确认使用的加密通信协议版本
  • 一个随机数(用于生成对称密钥,与客户端发来的随机数不是同一个)
  • 确认使用的加密方式
  • 服务端证书
    如果服务端想要验证客户端的证书,会向客户端发出请求获取客户端的证书。
3.客户端回应

收到服务端的回应报文后,客户端会先验证服务端的证书是否有效。通过验证后发送报文,包含的信息如下:

  • 一个随机数(该随机数被服务端的公钥加密)
  • 编码改变通知,表示随后的消息都是用“对话密钥”加密传送
  • 客户端握手结束通知,这一项同时也是前面所有内容的hash值,用来供服务端校验

为什么使用三个随机数来生成“对话密钥”?
首先,使用随机数是为了引入随机因素,保证每次会话生成的“对话密钥”都不相同;使用三个随机数是因为SSL/TLS协议认为主机生成的随机数并不是完全随机的,不完全随机就有可能被猜出来,使用三个随机数可以使随机数更接近于完全随机。

4.服务端回应

服务端收到第三个随机数后,向客户端发送响应消息,包含的信息如下:

  • 编码改变通知,表示随后的消息都将用生成的“对话密钥”加密传输
  • 服务端握手结束通知,同时也是前面所有内容的hash值,用来供客户端校验。

以上就是SSL/TLS协议的握手过程。

KeyStore

密钥库是用于存储加密密钥和证书的存储工具 ,最常用于SSL通信,以证明服务器和客户端的身份。密钥库中可以存储以下三种类型的数据:

  • PrivateKey:用于非对称加密过程中的私钥
  • Certificate证书:证书包含一个公钥,主要用于辨别服务器的身份
  • SecretKey:在对称加密中使用的密钥条目

密钥库有JKS,JCEKS,PKCS12,PKCS11和DKS这些类型,其中常见的是JKS,JCEKS,PKCS12这三种类型。
(之前自己就是不知道jks文件是密钥库文件,从而走了许多弯路,浪费了许多时间)

JKS(Java Key Store)

此密钥库是特定于Java平台的,通常具有jks的扩展名。此类型的密钥库可以包含私钥和证书,但不能用于存储密钥。由于它是Java特定的密钥库,因此不能在其他编程语言中使用。存储在JKS中的私钥无法在Java中提取。

JCEKS(Java Cryptography Extension KeyStore)

可以认为是增强式的JKS密钥库,支持更多算法。此密钥库具有jceks的扩展名。可以放入JCEKS密钥库的条目是私钥,密钥和证书。此密钥库通过使用Triple DES加密为存储的私钥提供更强大的保护。

PKCS12

一种标准的密钥库类型,可以在Java和其他语言中使用。它通常具有p12或pfx的扩展名。可以在此类型上存储私钥,密钥和证书。与JKS不同,PKCS12密钥库上的私钥可以用Java提取。此类型是可以与其他语言(如C,C ++或C#)编写的其他库一起使用。
在Java8及Java8以前的版本中,默认的密钥库是JKS,而在Java9中,默认的密钥库为PKCS12。

Netty

在对项目进行SSL/TLS加密升级前,需要先申请一个域名,由于还处于测试阶段我,我申请了一个免费的tk域名,然后在https://freessl.cn/网站上申请了免费的SSL证书。申请过程较为简单,使用邮箱注册后就能申请,因此就不赘述了。申请过后,会让你下载一个KeyManager软件,申请的证书和私钥会存放在这个软件中。选中需要的证书,然后点击导出证书,如下图所示。
在这里插入图片描述
我选择导出为jks格式的,是否加密私钥,我选择了是,并输入了加密的密码,假设密码为abcd。导出后,就会在你选择的文件夹内生成一个jks文件。然后,我们就可以用这个证书来升级自己的netty应用了。
在netty中进行加密升级,只需引入一个sslhandler就可以了。以下是部分重要代码,全部代码可以访问GitHub

//jksPath是jks文件所在的位置
String jksPath = "/home/xinfeng/cloudclipboard/serverStore.jks";
//storepass是密钥库文件的密码,keypass是加密私钥的密码,在这个例子中都是aaaa
SSLContext sslContext = SslContextFactory.getServerContext(jksPath,"nettyDemo","123456");
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
//不需要双向认证,即只需要验证服务端的证书即可
sslEngine.setNeedClientAuth(false);
SslHandler sslHandler = new SslHandler(sslEngine);

自定义类SslContextFactory中的重要代码如下:

KeyManagerFactory kmf = null;
if(pkPath!=null){
    //密钥库KeyStore
    KeyStore ks = KeyStore.getInstance("JKS");
//                Enumeration<String> enumeration = ks.aliases();
    //加载服务端证书
    in = new FileInputStream(pkPath);
    //加载服务端的KeyStore,  该密钥库的密码"storepass,storepass指定密钥库的密码(获取keystore信息所需的密码)
    ks.load(in, storepass.toCharArray());
    log.info("keystore加载成功!");

    kmf = KeyManagerFactory.getInstance("sunx509");
    //初始化密钥管理器, keypass 指定别名条目的密码(私钥的密码)
    kmf.init(ks, keypass.toCharArray());
    }
//获取安全套接字协议(TLS协议)的对象
sslContext = SSLContext.getInstance(PROTOCOL);
log.info("获取sslcontext成功!");
//初始化此上下文
//参数一:认证的密钥    参数二:对等信任认证,如果双向认证就写成tf.getTrustManagers()
// 参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null
sslContext.init(kmf.getKeyManagers(), null, null);

通过这种方式,即可为netty应用进行加密。

后记

自己在用netty程序导入jsk文件时,一直导入失败,提示密码错误。多方尝试后,猜测是KeyManager这个软件导出的文件可能存在一些问题。因此使用keytool命令又生成一个jks文件,将原jks文件中的私钥数据导入到新生成的jks文件中,再试,问题得以解决。
在这里插入图片描述

参考文献

  1. SSL/TLS协议运行机制的概述
  2. Java中不同类型的密钥库(Keystore) – 概述
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值