zoukankan      html  css  js  c++  java
  • Mina Core 11-SSL过滤

    SslFilter是负责管理通过安全连接发送的数据的加密和解密的过滤器。每当您需要建立安全连接或转换现有连接以使其安全时,您必须在过滤器链中添加SslFilter。

    由于任何会话都可以随意修改它的消息过滤器链,因此它允许在打开的连接上使用startTLS等协议。

    请注意,虽然名称包含SSL,但SslFilter支持TLS。实际上,TLS应该已经取代了SSL,但由于历史原因,SSL仍然被广泛使用。

    基本用法

    如果您希望您的应用程序支持SSL / TLS,只需在您的链中添加SslFilter:

    DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
    SslFilter sslFilter = new SslFilter(sslContext);
    chain.addFirst("sslFilter", sslFilter);
    

      

    你显然也需要一个SslContext实例:

    SSLContext sslContext;
     
        try
        {
            // Initialize the SSLContext to work with our key managers.
            sslContext = SSLContext.getInstance( "TLS" );
            sslContext.init( ... ); // Provide the needed KeyManager[], TrustManager[] and SecureRandom instances
        }
        catch ( Exception e )
        {
            // Handle your exception
        }
    

      

    这取决于您提供KeyManager,TrustManager和SecureRandom实例。

    一定要在链条的第一个位置注入SslFilter!

    稍后我们将看到有关如何创建SSLContext的详细示例。

    一点理论

    如果您想更深入地了解它是如何工作的,请阅读以下段落......

    SSL基础知识

    我们不打算解释SSL是如何工作的,有很好的书籍。我们将简要介绍它如何工作以及如何在MINA中实施。

    首先,您必须了解SSL / TLS是RFC中定义的协议:TLS 1.0,TLS 1.1和TLS 1.2。正在制定TLS 1.3草案......

    它最初是由Netscape开发的,在成为TLS之前命名为SSL(从1.0到3.0)。如今,SSL 2.0 *和SSL 3.0 **已被弃用,不应使用。

    SSL / TLS协议

    由于它是一个协议,它需要客户端和服务器之间的一些对话。这就是SSL / TLS的内容:描述此对话框。

    足以知道任何安全交换被称为握手的否定阶段排除,该角色是在客户端和服务器之间就将要使用的加密方法达成协议。基本的SSL / TLS会话将是一个看起来像:

     

    正如您在此图中所看到的,它是一个两阶段协议:首先是握手,然后在完成时客户端和服务器将能够交换将被加密的数据

    握手

    基本上,它都是关于用于加密数据的许多元素的否定。详细信息在本文档的上下文中并不那么有趣,足以说许多消息将在客户端和服务器之间交换,并且在此阶段不会发送任何数据。

    实际上,握手启动有两个条件:服务器必须等待一些握手消息到达客户端必须发送ClientHello消息

    我们使用Java SSLEngine类来管理整个SSL / TLS协议。 MINA应该注意的是会话的当前状态是能够获取和处理客户端HelloClient消息。当您在过滤器链中注入SslFilter时,会发生以下几件事:

    1.创建了一个SslHandler实例(我们为每个会话创建一个实例)。此SslHandler实例负责整个处理(即将到来的消息的握手和加密/解密)

    2此SslHandler使用已附加到SslFilter的SslContext实例创建SSLEngine

    1. SslEngine实例已配置并初始化
    2. SslHandler实例存储在会话中
    3. 除非特别要求,否则我们启动握手(在客户端和服务器端具有不同的含义:客户端将发送ClientHello消息,而服务器切换到等待某些数据被解包的模式)。请注意,如果需要,可以在以后完成握手初始化

    我们都准备好了。接下来的几个步骤是纯SSL / TLS协议交换。如果调用了session.write()方法,则会将消息排入队列,等待握手完成。将SslFilter添加到链中时,任何挂起的消息都将导致SSL / TLS握手失败,因此请确保在要注入它时有一个干净的位置。我们也不会收到任何非SSL / TLS协议消息的消息。

    如果要实现StartTLS,最后一点非常重要:因为它允许您的应用程序随时从纯文本交换切换到加密交换,您必须确保双方都没有待处理的消息。显然,在客户端 - 启动StartTLS的一方 - 每个待处理的消息都将在发送StartTLS消息之前发送,但它必须阻止任何其他不属于后续握手的消息,直到握手完成为止。在服务器端,一旦收到StartTLS消息,就不应该向远程对等体写入消息。

    事实上,在握手完成之前,在链中注入SslFilter应该阻止任何不属于握手协议的交换。如果您在握手完成之前提交要发送和加密的消息,则不会拒绝该消息,而是排队并在握手完成后处理该消息。

    之后,发送的每条消息都将通过SslHandler实例进行加密,并且每个收到的消息必须由SslHandler完全解密,然后才能用于下一个过滤器。

    发送数据

             OK,Handshaked已经完成了。您的SslFilter已准备好处理传入和传出消息。让我们关注你的会话要写的那些。

        一个重要的事情是你可以在同一个会话中写一个以上的消息(如果你的链中有一个Executor)。问题是SSLEngine一次不能处理多个消息。我们需要序列化正在写出的消息。更糟糕的是:您无法同时处理传入的消息和传出消息。

        总而言之,SSL / TLS处理就像一个黑盒子,只接受一个输入,在完成任务之前无法处理任何事情。以下模式表示它对传出消息的工作方式。

       

    传入消息并没有那么不同,除了我们在IoProcessor和SslFilter之间没有Executor。这使事情变得更简单,除了一件重要的事情发生:当我们处理传入的消息时,我们不能再处理外出消息了。请注意,它也适用于其他方式:当处理传出消息时,我们无法处理传入消息:

     

    这里重要的是SslHander一次不能处理多个消息。

    MINA 2中的SSL /TLS

    现在,我们将深入探讨MINA代码。我们将介绍所有过滤操作:

    管理:

    • init()
    • destroy()
    • onPreAdd(IoFilterChain, String, NextFilter)
    • onPostAdd(IoFilterChain, String, NextFilter)
    • onPreRemove(IoFilterChain, String, NextFilter)
    • onPostRemove(IoFilterChain, String, NextFilter)

    会话事件:

    • sessionCreated(NextFilter, IoSession)
    • sessionOpened(NextFilter, IoSession)
    • sessionClosed(NextFilter, IoSession)
    • sessionIdle(NextFilter, IoSession, IdleStatus)
    • exceptionCaught(NextFilter, IoSession, Throwable)
    • filterClose(NextFilter, IoSession)
    • inputClosed(NextFilter, IoSession)

    消息事件:

    • messageReceived(NextFilter, IoSession, Object)
    • filterWrite(NextFilter, IoSession, WriteRequest)
    • messageSent(NextFilter, IoSession, WriteRequest)

    管理

    以下是Filter的管理方法:

    onPreAdd

    这是我们创建SslHandler实例并初始化它的地方。我们还定义了支持的密码。

    SslHandler实例本身将创建一个SSLEngine实例,并使用SslFilter中设置的所有参数对其进行配置:

    1.如果这是客户端或服务器端

    2.当它是服务器端时,表示我们想要或需要客户端身份验证的标志

    3.已启用密码的列表

    4.已启用协议的列表

    完成后,对该实例的引用将存储到Session的属性中。

    onPostAdd

    这是我们开始握手的地方,如果它没有明确推迟。这就是这种方法的作用。所有逻辑都由SslHandler实现。

    onPreRemove

    在这里,我们停止SSL会话并清理会话(从会话的链中删除过滤器,从会话的属性中删除SslHandler实例)。在刷新任何尚未处理的事件后,Sslhandler实例也被破坏。

    会话事件

    以下是通过过滤器链传播并由SslFilter处理的事件:

    sessionClosed

    我们只是销毁SslHandler实例。

    exceptionCaught

    当异常是由关闭的会话引起时,我们有一个特殊的任务要继续:我们必须收集所有消息,将它们添加到将要传播的异常中。

    filterClose

    在这里,如果启动了SSL会话,我们需要关闭它。无论如何,我们将事件传播到链中的下一个过滤器。

    消息事件

    最后,并非最不重要的是,与消息相关的三个事件

    messageReceived事件

    当我们从套接字读取一些数据时收到此事件。我们必须处理一些极端情况:握手已经完成握手已经启动但未完成*没有握手已经开始,并且SslHandler尚未初始化

    这三个用例按频率顺序列出。让我们看看每个用例会发生什么。

    握手已经完成

    好!这意味着每个传入的消息都封装在SSL / TLS信封中,并且应该被解密。现在,我们讨论的是消息,但实际上我们接收的字节可能需要聚合以形成完整的消息(至少在TCP中)。如果消息被分段,我们将收到许多缓冲区,当我们收到最后一块时,我们将能够完全解密它。请记住,我们在所有过程中都被阻止,这可能会阻止此会话的SslHandler实例很长一段时间......

    在任何情况下,每个数据块都由SslHandler处理,SslHandler将其收到的字节的解密委托给SslEngine。

    这是我们在messageReceived()中实现的基本算法:

    get the session's sslHandler
     
    syncrhonized on sshHandler {
        if handshake completed
            then
                get the sslHandler decrypting the data
                if the application buffer is completed, push it into the message to forward to the IoHandler
            else
                enqueue the incoming data
    }
     
    flush the messages if any
    

      

    这里的重要部分是SslHandler将累积数据,直到它有一个完整的消息进入链。这可能需要一段时间,并且许多套接字读取。原因是SSLEngine无法处理消息,除非它具有完全解码消息的所有字节。

    提示:增加传输缓冲区大小以限制发送大消息所需的往返次数。

    握手尚未完成

    这意味着接收的消息是握手协议的一部分。没有任何东西会传播到IoHandler,消息将由SslHandler使用。

    在完成全部握手之前,每个传入的数据都将被视为握手protocl消息。

    同时,IoHandler将被排队的消息,等待Handshake完成。

    这是一个模式,表示在两次往返中收到数据时的完整过程:

     

    filterwWrite事件

    调用IoSession.write()方法时将处理此事件。

    如果未启动SSL会话,我们只是累积要写入的消息。它将在稍后发送。

    对于一些非常具体的需求,这里有一个棘手的参数。通常,在实现startTLS协议时,服务器通过应用程序消息(可能是响应)从非安全连接切换到安全连接,我们需要在SslFilter之前将响应发送回客户端已安装(否则,响应将被阻止,安全连接的安装将失败)。这是DISABLE_ENCRYPTION_ONCE属性。它包含的内容并不重要(它可以只是一个布尔值),这个参数在第一个消息的会话中出现就足以通过分配SslFilter。

    我们控制会话属性中DISABLE_ENCRYPTION_ONCE标志的存在,如果存在,我们将其从会话中删除,并将未加密的消息推送到要发送的消息队列中。

        否则,如果握手尚未完成,我们将消息保留在队列中,如果已完成,我们对其进行加密并安排将其写入。

    如果某个消息已被安排写入,我们将它们全部清除。

    SSLContext初始化

    我们看到,为了建立SSL会话,我们需要创建一个SSLContext。这是代码:

    SSLContext sslContext;
     
    try
    {
        // Initialize the SSLContext to work with our key managers.
        sslContext = SSLContext.getInstance( "TLS" );
        sslContext.init( ... ); // Provide the needed KeyManager[], TrustManager[] and SecureRandom instances
    }
    catch ( Exception e )
    {
        // Handle your exception
    }
    

      

    我们这里没有公开的是构造函数和init()方法。

    SSLContext可以通过其构造函数显式创建 - 或者我们要求静态工厂返回一个实例(这是我们在前面的代码中所做的。第二种方法非常简单,大部分时间都适合。它足以传递要使用的协议的名称,它是以下之一:

    • SSL
    • SSLv2
    • SSLv3
    • TLS
    • TLSv1
    • TLSv1.1
    • TLSv1.2 (not supported in Java 6)

    如果您的客户支持,强烈建议选择更高的算法(即TLSv1.2)。

    init()方法有3个参数:

    1.一个KeyManager(可以为null)

    2.一个TrustManager(可以为null)

    3.随机生成器(可以为null)

    如果参数设置为null,则安装的安全提供程序将选择优先级最高的实现。

     

     

  • 相关阅读:
    Zabbix5 Frame 嵌套
    Zabbix5 对接 SAML 协议 SSO
    CentOS7 安装 Nexus
    CentOS7 安装 SonarQube
    GitLab 后台修改用户密码
    GitLab 查看版本号
    GitLab Admin Area 500 Error
    Linux 安装 PostgreSQL
    Liger ui grid 参数
    vue.js 是一个怪东西
  • 原文地址:https://www.cnblogs.com/fubinhnust/p/9944461.html
Copyright © 2011-2022 走看看