public class SslHandler extends FrameDecoder implements ChannelDownstreamHandler
Channel. Please refer
to the "SecureChat" example in the distribution or the web
site for the detailed usage.
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.
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.
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:
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.
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.
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:
SslHandler instance with startTls flag set
to true,SslHandler to the ChannelPipeline, andSslHandler 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.
SslHandler instance with startTls flag set
to false,SslHandler to the ChannelPipeline, andhandshake().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.
| Modifier and Type | Class and Description |
|---|---|
private static class |
SslHandler.ClosingChannelFutureListener |
private static class |
SslHandler.PendingWrite |
private class |
SslHandler.SSLEngineInboundCloseFuture |
ChannelHandler.Sharable| Modifier and Type | Field and Description |
|---|---|
private SslBufferPool |
bufferPool |
private static java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> |
CLOSED_OUTBOUND_AND_CHANNEL_UPDATER |
private int |
closedOutboundAndChannel |
private boolean |
closeOnSslException |
private ChannelHandlerContext |
ctx |
private static SslBufferPool |
defaultBufferPool |
private static java.nio.ByteBuffer |
EMPTY_BUFFER |
private boolean |
enableRenegotiation |
private javax.net.ssl.SSLEngine |
engine |
private ChannelFuture |
handshakeFuture |
(package private) java.lang.Object |
handshakeLock |
private boolean |
handshaken |
private Timeout |
handshakeTimeout |
private long |
handshakeTimeoutInMillis |
private boolean |
handshaking |
private static java.util.regex.Pattern |
IGNORABLE_CLASS_IN_STACK |
private static java.util.regex.Pattern |
IGNORABLE_ERROR_MESSAGE |
(package private) int |
ignoreClosedChannelException |
(package private) java.lang.Object |
ignoreClosedChannelExceptionLock |
private boolean |
issueHandshake |
private static InternalLogger |
logger |
private int |
packetLength |
private java.util.Queue<MessageEvent> |
pendingEncryptedWrites |
private NonReentrantLock |
pendingEncryptedWritesLock |
private java.util.Queue<SslHandler.PendingWrite> |
pendingUnencryptedWrites |
private NonReentrantLock |
pendingUnencryptedWritesLock |
private static java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> |
SENT_CLOSE_NOTIFY_UPDATER |
private static java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> |
SENT_FIRST_MESSAGE_UPDATER |
private int |
sentCloseNotify |
private int |
sentFirstMessage |
private SslHandler.SSLEngineInboundCloseFuture |
sslEngineCloseFuture |
private boolean |
startTls |
private Timer |
timer |
private boolean |
writeBeforeHandshakeDone |
cumulation, DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS| Constructor and Description |
|---|
SslHandler(javax.net.ssl.SSLEngine engine)
Creates a new instance.
|
SslHandler(javax.net.ssl.SSLEngine engine,
boolean startTls)
Creates a new instance.
|
SslHandler(javax.net.ssl.SSLEngine engine,
SslBufferPool bufferPool)
Creates a new instance.
|
SslHandler(javax.net.ssl.SSLEngine engine,
SslBufferPool bufferPool,
boolean startTls)
Creates a new instance.
|
SslHandler(javax.net.ssl.SSLEngine engine,
SslBufferPool bufferPool,
boolean startTls,
Timer timer,
long handshakeTimeoutInMillis)
Creates a new instance.
|
| Modifier and Type | Method and Description |
|---|---|
void |
afterRemove(ChannelHandlerContext ctx)
Fail all pending writes which we were not able to flush out
|
void |
beforeAdd(ChannelHandlerContext ctx) |
private void |
cancelHandshakeTimeout() |
void |
channelClosed(ChannelHandlerContext ctx,
ChannelStateEvent e)
Loop over all the pending writes and fail them.
|
void |
channelConnected(ChannelHandlerContext ctx,
ChannelStateEvent e)
Calls
handshake() once the Channel is connected |
void |
channelDisconnected(ChannelHandlerContext ctx,
ChannelStateEvent e)
Invoked when a
Channel was disconnected from its remote peer. |
ChannelFuture |
close()
Sends an SSL
close_notify message to the specified channel and
destroys the underlying SSLEngine. |
private void |
closeEngine() |
private void |
closeOutboundAndChannel(ChannelHandlerContext context,
ChannelStateEvent e) |
protected java.lang.Object |
decode(ChannelHandlerContext ctx,
Channel channel,
ChannelBuffer in)
Decodes the received packets so far into a frame.
|
void |
exceptionCaught(ChannelHandlerContext ctx,
ExceptionEvent e)
Invoked when an exception was raised by an I/O thread or a
ChannelHandler. |
private void |
flushPendingEncryptedWrites(ChannelHandlerContext ctx) |
boolean |
getCloseOnSSLException() |
static SslBufferPool |
getDefaultBufferPool()
Returns the default
SslBufferPool used when no pool is
specified in the constructor. |
private static int |
getEncryptedPacketLength(ChannelBuffer buffer,
int offset)
Return how much bytes can be read out of the encrypted data.
|
javax.net.ssl.SSLEngine |
getEngine()
Returns the
SSLEngine which is used by this handler. |
long |
getHandshakeTimeout()
Return the timeout (in ms) after which the
ChannelFuture of handshake() will be failed, while
a handshake is in progress |
private static short |
getShort(ChannelBuffer buf,
int offset)
Reads a big-endian short integer from the buffer.
|
ChannelFuture |
getSSLEngineInboundCloseFuture()
Return the
ChannelFuture that will get notified if the inbound of the SSLEngine will get closed. |
void |
handleDownstream(ChannelHandlerContext context,
ChannelEvent evt)
Handles the specified downstream event.
|
private void |
handleRenegotiation(javax.net.ssl.SSLEngineResult.HandshakeStatus handshakeStatus) |
ChannelFuture |
handshake()
Starts an SSL / TLS handshake for the specified channel.
|
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. |
boolean |
isEnableRenegotiation()
Returns
true if and only if TLS renegotiation is enabled. |
static boolean |
isEncrypted(ChannelBuffer buffer)
Returns
true if the given ChannelBuffer is encrypted. |
boolean |
isIssueHandshake()
Returns
true if the automatic handshake is enabled |
private void |
offerEncryptedWriteRequest(MessageEvent encryptedWrite) |
private void |
runDelegatedTasks()
Fetches all delegated tasks from the
SSLEngine and runs them immediately. |
void |
setCloseOnSSLException(boolean closeOnSslException)
|
void |
setEnableRenegotiation(boolean enableRenegotiation)
Enables or disables TLS renegotiation.
|
private void |
setHandshakeFailure(Channel channel,
javax.net.ssl.SSLException cause) |
private void |
setHandshakeSuccess(Channel channel) |
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. |
void |
setIssueHandshake(boolean issueHandshake)
Enables or disables the automatic handshake once the
Channel is
connected. |
private ChannelBuffer |
unwrap(ChannelHandlerContext ctx,
Channel channel,
java.nio.ByteBuffer nioInNetBuf,
int initialNettyOutAppBufCapacity,
boolean mightNeedHandshake)
Unwraps inbound SSL records.
|
private void |
unwrapNonAppData(ChannelHandlerContext ctx,
Channel channel,
boolean mightNeedHandshake)
Calls
SSLEngine.unwrap(ByteBuffer, ByteBuffer) with an empty buffer to handle handshakes, etc. |
private void |
wrap(ChannelHandlerContext context,
Channel channel) |
private ChannelFuture |
wrapNonAppData(ChannelHandlerContext ctx,
Channel channel) |
actualReadableBytes, afterAdd, appendToCumulation, beforeRemove, cleanup, decodeLast, extractFrame, getMaxCumulationBufferCapacity, getMaxCumulationBufferComponents, internalBuffer, isUnfold, messageReceived, newCumulationBuffer, replace, setMaxCumulationBufferCapacity, setMaxCumulationBufferComponents, setUnfold, unfoldAndFireMessageReceived, updateCumulationchannelBound, channelInterestChanged, channelOpen, channelUnbound, childChannelClosed, childChannelOpen, handleUpstream, writeCompleteprivate static final InternalLogger logger
private static final java.nio.ByteBuffer EMPTY_BUFFER
private static final java.util.regex.Pattern IGNORABLE_CLASS_IN_STACK
private static final java.util.regex.Pattern IGNORABLE_ERROR_MESSAGE
private static SslBufferPool defaultBufferPool
private volatile ChannelHandlerContext ctx
private final javax.net.ssl.SSLEngine engine
private final SslBufferPool bufferPool
private final boolean startTls
private volatile boolean enableRenegotiation
final java.lang.Object handshakeLock
private boolean handshaking
private volatile boolean handshaken
private volatile ChannelFuture handshakeFuture
private volatile int sentFirstMessage
private volatile int sentCloseNotify
private volatile int closedOutboundAndChannel
private static final java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> SENT_FIRST_MESSAGE_UPDATER
private static final java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> SENT_CLOSE_NOTIFY_UPDATER
private static final java.util.concurrent.atomic.AtomicIntegerFieldUpdater<SslHandler> CLOSED_OUTBOUND_AND_CHANNEL_UPDATER
int ignoreClosedChannelException
final java.lang.Object ignoreClosedChannelExceptionLock
private final java.util.Queue<SslHandler.PendingWrite> pendingUnencryptedWrites
private final NonReentrantLock pendingUnencryptedWritesLock
private final java.util.Queue<MessageEvent> pendingEncryptedWrites
private final NonReentrantLock pendingEncryptedWritesLock
private volatile boolean issueHandshake
private volatile boolean writeBeforeHandshakeDone
private final SslHandler.SSLEngineInboundCloseFuture sslEngineCloseFuture
private boolean closeOnSslException
private int packetLength
private final Timer timer
private final long handshakeTimeoutInMillis
private Timeout handshakeTimeout
public SslHandler(javax.net.ssl.SSLEngine engine)
engine - the SSLEngine this handler will usepublic SslHandler(javax.net.ssl.SSLEngine engine,
SslBufferPool bufferPool)
engine - the SSLEngine this handler will usebufferPool - the SslBufferPool where this handler will
acquire the buffers required by the SSLEnginepublic SslHandler(javax.net.ssl.SSLEngine engine,
boolean startTls)
engine - the SSLEngine this handler will usestartTls - true if the first write request shouldn't be
encrypted by the SSLEnginepublic SslHandler(javax.net.ssl.SSLEngine engine,
SslBufferPool bufferPool,
boolean startTls)
engine - the SSLEngine this handler will usebufferPool - the SslBufferPool where this handler will
acquire the buffers required by the SSLEnginestartTls - true if the first write request shouldn't be
encrypted by the SSLEnginepublic SslHandler(javax.net.ssl.SSLEngine engine,
SslBufferPool bufferPool,
boolean startTls,
Timer timer,
long handshakeTimeoutInMillis)
engine - the SSLEngine this handler will usebufferPool - the SslBufferPool where this handler will acquire
the buffers required by the SSLEnginestartTls - true if the first write request shouldn't be encrypted
by the SSLEnginetimer - 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 anymorehandshakeTimeoutInMillis - the time in milliseconds after whic the handshake() will be failed, and so the future notifiedpublic static SslBufferPool getDefaultBufferPool()
SslBufferPool used when no pool is
specified in the constructor.public javax.net.ssl.SSLEngine getEngine()
SSLEngine which is used by this handler.public ChannelFuture handshake()
ChannelFuture which is notified when the handshake
succeeds or fails.public ChannelFuture close()
close_notify message to the specified channel and
destroys the underlying SSLEngine.public boolean isEnableRenegotiation()
true if and only if TLS renegotiation is enabled.public void setEnableRenegotiation(boolean enableRenegotiation)
public void setIssueHandshake(boolean issueHandshake)
public boolean isIssueHandshake()
true if the automatic handshake is enabledpublic ChannelFuture getSSLEngineInboundCloseFuture()
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 SSLEnginepublic long getHandshakeTimeout()
ChannelFuture of handshake() will be failed, while
a handshake is in progresspublic void setCloseOnSSLException(boolean closeOnSslException)
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.public boolean getCloseOnSSLException()
public void handleDownstream(ChannelHandlerContext context, ChannelEvent evt) throws java.lang.Exception
ChannelDownstreamHandlerhandleDownstream in interface ChannelDownstreamHandlercontext - the context object for this handlerevt - the downstream event to process or interceptjava.lang.Exceptionprivate void cancelHandshakeTimeout()
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws java.lang.Exception
SimpleChannelUpstreamHandlerChannel was disconnected from its remote peer.channelDisconnected in class FrameDecoderjava.lang.Exceptionprivate void closeEngine()
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws java.lang.Exception
SimpleChannelUpstreamHandlerChannelHandler.exceptionCaught in class FrameDecoderjava.lang.Exceptionprivate boolean ignoreException(java.lang.Throwable t)
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.public static boolean isEncrypted(ChannelBuffer buffer)
true if the given ChannelBuffer is encrypted. Be aware that this method
will not increase the readerIndex of the given ChannelBuffer.buffer - The ChannelBuffer to read from. Be aware that it must have at least 5 bytes to read,
otherwise it will throw an IllegalArgumentException.true if the ChannelBuffer is encrypted, false otherwise.java.lang.IllegalArgumentException - Is thrown if the given ChannelBuffer has not at least 5 bytes to read.private static int getEncryptedPacketLength(ChannelBuffer buffer, int offset)
ChannelBuffer.buffer - The ChannelBuffer to read from. Be aware that it must have at least 5 bytes to read,
otherwise it will throw an IllegalArgumentException.-1 if the given ChannelBuffer is not encrypted at all.java.lang.IllegalArgumentException - Is thrown if the given ChannelBuffer has not at least 5 bytes to read.protected java.lang.Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer in) throws java.lang.Exception
FrameDecoderFrameDecoder.extractFrame(ChannelBuffer, int, int) method,
to make optimizations easier later.decode in class FrameDecoderctx - the context of this handlerchannel - the current channelin - 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.null if there's not enough data in the buffer to decode a frame.java.lang.Exceptionprivate static short getShort(ChannelBuffer buf, int offset)
ChannelBuffer.getShort(int) because it might be a little-endian buffer.private void wrap(ChannelHandlerContext context, Channel channel) throws javax.net.ssl.SSLException
javax.net.ssl.SSLExceptionprivate void offerEncryptedWriteRequest(MessageEvent encryptedWrite)
private void flushPendingEncryptedWrites(ChannelHandlerContext ctx)
private ChannelFuture wrapNonAppData(ChannelHandlerContext ctx, Channel channel) throws javax.net.ssl.SSLException
javax.net.ssl.SSLExceptionprivate void unwrapNonAppData(ChannelHandlerContext ctx, Channel channel, boolean mightNeedHandshake) throws javax.net.ssl.SSLException
SSLEngine.unwrap(ByteBuffer, ByteBuffer) with an empty buffer to handle handshakes, etc.javax.net.ssl.SSLExceptionprivate ChannelBuffer unwrap(ChannelHandlerContext ctx, Channel channel, java.nio.ByteBuffer nioInNetBuf, int initialNettyOutAppBufCapacity, boolean mightNeedHandshake) throws javax.net.ssl.SSLException
javax.net.ssl.SSLExceptionprivate void handleRenegotiation(javax.net.ssl.SSLEngineResult.HandshakeStatus handshakeStatus)
private void runDelegatedTasks()
SSLEngine and runs them immediately.private boolean setHandshakeSuccessIfStillHandshaking(Channel channel)
SSLEngine implementations that skip SSLEngineResult.HandshakeStatus.FINISHED and
go straight into SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING when handshake is finished.true if and only if the workaround has been applied and thus handshakeFuture has been
marked as success by this methodprivate void setHandshakeSuccess(Channel channel)
private void setHandshakeFailure(Channel channel, javax.net.ssl.SSLException cause)
private void closeOutboundAndChannel(ChannelHandlerContext context, ChannelStateEvent e)
public void beforeAdd(ChannelHandlerContext ctx) throws java.lang.Exception
beforeAdd in interface LifeCycleAwareChannelHandlerbeforeAdd in class FrameDecoderjava.lang.Exceptionpublic void afterRemove(ChannelHandlerContext ctx) throws java.lang.Exception
afterRemove in interface LifeCycleAwareChannelHandlerafterRemove in class FrameDecoderjava.lang.Exceptionpublic void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws java.lang.Exception
handshake() once the Channel is connectedchannelConnected in class SimpleChannelUpstreamHandlerjava.lang.Exceptionpublic void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws java.lang.Exception
channelClosed in class FrameDecoderjava.lang.Exception