java.lang.ObjectA class which enables secure communications using protocols such as the Secure Sockets Layer (SSL) or IETF RFC 2246 "Transport Layer Security" (TLS) protocols, but is transport independent.javax.net.ssl.SSLEngine
The secure communications modes include:
The cipher suite used is established by a negotiation process called "handshaking". The goal of this process is to create or rejoin a "session", which may protect many connections over time. After handshaking has completed, you can access session attributes by using the #getSession() method.
The SSLSocket
class provides much of the same security
functionality, but all of the inbound and outbound data is
automatically transported using the underlying Socket , which by design uses a blocking model.
While this is appropriate for many applications, this model does not
provide the scalability required by large servers.
The primary distinction of an SSLEngine
is that it
operates on inbound and outbound byte streams, independent of the
transport mechanism. It is the responsibility of the
SSLEngine
user to arrange for reliable I/O transport to
the peer. By separating the SSL/TLS abstraction from the I/O
transport mechanism, the SSLEngine
can be used for a
wide variety of I/O types, such as non-blocking I/O (polling) , selectable non-blocking I/O , Socket and the
traditional Input/OutputStreams, local ByteBuffers or byte arrays, future asynchronous
I/O models , and so on.
At a high level, the SSLEngine
appears thus:
app data | ^ | | | v | | +----+-----|-----+----+ | | | | SSL|Engine | wrap() | | | unwrap() | OUTBOUND | INBOUND | | | | +----+-----|-----+----+ | | ^ | | | v | net dataApplication data (also known as plaintext or cleartext) is data which is produced or consumed by an application. Its counterpart is network data, which consists of either handshaking and/or ciphertext (encrypted) data, and destined to be transported via an I/O mechanism. Inbound data is data which has been received from the peer, and outbound data is destined for the peer.
(In the context of an SSLEngine
, the term "handshake
data" is taken to mean any data exchanged to establish and control a
secure connection. Handshake data includes the SSL/TLS messages
"alert", "change_cipher_spec," and "handshake.")
There are five distinct phases to an SSLEngine
.
SSLEngine
has been created and
initialized, but has not yet been used. During this phase, an
application may set any SSLEngine
-specific settings
(enabled cipher suites, whether the SSLEngine
should
handshake in client or server mode, and so on). Once
handshaking has begun, though, any new settings (except
client/server mode, see below) will be used for
the next handshake.
SSLEngine
. Outbound
application messages are encrypted and integrity protected,
and inbound messages reverse the process.
SSLEngine
configuration settings will not be used until the next
handshake.
SSLEngine
and should
send/receive any remaining messages to the peer before
closing the underlying transport mechanism. Once an engine is
closed, it is not reusable: a new SSLEngine
must
be created.
SSLEngine
is created by calling SSLContext#createSSLEngine() from an initialized
SSLContext
. Any configuration
parameters should be set before making the first call to
wrap()
, unwrap()
, or
beginHandshake()
. These methods all trigger the
initial handshake.
Data moves through the engine by calling wrap() or ByteBuffer)
unwrap() on outbound or inbound data, respectively. Depending on
the state of the SSLEngine
, a wrap()
call
may consume application data from the source buffer and may produce
network data in the destination buffer. The outbound data
may contain application and/or handshake data. A call to
unwrap()
will examine the source buffer and may
advance the handshake if the data is handshaking information, or
may place application data in the destination buffer if the data
is application. The state of the underlying SSL/TLS algorithm
will determine when data is consumed and produced.
Calls to wrap()
and unwrap()
return an
SSLEngineResult
which indicates the status of the
operation, and (optionally) how to interact with the engine to make
progress.
The SSLEngine
produces/consumes complete SSL/TLS
packets only, and does not store application data internally between
calls to wrap()/unwrap()
. Thus input and output
ByteBuffer
s must be sized appropriately to hold the
maximum record that can be produced. Calls to SSLSession#getPacketBufferSize() and SSLSession#getApplicationBufferSize() should be used to determine
the appropriate buffer sizes. The size of the outbound application
data buffer generally does not matter. If buffer conditions do not
allow for the proper consumption/production of data, the application
must determine (via SSLEngineResult ) and correct the
problem, and then try the call again.
For example, unwrap()
will return a SSLEngineResult.Status#BUFFER_OVERFLOW result if the engine
determines that there is not enough destination buffer space available.
Applications should call SSLSession#getApplicationBufferSize()
and compare that value with the space available in the destination buffer,
enlarging the buffer if necessary. Similarly, if unwrap()
were to return a SSLEngineResult.Status#BUFFER_UNDERFLOW , the
application should call SSLSession#getPacketBufferSize() to ensure
that the source buffer has enough room to hold a record (enlarging if
necessary), and then obtain more inbound data.
SSLEngineResult r = engine.unwrap(src, dst); switch (r.getStatus()) { BUFFER_OVERFLOW: // Could attempt to drain the dst buffer of any already obtained // data, but we'll just increase it to the size needed. int appSize = engine.getSession().getApplicationBufferSize(); ByteBuffer b = ByteBuffer.allocate(appSize + dst.position()); dst.flip(); b.put(dst); dst = b; // retry the operation. break; BUFFER_UNDERFLOW: int netSize = engine.getSession().getPacketBufferSize(); // Resize buffer if needed. if (netSize > dst.capacity()) { ByteBuffer b = ByteBuffer.allocate(netSize); src.flip(); b.put(src); src = b; } // Obtain more inbound network data for src, // then retry the operation. break; // other cases: CLOSED, OK. }
Unlike SSLSocket
, all methods of SSLEngine are
non-blocking. SSLEngine
implementations may
require the results of tasks that may take an extended period of
time to complete, or may even block. For example, a TrustManager
may need to connect to a remote certificate validation service,
or a KeyManager might need to prompt a user to determine which
certificate to use as part of client authentication. Additionally,
creating cryptographic signatures and verifying them can be slow,
seemingly blocking.
For any operation which may potentially block, the
SSLEngine
will create a java.lang.Runnable
delegated task. When SSLEngineResult
indicates that a
delegated task result is needed, the application must call #getDelegatedTask() to obtain an outstanding delegated task and
call its run() method (possibly using
a different thread depending on the compute strategy). The
application should continue obtaining delegated tasks until no more
exist, and try the original operation again.
At the end of a communication session, applications should properly
close the SSL/TLS link. The SSL/TLS protocols have closure handshake
messages, and these messages should be communicated to the peer
before releasing the SSLEngine
and closing the
underlying transport mechanism. A close can be initiated by one of:
an SSLException, an inbound closure handshake message, or one of the
close methods. In all cases, closure handshake messages are
generated by the engine, and wrap()
should be repeatedly
called until the resulting SSLEngineResult
's status
returns "CLOSED", or #isOutboundDone() returns true. All
data obtained from the wrap()
method should be sent to the
peer.
#closeOutbound() is used to signal the engine that the application will not be sending any more data.
A peer will signal its intent to close by sending its own closure
handshake message. After this message has been received and
processed by the local SSLEngine
's unwrap()
call, the application can detect the close by calling
unwrap()
and looking for a SSLEngineResult
with status "CLOSED", or if #isInboundDone() returns true.
If for some reason the peer closes the communication link without
sending the proper SSL/TLS closure message, the application can
detect the end-of-stream and can signal the engine via #closeInbound() that there will no more inbound messages to
process. Some applications might choose to require orderly shutdown
messages from a peer, in which case they can check that the closure
was generated by a handshake message and not by an end-of-stream
condition.
There are two groups of cipher suites which you will need to know about when managing cipher suites:
Each SSL/TLS connection must have one client and one server, thus
each endpoint must decide which role to assume. This choice determines
who begins the handshaking process as well as which type of messages
should be sent by each party. The method #setUseClientMode(boolean) configures the mode. Once the initial
handshaking has started, an SSLEngine
can not switch
between client and server modes, even when performing renegotiations.
Applications might choose to process delegated tasks in different
threads. When an SSLEngine
is created, the current java.security.AccessControlContext
is saved. All future delegated tasks will be processed using this
context: that is, all access control decisions will be made using the
context captured at engine creation.
wrap()
and unwrap()
methods
may execute concurrently of each other.
For example:
synchronized (outboundLock) { sslEngine.wrap(src, dst); outboundQueue.put(dst); }As a corollary, two threads must not attempt to call the same method (either
wrap()
or unwrap()
) concurrently,
because there is no way to guarantee the eventual packet ordering.
1.5
- Brad
- R. WetmoreConstructor: |
---|
SSLEngine providing no hints
for an internal session reuse strategy.
|
SSLEngine .
Some cipher suites (such as Kerberos) require remote hostname information. Implementations of this class should use this constructor to use Kerberos.
The parameters are not authenticated by the
|
Methods from java.lang.Object: |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Method from javax.net.ssl.SSLEngine Detail: |
---|
This method is not needed for the initial handshake, as the
Note that the peer may also request a session renegotiation with
this Unlike the SSLSocket#startHandshake() method, this method does not block until handshaking is completed. To force a complete SSL/TLS session renegotiation, the current session should be invalidated prior to calling this method.
Some protocols may not support multiple handshakes on an existing
engine and may throw an |
SSLEngine .
If the application initiated the closing process by calling #closeOutbound() , under some circumstances it is not required that the initiator wait for the peer's corresponding close message. (See section 7.2.1 of the TLS specification (RFC 2246) for more information on waiting for closure alerts.) In such cases, this method need not be called. But if the application did not initiate the closure process, or if the circumstances above do not apply, this method should be called whenever the end of the SSL/TLS data stream is reached. This ensures closure of the inbound side, and checks that the peer followed the SSL/TLS close procedure properly, thus detecting possible truncation attacks. This method is idempotent: if the inbound side has already been closed, this method does not do anything. wrap() should be called to flush any remaining handshake data. |
SSLEngine .
This method is idempotent: if the outbound side has already been closed, this method does not do anything. #wrap(ByteBuffer, ByteBuffer) should be called to flush any remaining handshake data. |
Runnable task for
this SSLEngine .
Delegated tasks run in the A call to this method will return each outstanding task exactly once. Multiple delegated tasks can be run in parallel. |
|
Even if a suite has been enabled, it might never be used. (For example, the peer does not support it, the requisite certificates/private keys for the suite are not available, or an anonymous suite is enabled but authentication is required.) |
SSLEngine . |
TLS protocols may negotiate parameters that are needed when using an instance of this class, but before the {@code SSLSession} has been completely initialized and made available via {@code getSession}. For example, the list of valid signature algorithms may restrict the type of certificates that can used during TrustManager decisions, or the maximum TLS fragment packet sizes can be resized to better support the network environment. This method provides early access to the {@code SSLSession} being constructed. Depending on how far the handshake has progressed, some data may not yet be available for use. For example, if a remote server will be sending a Certificate chain, but that chain has yet not been processed, the {@code getPeerCertificates} method of {@code SSLSession} will throw a SSLPeerUnverifiedException. Once that chain has been processed, {@code getPeerCertificates} will return the proper value. |
SSLEngine . |
|
Note that the value is not authenticated, and should not be relied upon. |
Note that the value is not authenticated, and should not be relied upon. |
|
SSLSession in use in this
SSLEngine .
These can be long lived, and frequently correspond to an entire login session for some user. The session specifies a particular cipher suite which is being actively used by all connections in that session, as well as the identities of the session's client and server. Unlike SSLSocket#getSession() this method does not block until handshaking is complete. Until the initial handshake has completed, this method returns a session object which reports an invalid cipher suite of "SSL_NULL_WITH_NULL_NULL". |
|
SSLEngine . |
|
|
|
Note that during the closure phase, a |
|
Each cipher suite in the See #getEnabledCipherSuites() for more information on why a specific cipher suite may never be used on a engine. |
The protocols must have been listed by getSupportedProtocols()
as being supported. Following a successful call to this method,
only protocols listed in the |
An engine's client authentication setting is one of the following: Unlike #setWantClientAuth(boolean) , if this option is set and the client chooses not to provide authentication information about itself, the negotiations will stop and the engine will begin its closure procedure. Calling this method overrides any previous setting made by this method or #setWantClientAuth(boolean) . |
This means: |
This method must be called before any handshaking occurs. Once handshaking has begun, the mode can not be reset for the life of this engine. Servers normally authenticate themselves, and clients are not required to do so. |
An engine's client authentication setting is one of the following: Unlike #setNeedClientAuth(boolean) , if this option is set and the client chooses not to provide authentication information about itself, the negotiations will continue. Calling this method overrides any previous setting made by this method or #setNeedClientAuth(boolean) . |
An invocation of this method behaves in exactly the same manner as the invocation: ByteBuffer [], int, int) new ByteBuffer [] { dst , 0, 1);} |
An invocation of this method behaves in exactly the same manner as the invocation: ByteBuffer [], int, int) dsts, 0, dsts.length); |
Depending on the state of the SSLEngine, this method may consume network data without producing any application data (for example, it may consume handshake data.) The application is responsible for reliably obtaining the network data from the peer, and for invoking unwrap() on the data in the order it was received. The application must properly synchronize multiple calls to this method.
If this
This method will attempt to consume one complete SSL/TLS network
packet, but will never consume more than the sum of the bytes
remaining in the buffers. Each
The underlying memory used by the The inbound network buffer may be modified as a result of this call: therefore if the network data packet is required for some secondary purpose, the data should be duplicated before calling this method. Note: the network data will not be useful to a second SSLEngine, as each SSLEngine contains unique random state which influences the SSL/TLS messages. See the class description for more information on engine closure. |
An invocation of this method behaves in exactly the same manner as the invocation: [], int, int, ByteBuffer) ByteBuffer [] { src , 0, 1, dst);} |
An invocation of this method behaves in exactly the same manner as the invocation: [], int, int, ByteBuffer) 0, srcs.length, dst); |
Depending on the state of the SSLEngine, this method may produce network data without consuming any application data (for example, it may generate handshake data.) The application is responsible for reliably transporting the network data to the peer, and for ensuring that data created by multiple calls to wrap() is transported in the same order in which it was generated. The application must properly synchronize multiple calls to this method.
If this
This method will attempt to produce one SSL/TLS packet, and will
consume as much source data as possible, but will never consume
more than the sum of the bytes remaining in each buffer. Each
The underlying memory used by the See the class description for more information on engine closure. |