Class SslHandler
- java.lang.Object
-
- io.netty.channel.ChannelHandlerAdapter
-
- io.netty.channel.ChannelInboundHandlerAdapter
-
- io.netty.handler.codec.ByteToMessageDecoder
-
- io.netty.handler.ssl.SslHandler
-
- All Implemented Interfaces:
ChannelHandler
,ChannelInboundHandler
,ChannelOutboundHandler
public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundHandler
Adds SSL · TLS and StartTLS support to aChannel
. Please refer to the "SecureChat" example in the distribution or the web site for the detailed usage.Beginning the handshake
Beside using the handshake
ChannelFuture
to get notified about the completion of the handshake it's also possible to detect it by implement theChannelInboundHandler.userEventTriggered(ChannelHandlerContext, Object)
method and check for aSslHandshakeCompletionEvent
.Handshake
The handshake will be automatically issued for you once the
Channel
is active andSSLEngine.getUseClientMode()
returnstrue
. So no need to bother with it by your self.Closing the session
To close the SSL session, the
closeOutbound()
method should be called to send theclose_notify
message to the remote peer. One exception is when you close theChannel
-SslHandler
intercepts the close request and send theclose_notify
message before the channel closure automatically. Once the SSL session is closed, it is not reusable, and consequently you should create a newSslHandler
with a newSSLEngine
as explained in the following section.Restarting the session
To restart the SSL session, you must remove the existing closed
SslHandler
from theChannelPipeline
, insert a newSslHandler
with a newSSLEngine
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:
- Client sends a StartTLS request to server.
- Server sends a StartTLS response to client.
- Client begins SSL handshake.
- create a new
SslHandler
instance withstartTls
flag set totrue
, - insert the
SslHandler
to theChannelPipeline
, and - write a StartTLS response.
SslHandler
before sending the StartTLS response. Otherwise the client can send begin SSL handshake beforeSslHandler
is inserted to theChannelPipeline
, causing data corruption.The client-side implementation is much simpler.
- Write a StartTLS request,
- wait for the StartTLS response,
- create a new
SslHandler
instance withstartTls
flag set tofalse
, - insert the
SslHandler
to theChannelPipeline
, and - Initiate SSL 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.
-
-
Nested Class Summary
-
Nested classes/interfaces inherited from class io.netty.handler.codec.ByteToMessageDecoder
ByteToMessageDecoder.Cumulator
-
Nested classes/interfaces inherited from interface io.netty.channel.ChannelHandler
ChannelHandler.Sharable
-
-
Field Summary
-
Fields inherited from class io.netty.handler.codec.ByteToMessageDecoder
COMPOSITE_CUMULATOR, MERGE_CUMULATOR
-
-
Constructor Summary
Constructors Constructor Description SslHandler(javax.net.ssl.SSLEngine engine)
Creates a new instance which runs all delegated tasks directly on theEventExecutor
.SslHandler(javax.net.ssl.SSLEngine engine, boolean startTls)
Creates a new instance which runs all delegated tasks directly on theEventExecutor
.SslHandler(javax.net.ssl.SSLEngine engine, boolean startTls, java.util.concurrent.Executor delegatedTaskExecutor)
Creates a new instance.SslHandler(javax.net.ssl.SSLEngine engine, java.util.concurrent.Executor delegatedTaskExecutor)
Creates a new instance.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Deprecated Methods Modifier and Type Method Description java.lang.String
applicationProtocol()
Returns the name of the current application-level protocol.void
bind(ChannelHandlerContext ctx, java.net.SocketAddress localAddress, ChannelPromise promise)
Called once a bind operation is made.void
channelActive(ChannelHandlerContext ctx)
Issues an initial TLS handshake once connected when used in client-modevoid
channelInactive(ChannelHandlerContext ctx)
CallsChannelHandlerContext.fireChannelInactive()
to forward to the nextChannelInboundHandler
in theChannelPipeline
.void
channelReadComplete(ChannelHandlerContext ctx)
CallsChannelHandlerContext.fireChannelReadComplete()
to forward to the nextChannelInboundHandler
in theChannelPipeline
.ChannelFuture
close()
Deprecated.void
close(ChannelHandlerContext ctx, ChannelPromise promise)
Called once a close operation is made.ChannelFuture
close(ChannelPromise promise)
Deprecated.ChannelFuture
closeOutbound()
Sends an SSLclose_notify
message to the specified channel and destroys the underlyingSSLEngine
.ChannelFuture
closeOutbound(ChannelPromise promise)
Sends an SSLclose_notify
message to the specified channel and destroys the underlyingSSLEngine
.void
connect(ChannelHandlerContext ctx, java.net.SocketAddress remoteAddress, java.net.SocketAddress localAddress, ChannelPromise promise)
Called once a connect operation is made.protected void
decode(ChannelHandlerContext ctx, ByteBuf in, java.util.List<java.lang.Object> out)
Decode the from oneByteBuf
to an other.void
deregister(ChannelHandlerContext ctx, ChannelPromise promise)
Called once a deregister operation is made from the current registeredEventLoop
.void
disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
Called once a disconnect operation is made.javax.net.ssl.SSLEngine
engine()
Returns theSSLEngine
which is used by this handler.void
exceptionCaught(ChannelHandlerContext ctx, java.lang.Throwable cause)
CallsChannelHandlerContext.fireExceptionCaught(Throwable)
to forward to the nextChannelHandler
in theChannelPipeline
.void
flush(ChannelHandlerContext ctx)
Called once a flush operation is made.long
getCloseNotifyFlushTimeoutMillis()
Gets the timeout for flushing the close_notify that was triggered by closing theChannel
.long
getCloseNotifyReadTimeoutMillis()
Gets the timeout (in ms) for receiving the response for the close_notify that was triggered by closing theChannel
.long
getCloseNotifyTimeoutMillis()
Deprecated.long
getHandshakeTimeoutMillis()
void
handlerAdded(ChannelHandlerContext ctx)
Do nothing by default, sub-classes may override this method.void
handlerRemoved0(ChannelHandlerContext ctx)
Gets called after theByteToMessageDecoder
was removed from the actual context and it doesn't handle events anymore.Future<Channel>
handshakeFuture()
Returns aFuture
that will get notified once the current TLS handshake completes.static boolean
isEncrypted(ByteBuf buffer)
Deprecated.static boolean
isEncrypted(ByteBuf buffer, boolean probeSSLv2)
Returnstrue
if the givenByteBuf
is encrypted.void
read(ChannelHandlerContext ctx)
InterceptsChannelHandlerContext.read()
.Future<Channel>
renegotiate()
Performs TLS renegotiation.Future<Channel>
renegotiate(Promise<Channel> promise)
Performs TLS renegotiation.void
setCloseNotifyFlushTimeout(long closeNotifyFlushTimeout, java.util.concurrent.TimeUnit unit)
Sets the timeout for flushing the close_notify that was triggered by closing theChannel
.void
setCloseNotifyFlushTimeoutMillis(long closeNotifyFlushTimeoutMillis)
void
setCloseNotifyReadTimeout(long closeNotifyReadTimeout, java.util.concurrent.TimeUnit unit)
Sets the timeout for receiving the response for the close_notify that was triggered by closing theChannel
.void
setCloseNotifyReadTimeoutMillis(long closeNotifyReadTimeoutMillis)
void
setCloseNotifyTimeout(long closeNotifyTimeout, java.util.concurrent.TimeUnit unit)
Deprecated.void
setCloseNotifyTimeoutMillis(long closeNotifyFlushTimeoutMillis)
Deprecated.void
setHandshakeTimeout(long handshakeTimeout, java.util.concurrent.TimeUnit unit)
void
setHandshakeTimeoutMillis(long handshakeTimeoutMillis)
void
setWrapDataSize(int wrapDataSize)
Sets the number of bytes to pass to eachSSLEngine.wrap(ByteBuffer[], int, int, ByteBuffer)
call.Future<Channel>
sslCloseFuture()
Return theFuture
that will get notified if the inbound of theSSLEngine
is closed.void
write(ChannelHandlerContext ctx, java.lang.Object msg, ChannelPromise promise)
Called once a write operation is made.-
Methods inherited from class io.netty.handler.codec.ByteToMessageDecoder
actualReadableBytes, callDecode, channelRead, decodeLast, discardSomeReadBytes, handlerRemoved, internalBuffer, isSingleDecode, setCumulator, setDiscardAfterReads, setSingleDecode, userEventTriggered
-
Methods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelRegistered, channelUnregistered, channelWritabilityChanged
-
Methods inherited from class io.netty.channel.ChannelHandlerAdapter
ensureNotSharable, isSharable
-
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
-
Methods inherited from interface io.netty.channel.ChannelHandler
handlerRemoved
-
-
-
-
Constructor Detail
-
SslHandler
public SslHandler(javax.net.ssl.SSLEngine engine)
Creates a new instance which runs all delegated tasks directly on theEventExecutor
.- Parameters:
engine
- theSSLEngine
this handler will use
-
SslHandler
public SslHandler(javax.net.ssl.SSLEngine engine, boolean startTls)
Creates a new instance which runs all delegated tasks directly on theEventExecutor
.- Parameters:
engine
- theSSLEngine
this handler will usestartTls
-true
if the first write request shouldn't be encrypted by theSSLEngine
-
SslHandler
public SslHandler(javax.net.ssl.SSLEngine engine, java.util.concurrent.Executor delegatedTaskExecutor)
Creates a new instance.- Parameters:
engine
- theSSLEngine
this handler will usedelegatedTaskExecutor
- theExecutor
that will be used to execute tasks that are returned bySSLEngine.getDelegatedTask()
.
-
SslHandler
public SslHandler(javax.net.ssl.SSLEngine engine, boolean startTls, java.util.concurrent.Executor delegatedTaskExecutor)
Creates a new instance.- Parameters:
engine
- theSSLEngine
this handler will usestartTls
-true
if the first write request shouldn't be encrypted by theSSLEngine
delegatedTaskExecutor
- theExecutor
that will be used to execute tasks that are returned bySSLEngine.getDelegatedTask()
.
-
-
Method Detail
-
getHandshakeTimeoutMillis
public long getHandshakeTimeoutMillis()
-
setHandshakeTimeout
public void setHandshakeTimeout(long handshakeTimeout, java.util.concurrent.TimeUnit unit)
-
setHandshakeTimeoutMillis
public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis)
-
setWrapDataSize
@UnstableApi public final void setWrapDataSize(int wrapDataSize)
Sets the number of bytes to pass to eachSSLEngine.wrap(ByteBuffer[], int, int, ByteBuffer)
call.This value will partition data which is passed to write
write(ChannelHandlerContext, Object, ChannelPromise)
. The partitioning will work as follows:- If
wrapDataSize <= 0
then we will write each data chunk as is. - If
wrapDataSize > data size
then we will attempt to aggregate multiple data chunks together. - If
wrapDataSize > data size
Else ifwrapDataSize <= data size
then we will divide the data into chunks ofwrapDataSize
when writing.
If the
SSLEngine
doesn't support a gather wrap operation (e.g.SslProvider.OPENSSL
) then aggregating data before wrapping can help reduce the ratio between TLS overhead vs data payload which will lead to better goodput. Writing fixed chunks of data can also help target the underlying transport's (e.g. TCP) frame size. Under lossy/congested network conditions this may help the peer get full TLS packets earlier and be able to do work sooner, as opposed to waiting for the all the pieces of the TLS packet to arrive.- Parameters:
wrapDataSize
- the number of bytes which will be passed to eachSSLEngine.wrap(ByteBuffer[], int, int, ByteBuffer)
call.
- If
-
getCloseNotifyTimeoutMillis
@Deprecated public long getCloseNotifyTimeoutMillis()
Deprecated.
-
setCloseNotifyTimeout
@Deprecated public void setCloseNotifyTimeout(long closeNotifyTimeout, java.util.concurrent.TimeUnit unit)
Deprecated.
-
setCloseNotifyTimeoutMillis
@Deprecated public void setCloseNotifyTimeoutMillis(long closeNotifyFlushTimeoutMillis)
Deprecated.
-
getCloseNotifyFlushTimeoutMillis
public final long getCloseNotifyFlushTimeoutMillis()
-
setCloseNotifyFlushTimeout
public final void setCloseNotifyFlushTimeout(long closeNotifyFlushTimeout, java.util.concurrent.TimeUnit unit)
-
setCloseNotifyFlushTimeoutMillis
public final void setCloseNotifyFlushTimeoutMillis(long closeNotifyFlushTimeoutMillis)
-
getCloseNotifyReadTimeoutMillis
public final long getCloseNotifyReadTimeoutMillis()
-
setCloseNotifyReadTimeout
public final void setCloseNotifyReadTimeout(long closeNotifyReadTimeout, java.util.concurrent.TimeUnit unit)
-
setCloseNotifyReadTimeoutMillis
public final void setCloseNotifyReadTimeoutMillis(long closeNotifyReadTimeoutMillis)
-
engine
public javax.net.ssl.SSLEngine engine()
Returns theSSLEngine
which is used by this handler.
-
applicationProtocol
public java.lang.String applicationProtocol()
Returns the name of the current application-level protocol.- Returns:
- the protocol name or
null
if application-level protocol has not been negotiated
-
handshakeFuture
public Future<Channel> handshakeFuture()
Returns aFuture
that will get notified once the current TLS handshake completes.- Returns:
- the
Future
for the initial TLS handshake ifrenegotiate()
was not invoked. TheFuture
for the most recent TLS renegotiation otherwise.
-
close
@Deprecated public ChannelFuture close()
Deprecated.UsecloseOutbound()
-
close
@Deprecated public ChannelFuture close(ChannelPromise promise)
Deprecated.
-
closeOutbound
public ChannelFuture closeOutbound()
Sends an SSLclose_notify
message to the specified channel and destroys the underlyingSSLEngine
. This will not close the underlyingChannel
. If you want to also close theChannel
useChannel.close()
orChannelOutboundInvoker.close()
-
closeOutbound
public ChannelFuture closeOutbound(ChannelPromise promise)
Sends an SSLclose_notify
message to the specified channel and destroys the underlyingSSLEngine
. This will not close the underlyingChannel
. If you want to also close theChannel
useChannel.close()
orChannelOutboundInvoker.close()
-
sslCloseFuture
public Future<Channel> sslCloseFuture()
Return theFuture
that will get notified if the inbound of theSSLEngine
is closed. This method will return the sameFuture
all the time.- See Also:
SSLEngine
-
handlerRemoved0
public void handlerRemoved0(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from class:ByteToMessageDecoder
Gets called after theByteToMessageDecoder
was removed from the actual context and it doesn't handle events anymore.- Overrides:
handlerRemoved0
in classByteToMessageDecoder
- Throws:
java.lang.Exception
-
bind
public void bind(ChannelHandlerContext ctx, java.net.SocketAddress localAddress, ChannelPromise promise) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a bind operation is made.- Specified by:
bind
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the bind operation is madelocalAddress
- theSocketAddress
to which it should boundpromise
- theChannelPromise
to notify once the operation completes- Throws:
java.lang.Exception
- thrown if an error occurs
-
connect
public void connect(ChannelHandlerContext ctx, java.net.SocketAddress remoteAddress, java.net.SocketAddress localAddress, ChannelPromise promise) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a connect operation is made.- Specified by:
connect
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the connect operation is maderemoteAddress
- theSocketAddress
to which it should connectlocalAddress
- theSocketAddress
which is used as source on connectpromise
- theChannelPromise
to notify once the operation completes- Throws:
java.lang.Exception
- thrown if an error occurs
-
deregister
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a deregister operation is made from the current registeredEventLoop
.- Specified by:
deregister
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the close operation is madepromise
- theChannelPromise
to notify once the operation completes- Throws:
java.lang.Exception
- thrown if an error occurs
-
disconnect
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a disconnect operation is made.- Specified by:
disconnect
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the disconnect operation is madepromise
- theChannelPromise
to notify once the operation completes- Throws:
java.lang.Exception
- thrown if an error occurs
-
close
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a close operation is made.- Specified by:
close
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the close operation is madepromise
- theChannelPromise
to notify once the operation completes- Throws:
java.lang.Exception
- thrown if an error occurs
-
read
public void read(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
InterceptsChannelHandlerContext.read()
.- Specified by:
read
in interfaceChannelOutboundHandler
- Throws:
java.lang.Exception
-
write
public void write(ChannelHandlerContext ctx, java.lang.Object msg, ChannelPromise promise) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a write operation is made. The write operation will write the messages through theChannelPipeline
. Those are then ready to be flushed to the actualChannel
onceChannel.flush()
is called- Specified by:
write
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the write operation is mademsg
- the message to writepromise
- theChannelPromise
to notify once the operation completes- Throws:
java.lang.Exception
- thrown if an error occurs
-
flush
public void flush(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from interface:ChannelOutboundHandler
Called once a flush operation is made. The flush operation will try to flush out all previous written messages that are pending.- Specified by:
flush
in interfaceChannelOutboundHandler
- Parameters:
ctx
- theChannelHandlerContext
for which the flush operation is made- Throws:
java.lang.Exception
- thrown if an error occurs
-
channelInactive
public void channelInactive(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from class:ChannelInboundHandlerAdapter
CallsChannelHandlerContext.fireChannelInactive()
to forward to the nextChannelInboundHandler
in theChannelPipeline
. Sub-classes may override this method to change behavior.- Specified by:
channelInactive
in interfaceChannelInboundHandler
- Overrides:
channelInactive
in classByteToMessageDecoder
- Throws:
java.lang.Exception
-
exceptionCaught
public void exceptionCaught(ChannelHandlerContext ctx, java.lang.Throwable cause) throws java.lang.Exception
Description copied from class:ChannelInboundHandlerAdapter
CallsChannelHandlerContext.fireExceptionCaught(Throwable)
to forward to the nextChannelHandler
in theChannelPipeline
. Sub-classes may override this method to change behavior.- Specified by:
exceptionCaught
in interfaceChannelHandler
- Specified by:
exceptionCaught
in interfaceChannelInboundHandler
- Overrides:
exceptionCaught
in classChannelInboundHandlerAdapter
- Throws:
java.lang.Exception
-
isEncrypted
@Deprecated public static boolean isEncrypted(ByteBuf buffer)
Deprecated.Returnstrue
if the givenByteBuf
is encrypted. Be aware that this method will not increase the readerIndex of the givenByteBuf
.- Parameters:
buffer
- TheByteBuf
to read from. Be aware that it must have at least 5 bytes to read, otherwise it will throw anIllegalArgumentException
.- Returns:
- encrypted
true
if theByteBuf
is encrypted,false
otherwise. - Throws:
java.lang.IllegalArgumentException
- Is thrown if the givenByteBuf
has not at least 5 bytes to read.
-
isEncrypted
public static boolean isEncrypted(ByteBuf buffer, boolean probeSSLv2)
Returnstrue
if the givenByteBuf
is encrypted. Be aware that this method will not increase the readerIndex of the givenByteBuf
.- Parameters:
buffer
- TheByteBuf
to read from. Be aware that it must have at least 5 bytes to read, otherwise it will throw anIllegalArgumentException
.probeSSLv2
-true
if the inputbuffer
might be SSLv2. Iftrue
is used this methods might produce false-positives in some cases so it's strongly suggested to usefalse
.- Returns:
- encrypted
true
if theByteBuf
is encrypted,false
otherwise. - Throws:
java.lang.IllegalArgumentException
- Is thrown if the givenByteBuf
has not at least 5 bytes to read.
-
decode
protected void decode(ChannelHandlerContext ctx, ByteBuf in, java.util.List<java.lang.Object> out) throws javax.net.ssl.SSLException
Description copied from class:ByteToMessageDecoder
Decode the from oneByteBuf
to an other. This method will be called till either the inputByteBuf
has nothing to read when return from this method or till nothing was read from the inputByteBuf
.- Specified by:
decode
in classByteToMessageDecoder
- Parameters:
ctx
- theChannelHandlerContext
which thisByteToMessageDecoder
belongs toin
- theByteBuf
from which to read dataout
- theList
to which decoded messages should be added- Throws:
javax.net.ssl.SSLException
-
channelReadComplete
public void channelReadComplete(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from class:ChannelInboundHandlerAdapter
CallsChannelHandlerContext.fireChannelReadComplete()
to forward to the nextChannelInboundHandler
in theChannelPipeline
. Sub-classes may override this method to change behavior.- Specified by:
channelReadComplete
in interfaceChannelInboundHandler
- Overrides:
channelReadComplete
in classByteToMessageDecoder
- Throws:
java.lang.Exception
-
handlerAdded
public void handlerAdded(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from class:ChannelHandlerAdapter
Do nothing by default, sub-classes may override this method.- Specified by:
handlerAdded
in interfaceChannelHandler
- Overrides:
handlerAdded
in classChannelHandlerAdapter
- Throws:
java.lang.Exception
-
renegotiate
public Future<Channel> renegotiate(Promise<Channel> promise)
Performs TLS renegotiation.
-
channelActive
public void channelActive(ChannelHandlerContext ctx) throws java.lang.Exception
Issues an initial TLS handshake once connected when used in client-mode- Specified by:
channelActive
in interfaceChannelInboundHandler
- Overrides:
channelActive
in classChannelInboundHandlerAdapter
- Throws:
java.lang.Exception
-
-