The secure communications modes include:
- Integrity Protection. SSL/TLS/DTLS protects against modification of messages by an active wiretapper.
- Authentication. In most modes, SSL/TLS/DTLS provides peer authentication. Servers are usually authenticated, and clients may be authenticated as requested by servers.
- Confidentiality (Privacy Protection). In most modes, SSL/TLS/DTLS encrypts data being sent between client and server. This protects the confidentiality of data, so that passive wiretappers won't see sensitive data such as financial information or personal information of many kinds.
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 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/DTLS 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 data
Application 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/DTLS messages
"alert", "change_cipher_spec," and "handshake.")
There are five distinct phases to an SSLEngine.
- Creation - The
SSLEnginehas been created and initialized, but has not yet been used. During this phase, an application may set anySSLEngine-specific settings (enabled cipher suites, whether theSSLEngineshould 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. - Initial Handshake - The initial handshake is a procedure by which the two peers exchange communication parameters until an SSLSession is established. Application data can not be sent during this phase.
- Application Data - Once the communication parameters have
been established and the handshake is complete, application data
may flow through the
SSLEngine. Outbound application messages are encrypted and integrity protected, and inbound messages reverse the process. - Rehandshaking - Either side may request a renegotiation of
the session at any time during the Application Data phase. New
handshaking data can be intermixed among the application data.
Before starting the rehandshake phase, the application may
reset the SSL/TLS/DTLS communication parameters such as the list of
enabled ciphersuites and whether to use client authentication,
but can not change between client/server modes. As before, once
handshaking has begun, any new
SSLEngineconfiguration settings will not be used until the next handshake. - Closure - When the connection is no longer needed, the client
and the server applications should each close both sides of their
respective connections. For
SSLEngineobjects, an application should callcloseOutbound()and send any remaining messages to the peer. Likewise, an application should receive any remaining messages from the peer before callingcloseInbound(). The underlying transport mechanism can then be closed after both sides of theSSLEnginehave been closed. If the connection is not closed in an orderly manner (for examplecloseInbound()is called before the peer's write closure notification has been received), exceptions will be raised to indicate that an error has occurred. Once an engine is closed, it is not reusable: a newSSLEnginemust 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 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/DTLS 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/DTLS
packets only, and does not store application data internally between
calls to wrap()/unwrap(). Thus input and output
ByteBuffers 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()) {
case 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;
case BUFFER_UNDERFLOW:
int netSize = engine.getSession().getPacketBufferSize();
// Resize buffer if needed.
if (netSize > src.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 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/DTLS link. The SSL/TLS/DTLS 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/DTLS 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:
- Supported cipher suites: all the suites which are
supported by the SSL implementation. This list is reported
using
getSupportedCipherSuites(). - Enabled cipher suites, which may be fewer than
the full set of supported suites. This group is set using the
setEnabledCipherSuites(String[])method, and queried using thegetEnabledCipherSuites()method. Initially, a default set of cipher suites will be enabled on a new engine that represents the minimum suggested configuration.
Each SSL/TLS/DTLS 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. Note that the
default mode for a new SSLEngine is provider-specific.
Applications should set the mode explicitly before invoking other
methods of the SSLEngine. Once the initial handshaking has
started, an SSLEngine can not switch between client and server
modes, even when performing renegotiations.
The ApplicationProtocol String values returned by the methods
in this class are in the network byte representation sent by the peer.
The bytes could be directly compared, or converted to its Unicode
String format for comparison.
String networkString = sslEngine.getHandshakeApplicationProtocol();
byte[] bytes = networkString.getBytes(StandardCharsets.ISO_8859_1);
//
// Match using bytes:
//
// "http/1.1" (7-bit ASCII values same in UTF-8)
// MEETEI MAYEK LETTERS "HUK UN I" (Unicode 0xabcd->0xabcf)
//
String HTTP1_1 = "http/1.1";
byte[] HTTP1_1_BYTES = HTTP1_1.getBytes(StandardCharsets.UTF_8);
byte[] HUK_UN_I_BYTES = new byte[] {
(byte) 0xab, (byte) 0xcd,
(byte) 0xab, (byte) 0xce,
(byte) 0xab, (byte) 0xcf};
if ((Arrays.compare(bytes, HTTP1_1_BYTES) == 0 )
|| Arrays.compare(bytes, HUK_UN_I_BYTES) == 0) {
...
}
//
// Alternatively match using string.equals() if we know the ALPN value
// was encoded from a String using a certain character set,
// for example UTF-8. The ALPN value must first be properly
// decoded to a Unicode String before use.
//
String unicodeString = new String(bytes, StandardCharsets.UTF_8);
if (unicodeString.equals(HTTP1_1)
|| unicodeString.equals("\uabcd\uabce\uabcf")) {
...
}
Applications might choose to process delegated tasks in different
threads. When an SSLEngine
is created, the current 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.
Concurrency Notes: There are two concurrency issues to be aware of:
- The
wrap()andunwrap()methods may execute concurrently of each other. - The SSL/TLS/DTLS protocols employ ordered packets.
Applications must take care to ensure that generated packets
are delivered in sequence. If packets arrive
out-of-order, unexpected or fatal results may occur.
For example:
synchronized (outboundLock) { sslEngine.wrap(src, dst); outboundQueue.put(dst); }As a corollary, two threads must not attempt to call the same method (eitherwrap()orunwrap()) concurrently, because there is no way to guarantee the eventual packet ordering.
- Since:
- 1.5
- See Also:
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionabstract voidInitiates handshaking (initial or renegotiation) on this SSLEngine.abstract voidSignals that no more inbound network data will be sent to thisSSLEngine.abstract voidSignals that no more outbound application data will be sent on thisSSLEngine.Returns the most recent application protocol value negotiated for this connection.abstract RunnableReturns a delegatedRunnabletask for thisSSLEngine.abstract String[]Returns the names of the SSL cipher suites which are currently enabled for use on this engine.abstract String[]Returns the names of the protocol versions which are currently enabled for use with thisSSLEngine.abstract booleanReturns true if new SSL sessions may be established by this engine.Returns the application protocol value negotiated on a SSL/TLS handshake currently in progress.Retrieves the callback function that selects an application protocol value during a SSL/TLS/DTLS handshake.Returns theSSLSessionbeing constructed during a SSL/TLS/DTLS handshake.abstract SSLEngineResult.HandshakeStatusReturns the current handshake status for thisSSLEngine.abstract booleanReturns true if the engine will require client authentication.Returns the host name of the peer.intReturns the port number of the peer.abstract SSLSessionReturns theSSLSessionin use in thisSSLEngine.Returns the SSLParameters in effect for this SSLEngine.abstract String[]Returns the names of the cipher suites which could be enabled for use on this engine.abstract String[]Returns the names of the protocols which could be enabled for use with thisSSLEngine.abstract booleanReturns true if the engine is set to use client mode when handshaking.abstract booleanReturns true if the engine will request client authentication.abstract booleanReturns whetherunwrap(ByteBuffer, ByteBuffer)will accept any more inbound data messages.abstract booleanReturns whetherwrap(ByteBuffer, ByteBuffer)will produce any more outbound data messages.abstract voidsetEnabledCipherSuites(String[] suites) Sets the cipher suites enabled for use on this engine.abstract voidsetEnabledProtocols(String[] protocols) Set the protocol versions enabled for use on this engine.abstract voidsetEnableSessionCreation(boolean flag) Controls whether new SSL sessions may be established by this engine.voidRegisters a callback function that selects an application protocol value for a SSL/TLS/DTLS handshake.abstract voidsetNeedClientAuth(boolean need) Configures the engine to require client authentication.voidsetSSLParameters(SSLParameters params) Applies SSLParameters to this engine.abstract voidsetUseClientMode(boolean mode) Configures the engine to use client (or server) mode when handshaking.abstract voidsetWantClientAuth(boolean want) Configures the engine to request client authentication.unwrap(ByteBuffer src, ByteBuffer dst) Attempts to decode SSL/TLS/DTLS network data into a plaintext application data buffer.unwrap(ByteBuffer src, ByteBuffer[] dsts) Attempts to decode SSL/TLS/DTLS network data into a sequence of plaintext application data buffers.abstract SSLEngineResultunwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) Attempts to decode SSL/TLS/DTLS network data into a subsequence of plaintext application data buffers.abstract SSLEngineResultwrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) Attempts to encode plaintext bytes from a subsequence of data buffers into SSL/TLS/DTLS network data.wrap(ByteBuffer[] srcs, ByteBuffer dst) Attempts to encode plaintext bytes from a sequence of data buffers into SSL/TLS/DTLS network data.wrap(ByteBuffer src, ByteBuffer dst) Attempts to encode a buffer of plaintext application data into SSL/TLS/DTLS network data.
-
Constructor Details
-
SSLEngine
protected SSLEngine()Constructor for anSSLEngineproviding no hints for an internal session reuse strategy.- See Also:
-
SSLEngine
Constructor for anSSLEngine.SSLEngineimplementations may use thepeerHostandpeerPortparameters as hints for their internal session reuse strategy.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
SSLEngine.- Parameters:
peerHost- the name of the peer hostpeerPort- the port number of the peer- See Also:
-
-
Method Details
-
getPeerHost
Returns the host name of the peer.Note that the value is not authenticated, and should not be relied upon.
- Returns:
- the host name of the peer, or null if nothing is available.
-
getPeerPort
public int getPeerPort()Returns the port number of the peer.Note that the value is not authenticated, and should not be relied upon.
- Returns:
- the port number of the peer, or -1 if nothing is available.
-
wrap
Attempts to encode a buffer of plaintext application data into SSL/TLS/DTLS network data.An invocation of this method behaves in exactly the same manner as the invocation:
engine.wrap(new ByteBuffer[] { src }, 0, 1, dst);- Parameters:
src- aByteBuffercontaining outbound application datadst- aByteBufferto hold outbound network data- Returns:
- an
SSLEngineResultdescribing the result of this operation. - Throws:
SSLException- A problem was encountered while processing the data that caused theSSLEngineto abort. See the class description for more information on engine closure.ReadOnlyBufferException- if thedstbuffer is read-only.IllegalArgumentException- if eithersrcordstis null.IllegalStateException- if the client/server mode has not yet been set.- See Also:
-
wrap
Attempts to encode plaintext bytes from a sequence of data buffers into SSL/TLS/DTLS network data.An invocation of this method behaves in exactly the same manner as the invocation:
engine.wrap(srcs, 0, srcs.length, dst);- Parameters:
srcs- an array ofByteBufferscontaining the outbound application datadst- aByteBufferto hold outbound network data- Returns:
- an
SSLEngineResultdescribing the result of this operation. - Throws:
SSLException- A problem was encountered while processing the data that caused theSSLEngineto abort. See the class description for more information on engine closure.ReadOnlyBufferException- if thedstbuffer is read-only.IllegalArgumentException- if eithersrcsordstis null, or if any element insrcsis null.IllegalStateException- if the client/server mode has not yet been set.- See Also:
-
wrap
public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) throws SSLException Attempts to encode plaintext bytes from a subsequence of data buffers into SSL/TLS/DTLS network data. This "gathering" operation encodes, in a single invocation, a sequence of bytes from one or more of a given sequence of buffers. Gathering wraps are often useful when implementing network protocols or file formats that, for example, group data into segments consisting of one or more fixed-length headers followed by a variable-length body. SeeGatheringByteChannelfor more information on gathering, andGatheringByteChannel.write(ByteBuffer[], int, int)for more information on the subsequence behavior.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
SSLEnginehas not yet started its initial handshake, this method will automatically start the handshake.This method will attempt to produce SSL/TLS/DTLS records, 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
ByteBuffer's position is updated to reflect the amount of data consumed or produced. The limits remain the same.The underlying memory used by the
srcsanddst ByteBuffers must not be the same.See the class description for more information on engine closure.
- Parameters:
srcs- an array ofByteBufferscontaining the outbound application dataoffset- The offset within the buffer array of the first buffer from which bytes are to be retrieved; it must be non-negative and no larger thansrcs.lengthlength- The maximum number of buffers to be accessed; it must be non-negative and no larger thansrcs.length-offsetdst- aByteBufferto hold outbound network data- Returns:
- an
SSLEngineResultdescribing the result of this operation. - Throws:
SSLException- A problem was encountered while processing the data that caused theSSLEngineto abort. See the class description for more information on engine closure.IndexOutOfBoundsException- if the preconditions on theoffsetandlengthparameters do not hold.ReadOnlyBufferException- if thedstbuffer is read-only.IllegalArgumentException- if eithersrcsordstis null, or if any element in thesrcssubsequence specified is null.IllegalStateException- if the client/server mode has not yet been set.- See Also:
-
unwrap
Attempts to decode SSL/TLS/DTLS network data into a plaintext application data buffer.An invocation of this method behaves in exactly the same manner as the invocation:
engine.unwrap(src, new ByteBuffer[] { dst }, 0, 1);- Parameters:
src- aByteBuffercontaining inbound network data.dst- aByteBufferto hold inbound application data.- Returns:
- an
SSLEngineResultdescribing the result of this operation. - Throws:
SSLException- A problem was encountered while processing the data that caused theSSLEngineto abort. See the class description for more information on engine closure.ReadOnlyBufferException- if thedstbuffer is read-only.IllegalArgumentException- if eithersrcordstis null.IllegalStateException- if the client/server mode has not yet been set.- See Also:
-
unwrap
Attempts to decode SSL/TLS/DTLS network data into a sequence of plaintext application data buffers.An invocation of this method behaves in exactly the same manner as the invocation:
engine.unwrap(src, dsts, 0, dsts.length);- Parameters:
src- aByteBuffercontaining inbound network data.dsts- an array ofByteBuffers to hold inbound application data.- Returns:
- an
SSLEngineResultdescribing the result of this operation. - Throws:
SSLException- A problem was encountered while processing the data that caused theSSLEngineto abort. See the class description for more information on engine closure.ReadOnlyBufferException- if any of thedstbuffers are read-only.IllegalArgumentException- if eithersrcordstsis null, or if any element indstsis null.IllegalStateException- if the client/server mode has not yet been set.- See Also:
-
unwrap
public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws SSLException Attempts to decode SSL/TLS/DTLS network data into a subsequence of plaintext application data buffers. This "scattering" operation decodes, in a single invocation, a sequence of bytes into one or more of a given sequence of buffers. Scattering unwraps are often useful when implementing network protocols or file formats that, for example, group data into segments consisting of one or more fixed-length headers followed by a variable-length body. SeeScatteringByteChannelfor more information on scattering, andScatteringByteChannel.read(ByteBuffer[], int, int)for more information on the subsequence behavior.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
SSLEnginehas not yet started its initial handshake, this method will automatically start the handshake.This method will attempt to consume one complete SSL/TLS/DTLS network packet, but will never consume more than the sum of the bytes remaining in the buffers. Each
ByteBuffer's position is updated to reflect the amount of data consumed or produced. The limits remain the same.The underlying memory used by the
srcanddsts ByteBuffers must not be the same.The inbound network buffer,
src, 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/DTLS messages.See the class description for more information on engine closure.
- Parameters:
src- aByteBuffercontaining inbound network data.dsts- an array ofByteBuffers to hold inbound application data.offset- The offset within the buffer array of the first buffer from which bytes are to be transferred; it must be non-negative and no larger thandsts.length.length- The maximum number of buffers to be accessed; it must be non-negative and no larger thandsts.length-offset.- Returns:
- an
SSLEngineResultdescribing the result of this operation. - Throws:
SSLException- A problem was encountered while processing the data that caused theSSLEngineto abort. See the class description for more information on engine closure.IndexOutOfBoundsException- If the preconditions on theoffsetandlengthparameters do not hold.ReadOnlyBufferException- if any of thedstbuffers are read-only.IllegalArgumentException- if eithersrcordstsis null, or if any element in thedstssubsequence specified is null.IllegalStateException- if the client/server mode has not yet been set.- See Also:
-
getDelegatedTask
Returns a delegatedRunnabletask for thisSSLEngine.SSLEngineoperations may require the results of operations that block, or may take an extended period of time to complete. This method is used to obtain an outstandingRunnableoperation (task). Each task must be assigned a thread (possibly the current) to perform therunoperation. Once therunmethod returns, theRunnableobject is no longer needed and may be discarded.Delegated tasks run in the
AccessControlContextin place when this operatioUsibly ts="col-ss > - if the client/seofipeerHost, int peerPort)Constructor for anSSLEngine.
-