netty系列:使用 SSL/TLS 加密 Netty 程序

小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动。

对netty感兴趣的小伙伴可以点击这里哦,我的 netty专栏

今天数据隐私是一个十分关注的问题,作为开发人员,我们需要准备好解决这个问题。至少我们需要熟悉加密协议 SSL 和 TLS 等之上的其他协议实现数据安全。作为一个 HTTPS 网站的用户,你是安全。当然,这些协议是广泛不基于 http 的应用程序,例如安全SMTP(SMTPS)邮件服务,甚至关系数据库系统。

为了支持 SSL/TLS,Java 提供了 javax.net.ssl API 的类 SslContext SslEngine 使它相对简单的实现解密和加密。Netty 的利用该 API 命名 SslHandler 的 ChannelHandler 实现,有一个内部 SslEngine 做实际的工作。

SSL/TLS概述

SSL全称是Secure Sockets Layer,安全套接字层,它是由网景公司(Netscape)设计的主要用于Web的安全传输协议,目的是为网络通信提供机密性、认证性及数据完整性保障。如今,SSL已经成为互联网保密通信的工业标准。

SSL/TLS 位于TCP层和应用层之间,具体如下图所示:

Sslhandler类

Sslhandler 继承自 ByteToMessageDecoder 并实现了 ChannelOutboundHandler 接口,因此可以像其他 ChannelHandler 一样添加到 ChannelPipeline 中,下图展示了 SslHandler 数据流图。

  • 加密的入站数据被 SslHandler 拦截,进行解密。
  • 前面加密的数据被 SslHandler 解密后,原始数据入站。
  • 原始数据经过 SslHandler。
  • SslHandler 加密数据并它传递出站。
  • 以下为一个 SslHandler 使用 ChannelInitializer 添加到 ChannelPipeline 的示例。

    public class SslChannelInitializer extends ChannelInitializer<Channel> {
        private final SslContext context;
        private final boolean startTls;
        public SslChannelInitializer(SslContext context,
        boolean client, boolean startTls) {   //1
            this.context = context;
            this.startTls = startTls;
        @Override
        protected void initChannel(Channel ch) throws Exception {
            SSLEngine engine = context.newEngine(ch.alloc());  //2
            engine.setUseClientMode(client); //3
            ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls));  //4
    
  • 使用构造函数来传递 SSLContext 用于使用(startTls 是否启用)。
  • 从 SslContext 获得一个新的 SslEngine 。给每个 SslHandler 实例使用一个新的 SslEngine。
  • 设置 SslEngine 是 client 或者是 server 模式。
  • 添加 SslHandler 到 pipeline 作为第一个处理器。
  • 在大多数情况下,SslHandler 将成为 ChannelPipeline 中的第一个 ChannelHandler 。这将确保所有其他 ChannelHandler 应用他们的逻辑到数据后加密后才发生,从而确保他们的变化是安全的。

    SslHandler 有很多有用的方法,如下表所示。例如,在握手阶段两端相互验证,商定一个加密方法。你可以配置 SslHandler 修改其行为或提供 在SSL/TLS 握手完成后发送通知,这样所有数据都将被加密。 SSL/TLS 握手将自动执行。

    姓名描述
    setHandshakeTimeout(...) setHandshakeTimeoutMillis(...) getHandshakeTimeoutMillis()设置和获取超时,之后握手 ChannelFuture 被通知失败。
    setCloseNotifyTimeout(...) setCloseNotifyTimeoutMillis(...) getCloseNotifyTimeoutMillis()设置并获取超时时间,在此之后关闭通知将超时并且连接将关闭。这也会导致关闭通知 ChannelFuture 失败。
    握手未来()返回一个 ChannelFuture,一旦握手完成,就会收到通知。如果握手之前完成,它将返回一个包含上次握手结果的 ChannelFuture。
    关闭(...)发送 close_notify 请求关闭并销毁底层 SslEngine。