Class SSLSocketChannel

java.lang.Object
org.java_websocket.SSLSocketChannel
All Implemented Interfaces:
Closeable, AutoCloseable, ByteChannel, Channel, ReadableByteChannel, WritableByteChannel, ISSLChannel, WrappedByteChannel

public class SSLSocketChannel extends Object implements WrappedByteChannel, 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 Details

    • log

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

      private final SocketChannel socketChannel
      The underlying socket channel
    • engine

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

      private 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 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 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 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 ExecutorService executor
      Will be used to execute tasks that may emerge during handshake in parallel with the server's main thread.
  • Constructor Details

  • Method Details

    • read

      public int read(ByteBuffer dst) throws IOException
      Specified by:
      read in interface ReadableByteChannel
      Throws:
      IOException
    • write

      public int write(ByteBuffer output) throws IOException
      Specified by:
      write in interface WritableByteChannel
      Throws:
      IOException
    • doHandshake

      private boolean doHandshake() throws 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:
      IOException - - if an error occurs during read/write to the socket channel.
    • enlargePacketBuffer

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

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

      private ByteBuffer enlargeBuffer(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 ByteBuffer handleBufferUnderflow(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 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:
      IOException - if an I/O error occurs to the socket channel.
    • handleEndOfStream

      private void handleEndOfStream() throws 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:
      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
    • writeMore

      public void writeMore() throws IOException
      Description copied from interface: WrappedByteChannel
      Gets called when WrappedByteChannel.isNeedWrite() ()} requires a additional rite
      Specified by:
      writeMore in interface WrappedByteChannel
      Throws:
      IOException - may be thrown due to an error while writing
    • isNeedRead

      public boolean isNeedRead()
      Description copied from interface: WrappedByteChannel
      returns whether readMore should be called to fetch data which has been decoded but not yet been returned.
      Specified by:
      isNeedRead in interface WrappedByteChannel
      Returns:
      is a additional read needed
      See Also:
    • readMore

      public int readMore(ByteBuffer dst) throws 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:
      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 Channel
    • close

      public void close() throws IOException
      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Channel
      Specified by:
      close in interface Closeable
      Throws:
      IOException
    • getSSLEngine

      public 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