Class SslHandler

  • All Implemented Interfaces:
    ChannelDownstreamHandler, ChannelHandler, ChannelUpstreamHandler, LifeCycleAwareChannelHandler

    public class SslHandler
    extends FrameDecoder
    implements ChannelDownstreamHandler
    Adds SSL · TLS and StartTLS support to a Channel. Please refer to the "SecureChat" example in the distribution or the web site for the detailed usage.

    Beginning the handshake

    You must make sure not to write a message while the handshake is in progress unless you are renegotiating. You will be notified by the ChannelFuture which is returned by the handshake() method when the handshake process succeeds or fails.

    Handshake

    If isIssueHandshake() is false (default) you will need to take care of calling handshake() by your own. In most situations were SslHandler is used in 'client mode' you want to issue a handshake once the connection was established. if setIssueHandshake(boolean) is set to true you don't need to worry about this as the SslHandler will take care of it.

    Renegotiation

    If enableRenegotiation is true (default) and the initial handshake has been done successfully, you can call handshake() to trigger the renegotiation.

    If enableRenegotiation is false, an attempt to trigger renegotiation will result in the connection closure.

    Please note that TLS renegotiation had a security issue before. If your runtime environment did not fix it, please make sure to disable TLS renegotiation by calling setEnableRenegotiation(boolean) with false. For more information, please refer to the following documents:

    Closing the session

    To close the SSL session, the close() method should be called to send the close_notify message to the remote peer. One exception is when you close the Channel - SslHandler intercepts the close request and send the close_notify message before the channel closure automatically. Once the SSL session is closed, it is not reusable, and consequently you should create a new SslHandler with a new SSLEngine as explained in the following section.

    Restarting the session

    To restart the SSL session, you must remove the existing closed SslHandler from the ChannelPipeline, insert a new SslHandler with a new SSLEngine into the pipeline, and start the handshake process as described in the first section.

    Implementing StartTLS

    StartTLS is the communication pattern that secures the wire in the middle of the plaintext connection. Please note that it is different from SSL · TLS, that secures the wire from the beginning of the connection. Typically, StartTLS is composed of three steps:

    1. Client sends a StartTLS request to server.
    2. Server sends a StartTLS response to client.
    3. Client begins SSL handshake.
    If you implement a server, you need to:
    1. create a new SslHandler instance with startTls flag set to true,
    2. insert the SslHandler to the ChannelPipeline, and
    3. write a StartTLS response.
    Please note that you must insert SslHandler before sending the StartTLS response. Otherwise the client can send begin SSL handshake before SslHandler is inserted to the ChannelPipeline, causing data corruption.

    The client-side implementation is much simpler.

    1. Write a StartTLS request,
    2. wait for the StartTLS response,
    3. create a new SslHandler instance with startTls flag set to false,
    4. insert the SslHandler to the ChannelPipeline, and
    5. Initiate SSL handshake by calling handshake().

    Known issues

    Because of a known issue with the current implementation of the SslEngine that comes with Java it may be possible that you see blocked IO-Threads while a full GC is done.

    So if you are affected you can workaround this problem by adjust the cache settings like shown below:

         SslContext context = ...;
         context.getServerSessionContext().setSessionCacheSize(someSaneSize);
         context.getServerSessionContext().setSessionTime(someSameTimeout);
     

    What values to use here depends on the nature of your application and should be set based on monitoring and debugging of it. For more details see #832 in our issue tracker.

    • Field Detail

      • EMPTY_BUFFER

        private static final java.nio.ByteBuffer EMPTY_BUFFER
      • IGNORABLE_CLASS_IN_STACK

        private static final java.util.regex.Pattern IGNORABLE_CLASS_IN_STACK
      • IGNORABLE_ERROR_MESSAGE

        private static final java.util.regex.Pattern IGNORABLE_ERROR_MESSAGE
      • defaultBufferPool

        private static SslBufferPool defaultBufferPool
      • engine

        private final javax.net.ssl.SSLEngine engine
      • startTls

        private final boolean startTls
      • enableRenegotiation

        private volatile boolean enableRenegotiation
      • handshakeLock

        final java.lang.Object handshakeLock
      • handshaking

        private boolean handshaking
      • handshaken

        private volatile boolean handshaken
      • handshakeFuture

        private volatile ChannelFuture handshakeFuture
      • sentFirstMessage

        private volatile int sentFirstMessage
      • sentCloseNotify

        private volatile int sentCloseNotify
      • closedOutboundAndChannel

        private volatile int closedOutboundAndChannel
      • SENT_FIRST_MESSAGE_UPDATER

        private static final java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> SENT_FIRST_MESSAGE_UPDATER
      • SENT_CLOSE_NOTIFY_UPDATER

        private static final java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> SENT_CLOSE_NOTIFY_UPDATER
      • CLOSED_OUTBOUND_AND_CHANNEL_UPDATER

        private static final java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> CLOSED_OUTBOUND_AND_CHANNEL_UPDATER
      • ignoreClosedChannelException

        int ignoreClosedChannelException
      • ignoreClosedChannelExceptionLock

        final java.lang.Object ignoreClosedChannelExceptionLock
      • pendingUnencryptedWritesLock

        private final NonReentrantLock pendingUnencryptedWritesLock
      • pendingEncryptedWrites

        private final java.util.Queue<MessageEvent> pendingEncryptedWrites
      • pendingEncryptedWritesLock

        private final NonReentrantLock pendingEncryptedWritesLock
      • issueHandshake

        private volatile boolean issueHandshake
      • writeBeforeHandshakeDone

        private volatile boolean writeBeforeHandshakeDone
      • closeOnSslException

        private boolean closeOnSslException
      • packetLength

        private int packetLength
      • timer

        private final Timer timer
      • handshakeTimeoutInMillis

        private final long handshakeTimeoutInMillis
      • handshakeTimeout

        private Timeout handshakeTimeout
    • Constructor Detail

      • SslHandler

        public SslHandler​(javax.net.ssl.SSLEngine engine)
        Creates a new instance.
        Parameters:
        engine - the SSLEngine this handler will use
      • SslHandler

        public SslHandler​(javax.net.ssl.SSLEngine engine,
                          SslBufferPool bufferPool)
        Creates a new instance.
        Parameters:
        engine - the SSLEngine this handler will use
        bufferPool - the SslBufferPool where this handler will acquire the buffers required by the SSLEngine
      • SslHandler

        public SslHandler​(javax.net.ssl.SSLEngine engine,
                          boolean startTls)
        Creates a new instance.
        Parameters:
        engine - the SSLEngine this handler will use
        startTls - true if the first write request shouldn't be encrypted by the SSLEngine
      • SslHandler

        public SslHandler​(javax.net.ssl.SSLEngine engine,
                          SslBufferPool bufferPool,
                          boolean startTls)
        Creates a new instance.
        Parameters:
        engine - the SSLEngine this handler will use
        bufferPool - the SslBufferPool where this handler will acquire the buffers required by the SSLEngine
        startTls - true if the first write request shouldn't be encrypted by the SSLEngine
      • SslHandler

        public SslHandler​(javax.net.ssl.SSLEngine engine,
                          SslBufferPool bufferPool,
                          boolean startTls,
                          Timer timer,
                          long handshakeTimeoutInMillis)
        Creates a new instance.
        Parameters:
        engine - the SSLEngine this handler will use
        bufferPool - the SslBufferPool where this handler will acquire the buffers required by the SSLEngine
        startTls - true if the first write request shouldn't be encrypted by the SSLEngine
        timer - the Timer which will be used to process the timeout of the handshake(). Be aware that the given Timer will not get stopped automaticly, so it is up to you to cleanup once you not need it anymore
        handshakeTimeoutInMillis - the time in milliseconds after whic the handshake() will be failed, and so the future notified
    • Method Detail

      • getDefaultBufferPool

        public static SslBufferPool getDefaultBufferPool()
        Returns the default SslBufferPool used when no pool is specified in the constructor.
      • getEngine

        public javax.net.ssl.SSLEngine getEngine()
        Returns the SSLEngine which is used by this handler.
      • handshake

        public ChannelFuture handshake()
        Starts an SSL / TLS handshake for the specified channel.
        Returns:
        a ChannelFuture which is notified when the handshake succeeds or fails.
      • close

        public ChannelFuture close()
        Sends an SSL close_notify message to the specified channel and destroys the underlying SSLEngine.
      • isEnableRenegotiation

        public boolean isEnableRenegotiation()
        Returns true if and only if TLS renegotiation is enabled.
      • setEnableRenegotiation

        public void setEnableRenegotiation​(boolean enableRenegotiation)
        Enables or disables TLS renegotiation.
      • setIssueHandshake

        public void setIssueHandshake​(boolean issueHandshake)
        Enables or disables the automatic handshake once the Channel is connected. The value will only have affect if its set before the Channel is connected.
      • isIssueHandshake

        public boolean isIssueHandshake()
        Returns true if the automatic handshake is enabled
      • getSSLEngineInboundCloseFuture

        public ChannelFuture getSSLEngineInboundCloseFuture()
        Return the ChannelFuture that will get notified if the inbound of the SSLEngine will get closed. This method will return the same ChannelFuture all the time. For more informations see the apidocs of SSLEngine
      • getHandshakeTimeout

        public long getHandshakeTimeout()
        Return the timeout (in ms) after which the ChannelFuture of handshake() will be failed, while a handshake is in progress
      • setCloseOnSSLException

        public void setCloseOnSSLException​(boolean closeOnSslException)
        If set to true, the Channel will automatically get closed one a SSLException was caught. This is most times what you want, as after this its almost impossible to recover. Anyway the default is false to not break compatibility with older releases. This will be changed to true in the next major release.
      • getCloseOnSSLException

        public boolean getCloseOnSSLException()
      • cancelHandshakeTimeout

        private void cancelHandshakeTimeout()
      • closeEngine

        private void closeEngine()
      • ignoreException

        private boolean ignoreException​(java.lang.Throwable t)
        Checks if the given Throwable can be ignore and just "swallowed" When an ssl connection is closed a close_notify message is sent. After that the peer also sends close_notify however, it's not mandatory to receive the close_notify. The party who sent the initial close_notify can close the connection immediately then the peer will get connection reset error.
      • isEncrypted

        public static boolean isEncrypted​(ChannelBuffer buffer)
        Returns true if the given ChannelBuffer is encrypted. Be aware that this method will not increase the readerIndex of the given ChannelBuffer.
        Parameters:
        buffer - The ChannelBuffer to read from. Be aware that it must have at least 5 bytes to read, otherwise it will throw an IllegalArgumentException.
        Returns:
        encrypted true if the ChannelBuffer is encrypted, false otherwise.
        Throws:
        java.lang.IllegalArgumentException - Is thrown if the given ChannelBuffer has not at least 5 bytes to read.
      • getEncryptedPacketLength

        private static int getEncryptedPacketLength​(ChannelBuffer buffer,
                                                    int offset)
        Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase the readerIndex of the given ChannelBuffer.
        Parameters:
        buffer - The ChannelBuffer to read from. Be aware that it must have at least 5 bytes to read, otherwise it will throw an IllegalArgumentException.
        Returns:
        length The length of the encrypted packet that is included in the buffer. This will return -1 if the given ChannelBuffer is not encrypted at all.
        Throws:
        java.lang.IllegalArgumentException - Is thrown if the given ChannelBuffer has not at least 5 bytes to read.
      • decode

        protected java.lang.Object decode​(ChannelHandlerContext ctx,
                                          Channel channel,
                                          ChannelBuffer in)
                                   throws java.lang.Exception
        Description copied from class: FrameDecoder
        Decodes the received packets so far into a frame. If an sub-class wants to extract a frame out of the buffer it should use the FrameDecoder.extractFrame(ChannelBuffer, int, int) method, to make optimizations easier later.
        Specified by:
        decode in class FrameDecoder
        Parameters:
        ctx - the context of this handler
        channel - the current channel
        in - the cumulative buffer of received packets so far. Note that the buffer might be empty, which means you should not make an assumption that the buffer contains at least one byte in your decoder implementation.
        Returns:
        the decoded frame if a full frame was received and decoded. null if there's not enough data in the buffer to decode a frame.
        Throws:
        java.lang.Exception
      • getShort

        private static short getShort​(ChannelBuffer buf,
                                      int offset)
        Reads a big-endian short integer from the buffer. Please note that we do not use ChannelBuffer.getShort(int) because it might be a little-endian buffer.
      • wrap

        private void wrap​(ChannelHandlerContext context,
                          Channel channel)
                   throws javax.net.ssl.SSLException
        Throws:
        javax.net.ssl.SSLException
      • offerEncryptedWriteRequest

        private void offerEncryptedWriteRequest​(MessageEvent encryptedWrite)
      • unwrapNonAppData

        private void unwrapNonAppData​(ChannelHandlerContext ctx,
                                      Channel channel,
                                      boolean mightNeedHandshake)
                               throws javax.net.ssl.SSLException
        Calls SSLEngine.unwrap(ByteBuffer, ByteBuffer) with an empty buffer to handle handshakes, etc.
        Throws:
        javax.net.ssl.SSLException
      • unwrap

        private ChannelBuffer unwrap​(ChannelHandlerContext ctx,
                                     Channel channel,
                                     java.nio.ByteBuffer nioInNetBuf,
                                     int initialNettyOutAppBufCapacity,
                                     boolean mightNeedHandshake)
                              throws javax.net.ssl.SSLException
        Unwraps inbound SSL records.
        Throws:
        javax.net.ssl.SSLException
      • handleRenegotiation

        private void handleRenegotiation​(javax.net.ssl.SSLEngineResult.HandshakeStatus handshakeStatus)
      • runDelegatedTasks

        private void runDelegatedTasks()
        Fetches all delegated tasks from the SSLEngine and runs them immediately.
      • setHandshakeSuccessIfStillHandshaking

        private boolean setHandshakeSuccessIfStillHandshaking​(Channel channel)
        Works around some Android SSLEngine implementations that skip SSLEngineResult.HandshakeStatus.FINISHED and go straight into SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING when handshake is finished.
        Returns:
        true if and only if the workaround has been applied and thus handshakeFuture has been marked as success by this method
      • setHandshakeSuccess

        private void setHandshakeSuccess​(Channel channel)
      • setHandshakeFailure

        private void setHandshakeFailure​(Channel channel,
                                         javax.net.ssl.SSLException cause)