Class SSLSocketChannel

  • All Implemented Interfaces:
    java.io.Closeable, java.lang.AutoCloseable, java.nio.channels.ByteChannel, java.nio.channels.Channel, java.nio.channels.ReadableByteChannel, java.nio.channels.WritableByteChannel, ISSLChannel, WrappedByteChannel

    public class SSLSocketChannel
    extends java.lang.Object
    implements WrappedByteChannel, java.nio.channels.ByteChannel, ISSLChannel
    A class that represents an SSL/TLS peer, and can be extended to create a client or a server. It makes use of the JSSE framework, and specifically the SSLEngine logic, which is described by Oracle as "an advanced API, not appropriate for casual use", since it requires the user to implement much of the communication establishment procedure himself. More information about it can be found here: http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SSLEngine SSLSocketChannel implements the handshake protocol, required to establish a connection between two peers, which is common for both client and server and provides the abstract read(ByteBuffer) and write(ByteBuffer) (String)} methods, that need to be implemented by the specific SSL/TLS peer that is going to extend this class.
    • Field Summary

      Fields 
      Modifier and Type Field Description
      private javax.net.ssl.SSLEngine engine
      The engine which will be used for un-/wrapping of buffers
      private java.util.concurrent.ExecutorService executor
      Will be used to execute tasks that may emerge during handshake in parallel with the server's main thread.
      private org.slf4j.Logger log
      Logger instance
      private java.nio.ByteBuffer myAppData
      Will contain this peer's application data in plaintext, that will be later encrypted using SSLEngine.wrap(ByteBuffer, ByteBuffer) and sent to the other peer.
      private java.nio.ByteBuffer myNetData
      Will contain this peer's encrypted data, that will be generated after SSLEngine.wrap(ByteBuffer, ByteBuffer) is applied on myAppData.
      private java.nio.ByteBuffer peerAppData
      Will contain the other peer's (decrypted) application data.
      private java.nio.ByteBuffer peerNetData
      Will contain the other peer's encrypted data.
      private java.nio.channels.SocketChannel socketChannel
      The underlying socket channel
    • Constructor Summary

      Constructors 
      Constructor Description
      SSLSocketChannel​(java.nio.channels.SocketChannel inputSocketChannel, javax.net.ssl.SSLEngine inputEngine, java.util.concurrent.ExecutorService inputExecutor, java.nio.channels.SelectionKey key)  
    • Method Summary

      All Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      void close()  
      private void closeConnection()
      This method should be called when this peer wants to explicitly close the connection or when a close message has arrived from the other peer, in order to provide an orderly shutdown.
      private boolean doHandshake()
      Implements the handshake protocol between two peers, required for the establishment of the SSL/TLS connection.
      private java.nio.ByteBuffer enlargeApplicationBuffer​(java.nio.ByteBuffer buffer)
      Enlarging a packet buffer (peerAppData or myAppData)
      private java.nio.ByteBuffer enlargeBuffer​(java.nio.ByteBuffer buffer, int sessionProposedCapacity)
      Compares sessionProposedCapacity with buffer's capacity.
      private java.nio.ByteBuffer enlargePacketBuffer​(java.nio.ByteBuffer buffer)
      Enlarging a packet buffer (peerNetData or myNetData)
      javax.net.ssl.SSLEngine getSSLEngine()
      Get the ssl engine used for the de- and encryption of the communication.
      private java.nio.ByteBuffer handleBufferUnderflow​(java.nio.ByteBuffer buffer)
      Handles SSLEngineResult.Status.BUFFER_UNDERFLOW.
      private void handleEndOfStream()
      In addition to orderly shutdowns, an unorderly shutdown may occur, when the transport link (socket channel) is severed before close messages are exchanged.
      boolean isBlocking()
      This function returns the blocking state of the channel
      boolean isNeedRead()
      returns whether readMore should be called to fetch data which has been decoded but not yet been returned.
      boolean isNeedWrite()
      returns whether writeMore should be called write additional data.
      boolean isOpen()  
      int read​(java.nio.ByteBuffer dst)  
      int readMore​(java.nio.ByteBuffer dst)
      This function does not read data from the underlying channel at all.
      int write​(java.nio.ByteBuffer output)  
      void writeMore()
      Gets called when WrappedByteChannel.isNeedWrite() ()} requires a additional rite
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • log

        private final org.slf4j.Logger log
        Logger instance
        Since:
        1.4.0
      • socketChannel

        private final java.nio.channels.SocketChannel socketChannel
        The underlying socket channel
      • engine

        private final javax.net.ssl.SSLEngine engine
        The engine which will be used for un-/wrapping of buffers
      • myAppData

        private java.nio.ByteBuffer myAppData
        Will contain this peer's application data in plaintext, that will be later encrypted using SSLEngine.wrap(ByteBuffer, ByteBuffer) and sent to the other peer. This buffer can typically be of any size, as long as it is large enough to contain this peer's outgoing messages. If this peer tries to send a message bigger than buffer's capacity a BufferOverflowException will be thrown.
      • myNetData

        private java.nio.ByteBuffer myNetData
        Will contain this peer's encrypted data, that will be generated after SSLEngine.wrap(ByteBuffer, ByteBuffer) is applied on myAppData. It should be initialized using SSLSession.getPacketBufferSize(), which returns the size up to which, SSL/TLS packets will be generated from the engine under a session. All SSLEngine network buffers should be sized at least this large to avoid insufficient space problems when performing wrap and unwrap calls.
      • peerAppData

        private java.nio.ByteBuffer peerAppData
        Will contain the other peer's (decrypted) application data. It must be large enough to hold the application data from any peer. Can be initialized with SSLSession.getApplicationBufferSize() for an estimation of the other peer's application data and should be enlarged if this size is not enough.
      • peerNetData

        private java.nio.ByteBuffer peerNetData
        Will contain the other peer's encrypted data. The SSL/TLS protocols specify that implementations should produce packets containing at most 16 KB of plaintext, so a buffer sized to this value should normally cause no capacity problems. However, some implementations violate the specification and generate large records up to 32 KB. If the SSLEngine.unwrap(ByteBuffer, ByteBuffer) detects large inbound packets, the buffer sizes returned by SSLSession will be updated dynamically, so the this peer should check for overflow conditions and enlarge the buffer using the session's (updated) buffer size.
      • executor

        private java.util.concurrent.ExecutorService executor
        Will be used to execute tasks that may emerge during handshake in parallel with the server's main thread.
    • Constructor Detail

      • SSLSocketChannel

        public SSLSocketChannel​(java.nio.channels.SocketChannel inputSocketChannel,
                                javax.net.ssl.SSLEngine inputEngine,
                                java.util.concurrent.ExecutorService inputExecutor,
                                java.nio.channels.SelectionKey key)
                         throws java.io.IOException
        Throws:
        java.io.IOException
    • Method Detail

      • read

        public int read​(java.nio.ByteBuffer dst)
                 throws java.io.IOException
        Specified by:
        read in interface java.nio.channels.ReadableByteChannel
        Throws:
        java.io.IOException
      • write

        public int write​(java.nio.ByteBuffer output)
                  throws java.io.IOException
        Specified by:
        write in interface java.nio.channels.WritableByteChannel
        Throws:
        java.io.IOException
      • doHandshake

        private boolean doHandshake()
                             throws java.io.IOException
        Implements the handshake protocol between two peers, required for the establishment of the SSL/TLS connection. During the handshake, encryption configuration information - such as the list of available cipher suites - will be exchanged and if the handshake is successful will lead to an established SSL/TLS session.

        A typical handshake will usually contain the following steps:

        • 1. wrap: ClientHello
        • 2. unwrap: ServerHello/Cert/ServerHelloDone
        • 3. wrap: ClientKeyExchange
        • 4. wrap: ChangeCipherSpec
        • 5. wrap: Finished
        • 6. unwrap: ChangeCipherSpec
        • 7. unwrap: Finished

        Handshake is also used during the end of the session, in order to properly close the connection between the two peers. A proper connection close will typically include the one peer sending a CLOSE message to another, and then wait for the other's CLOSE message to close the transport link. The other peer from his perspective would read a CLOSE message from his peer and then enter the handshake procedure to send his own CLOSE message as well.

        Returns:
        True if the connection handshake was successful or false if an error occurred.
        Throws:
        java.io.IOException - - if an error occurs during read/write to the socket channel.
      • enlargePacketBuffer

        private java.nio.ByteBuffer enlargePacketBuffer​(java.nio.ByteBuffer buffer)
        Enlarging a packet buffer (peerNetData or myNetData)
        Parameters:
        buffer - the buffer to enlarge
        Returns:
        the enlarged buffer
      • enlargeApplicationBuffer

        private java.nio.ByteBuffer enlargeApplicationBuffer​(java.nio.ByteBuffer buffer)
        Enlarging a packet buffer (peerAppData or myAppData)
        Parameters:
        buffer - the buffer to enlarge
        Returns:
        the enlarged buffer
      • enlargeBuffer

        private java.nio.ByteBuffer enlargeBuffer​(java.nio.ByteBuffer buffer,
                                                  int sessionProposedCapacity)
        Compares sessionProposedCapacity with buffer's capacity. If buffer's capacity is smaller, returns a buffer with the proposed capacity. If it's equal or larger, returns a buffer with capacity twice the size of the initial one.
        Parameters:
        buffer - - the buffer to be enlarged.
        sessionProposedCapacity - - the minimum size of the new buffer, proposed by SSLSession.
        Returns:
        A new buffer with a larger capacity.
      • handleBufferUnderflow

        private java.nio.ByteBuffer handleBufferUnderflow​(java.nio.ByteBuffer buffer)
        Handles SSLEngineResult.Status.BUFFER_UNDERFLOW. Will check if the buffer is already filled, and if there is no space problem will return the same buffer, so the client tries to read again. If the buffer is already filled will try to enlarge the buffer either to session's proposed size or to a larger capacity. A buffer underflow can happen only after an unwrap, so the buffer will always be a peerNetData buffer.
        Parameters:
        buffer - - will always be peerNetData buffer.
        Returns:
        The same buffer if there is no space problem or a new buffer with the same data but more space.
      • closeConnection

        private void closeConnection()
                              throws java.io.IOException
        This method should be called when this peer wants to explicitly close the connection or when a close message has arrived from the other peer, in order to provide an orderly shutdown.

        It first calls SSLEngine.closeOutbound() which prepares this peer to send its own close message and sets SSLEngine to the NEED_WRAP state. Then, it delegates the exchange of close messages to the handshake method and finally, it closes socket channel.

        Throws:
        java.io.IOException - if an I/O error occurs to the socket channel.
      • handleEndOfStream

        private void handleEndOfStream()
                                throws java.io.IOException
        In addition to orderly shutdowns, an unorderly shutdown may occur, when the transport link (socket channel) is severed before close messages are exchanged. This may happen by getting an -1 or IOException when trying to read from the socket channel, or an IOException when trying to write to it. In both cases SSLEngine.closeInbound() should be called and then try to follow the standard procedure.
        Throws:
        java.io.IOException - if an I/O error occurs to the socket channel.
      • isNeedWrite

        public boolean isNeedWrite()
        Description copied from interface: WrappedByteChannel
        returns whether writeMore should be called write additional data.
        Specified by:
        isNeedWrite in interface WrappedByteChannel
        Returns:
        is a additional write needed
      • readMore

        public int readMore​(java.nio.ByteBuffer dst)
                     throws java.io.IOException
        Description copied from interface: WrappedByteChannel
        This function does not read data from the underlying channel at all. It is just a way to fetch data which has already be received or decoded but was but was not yet returned to the user. This could be the case when the decoded data did not fit into the buffer the user passed to ReadableByteChannel.read(ByteBuffer).
        Specified by:
        readMore in interface WrappedByteChannel
        Parameters:
        dst - the destiny of the read
        Returns:
        the amount of remaining data
        Throws:
        java.io.IOException - when a error occurred during unwrapping
      • isBlocking

        public boolean isBlocking()
        Description copied from interface: WrappedByteChannel
        This function returns the blocking state of the channel
        Specified by:
        isBlocking in interface WrappedByteChannel
        Returns:
        is the channel blocking
      • isOpen

        public boolean isOpen()
        Specified by:
        isOpen in interface java.nio.channels.Channel
      • close

        public void close()
                   throws java.io.IOException
        Specified by:
        close in interface java.lang.AutoCloseable
        Specified by:
        close in interface java.nio.channels.Channel
        Specified by:
        close in interface java.io.Closeable
        Throws:
        java.io.IOException
      • getSSLEngine

        public javax.net.ssl.SSLEngine getSSLEngine()
        Description copied from interface: ISSLChannel
        Get the ssl engine used for the de- and encryption of the communication.
        Specified by:
        getSSLEngine in interface ISSLChannel
        Returns:
        the ssl engine of this channel