java.lang.Object javax.net.ssl.SSLEngine
public abstract class SSLEngine
此类允许使用安全套接字层 (SSL) 或 IETF RFC 2246 "Transport Layer Security" (TLS) 协议进行安全通信,但它与传输无关。
安全的通信模式包括:
通过称为“握手”的协商过程来建立所用的密码套件。此过程的目的是创建或重新加入一个“会话”,“会话”可以保护很多会话过程中的连接。握手结束后,可以使用 getSession()
方法访问会话的属性。
SSLSocket
类提供很多相同的安全功能,但是所有的入站和出站数据都是使用底层 Socket
自动地传输,底层 Socket 设计为使用阻塞模型。虽然这适合于很多应用程序,但是此模型却不提供大型服务器所需的可伸缩性。
SSLEngine
的主要特征是它在入站和出站的字节流上操作,与传输机制无关。安排到同位体可靠的 I/O 传输是 SSLEngine
用户的职责。通过从 I/O 传输机制中分离出 SSL/TLS 抽象,SSLEngine
可以被用于各种 I/O 类型,例如 non-blocking I/O (polling)
、selectable non-blocking I/O
、 Socket
和传统的 Input/OutputStreams、本地的 ByteBuffers
或字节数组、未来的异步 I/O 模型等等。
从较高的层次上看,SSLEngine
表示为如下:
app data | ^ | | | v | | +----+-----|-----+----+ | | | | SSL|Engine | wrap() | | | unwrap() | OUTBOUND | INBOUND | | | | +----+-----|-----+----+ | | ^ | | | v | net data应用程序数据(也称为纯文本或明文)是应用程序生成或使用的数据。它的对应物是网络数据,它由握手和/或密文(加密的)数据组成,并且注定要通过 I/O 机制传输。入站数据指的是从同位体接收的数据,出站数据是去往同位体的数据。
(在 SSLEngine
上下文中,术语“握手数据”的意思是所有用来建立和控制安全连接的已交换数据。握手数据包括 SSL/TLS 消息 "alert"、"change_cipher_spec" 和 "handshake。")
SSLEngine
有五种不同的阶段。
SSLEngine
,但尚未被使用。在此阶段,应用程序可以设置任何特定于 SSLEngine
的设置(启用密码套件、SSLEngine
应该以客户端还是服务器模式握手等等)。一旦握手开始,任何新的设置(除了客户端/服务器模式,见下文)将在下一次握手时才被使用。 SSLEngine
传输应用程序数据。出站的应用程序报文被加密并进行完整性保护,入站的报文进行相反的过程。 SSLEngine
配置设置都将在下一次握手时才被使用。 SSLEngine
,并且也应该在关闭底层传输机制之前发送/接收所有剩余的报文到同位体。一旦引擎关闭,它就是不可重用的:必须创建一个新的 SSLEngine
。 SSLContext
来调用
SSLContext.createSSLEngine()
即可创建
SSLEngine
。在第一次调用
wrap()
、
unwrap()
或
beginHandshake()
之前应该设置所有的配置参数。这些方法都能触发初始握手。
分别对出站或入站数据调用 wrap()
或 unwrap()
可使数据通过引擎。根据 SSLEngine
的状态,对 wrap()
的调用可能使用源缓冲区的应用程序数据,并且可能在目标缓冲区内生成网络数据。出站数据可以包含应用程序和/或握手数据。对 unwrap()
的调用将检查源缓冲区,如果数据是握手信息,则可能提前握手;如果数据是应用程序数据,则将其放入目标缓冲区中。底层 SSL/TLS 算法的状态将决定何时使用和生成数据。
调用 wrap()
和 unwrap()
返回 SSLEngineResult
,它指示操作的状态以及(可选的)如何与引擎交互以取得进展。
SSLEngine
只是生成/使用完整的 SSL/TLS 包,它不在 wrap()/unwrap()
的调用之间内部存储应用程序数据。因此输入和输出 ByteBuffer
必须设为合适的大小以便容纳可能生成的最多记录。要确定合适的缓冲区大小,应该调用 SSLSession.getPacketBufferSize()
和 SSLSession.getApplicationBufferSize()
。通常无需考虑出站应用程序数据缓冲区的大小。如果缓冲区的状况不允许正确地使用/生成数据,则应用程序必须加以确定(通过 SSLEngineResult
)并纠正问题,然后再尝试调用一次。
例如,如果引擎确定没有足够的目标缓冲区空间可用,则 unwrap()
将返回 SSLEngineResult.Status.BUFFER_OVERFLOW
结果。应用程序应该调用 SSLSession.getApplicationBufferSize()
并将该值与目标缓冲区中的可用空间进行比较,如有必要,可以扩大该缓冲区。类似地,如果 unwrap()
返回 SSLEngineResult.Status.BUFFER_UNDERFLOW
,则应用程序应调用 SSLSession.getPacketBufferSize()
以确保源缓冲区有足够的空间来容纳记录(如有必要,可以扩大该缓冲区),然后获得更多的入站数据。
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. }
与 SSLSocket
不同,SSLEngine 的所有方法都是非阻塞的。SSLEngine
实现所需的任务结果可能需要经过很长的时间才能获得,甚至可能被阻塞。例如,TrustManager 可能需要连接到远程的证书验证服务,或者 KeyManager 可能需要提示用户来确定将哪个证书用作客户端验证部分。另外,创建加密的签名和验证它们可能很慢,就好像阻塞一样。
对于任何可能阻塞的操作,SSLEngine
将创建 Runnable
委托任务。当 SSLEngineResult
指示需要一个委托任务结果时,应用程序必须调用 getDelegatedTask()
来获取一个未执行的委托任务并调用其 run()
方法(根据计算策略的不同,可能要使用不同的线程)。应用程序应该继续获取委托任务,直到没有为止,然后再尝试执行最初的操作。
在通信会话的结尾,应用程序应该正确地关闭 SSL/TLS 链接。SSL/TLS 协议有关闭握手报文,这些报文应该在释放 SSLEngine
并关闭底层传输机制之前传送到同位体。通过以下某个方法可发起关闭:SSLException、入站的关闭握手报文或某种 close 方法。在所有情况下,关闭握手报文由引擎生成,在 SSLEngineResult
的状态返回 "CLOSED",或者 isOutboundDone()
返回 true 之前应该重复调用 wrap()
。从 wrap()
方法获得的所有数据都应该发送到同位体。
使用 closeOutbound()
通知引擎该应用程序将不再发送任何其他数据了。
同位体通过发送它自己的关闭握手报文来通知它的关闭意图。在此报文被本地 SSLEngine
的 unwrap()
调用接收和处理后,应用程序可检测关闭,方法是调用 unwrap()
并寻找带有状态 "CLOSED" 的 SSLEngineResult
,或者是 isInboundDone()
返回 true。如果由于某些原因使同位体关闭通信链接但没有发送正确的 SSL/TLS 关闭报文,则应用程序可以检测流的末尾并且能够通过 closeInbound()
来通知引擎没有更多的入站报文需要处理。某些应用程序可能选择要求同位体传送有序的关闭报文,在这种情况下,它们能够检查出关闭是由握手报文生成的,而不是由流的末尾这种情况引起的。
有两组密码套件是您在管理密码套件时需要知道的:
getSupportedCipherSuites()
来报告此列表。 setEnabledCipherSuites(String [])
方法设置此组套件,使用 getEnabledCipherSuites()
方法查询它们。最初,在一个新引擎上启用默认的密码套件集,表示最低的建议配置。 每个 SSL/TLS 连接都必须有一个客户端和一个服务器,因此每一端必须决定担任哪种角色。这种选择决定了谁来开始握手过程以及每一方应该发送哪种类型的报文。方法 setUseClientMode(boolean)
可配置模式。一旦初始握手开始,SSLEngine
就不能在客户端和服务器模式间转换,甚至在执行重新协商时也不能。
应用程序可能选择在不同的线程中处理委托任务。当创建一个 SSLEngine
时,当前的 AccessControlContext
被保存。所有以后的委托任务都将使用此上下文来处理:也就是说,所有的访问控制决定都使用创建引擎时所捕获的上下文来做出。
wrap()
和 unwrap()
方法可以互相并发执行。 例如:
synchronized (outboundLock) { sslEngine.wrap(src, dst); outboundQueue.put(dst); }做为必然的结果,两个线程必须不能试图并发地调用相同的方法(
wrap()
或 unwrap()
),因为没有方法保证最后包的排序。
SSLContext
,
SSLSocket
,
SSLServerSocket
,
SSLSession
,
Socket
构造方法摘要 | |
---|---|
protected |
SSLEngine() SSLEngine 的构造方法,该构造方法不为内部会话重用策略提供提示。 |
protected |
SSLEngine(String peerHost, int peerPort) SSLEngine 的构造方法。 |
方法摘要 | |
---|---|
abstract void |
beginHandshake() 在此 SSLEngine 上发起握手(初始的或重新协商)。 |
abstract void |
closeInbound() 通知没有更多的入站网络数据将发送到此 SSLEngine 。 |
abstract void |
closeOutbound() 通知没有更多的出站应用程序数据将在此 SSLEngine 上发送。 |
abstract Runnable |
getDelegatedTask() 返回此 SSLEngine 的一个委托 Runnable 任务。 |
abstract String[] |
getEnabledCipherSuites() 返回为了在此引擎上使用而当前已启用的 SSL 密码套件名称。 |
abstract String[] |
getEnabledProtocols() 返回当前已启用的,以便让 SSLEngine 使用的协议版本的名称。 |
abstract boolean |
getEnableSessionCreation() 如果可以由此引擎建立新的 SSL 会话,则返回 true。 |
abstract SSLEngineResult.HandshakeStatus |
getHandshakeStatus() 返回此 SSLEngine 的当前握手状态。 |
abstract boolean |
getNeedClientAuth() 如果引擎要求 客户端验证,则返回 true。 |
String |
getPeerHost() 返回同位体的主机名。 |
int |
getPeerPort() 返回同位体的端口号。 |
abstract SSLSession |
getSession() 返回此 SSLEngine 中使用的 SSLSession 。 |
SSLParameters |
getSSLParameters() 返回对此 SSLEngine 有效的 SSLParameters。 |
abstract String[] |
getSupportedCipherSuites() 返回为了在此引擎上使用而可以启用的密码套件名称。 |
abstract String[] |
getSupportedProtocols() 返回能够被启用,以便让 SSLEngine 可使用的协议的名称。 |
abstract boolean |
getUseClientMode() 如果引擎设置为在握手时使用客户端模式,则返回 true。 |
abstract boolean |
getWantClientAuth() 如果引擎请求 客户端验证,则返回 true。 |
abstract boolean |
isInboundDone() 返回 unwrap(ByteBuffer, ByteBuffer) 是否再接受更多的入站数据报文。 |
abstract boolean |
isOutboundDone() 返回 wrap(ByteBuffer, ByteBuffer) 是否再生成更多的出站数据报文。 |
abstract void |
setEnabledCipherSuites(String[] suites) 设置密码套件为启用,以便在此引擎上使用。 |
abstract void |
setEnabledProtocols(String[] protocols) 设置为了在此引擎上使用而启用的协议版本。 |
abstract void |
setEnableSessionCreation(boolean flag) 控制是否可以由此引擎建立新的 SSL 会话。 |
abstract void |
setNeedClientAuth(boolean need) 配置引擎要求 客户端进行验证。 |
void |
setSSLParameters(SSLParameters params) 对此引擎应用 SSLParameters。 |
abstract void |
setUseClientMode(boolean mode) 配置引擎在握手时使用客户端(或服务器)模式。 |
abstract void |
setWantClientAuth(boolean want) 配置引擎请求 客户端验证。 |
SSLEngineResult |
unwrap(ByteBuffer src, ByteBuffer dst) 尝试把 SSL/TLS 网络数据解码到纯文本应用程序数据缓冲区中。 |
SSLEngineResult |
unwrap(ByteBuffer src, ByteBuffer[] dsts) 尝试把 SSL/TLS 网络数据解码到纯文本应用程序数据缓冲区序列中。 |
abstract SSLEngineResult |
unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) 尝试把 SSL/TLS 网络数据解码到纯文本应用程序数据缓冲区子序列中。 |
SSLEngineResult |
wrap(ByteBuffer[] srcs, ByteBuffer dst) 尝试把数据缓冲区序列中的纯文本字节编码成 SSL/TLS 网络数据。 |
abstract SSLEngineResult |
wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) 尝试把数据缓冲区字序列中的纯文本字节编码成 SSL/TLS 网络数据。 |
SSLEngineResult |
wrap(ByteBuffer src, ByteBuffer dst) 试图把缓冲区的纯文本应用程序数据编码成 SSL/TLS 网络数据。 |
从类 java.lang.Object 继承的方法 |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
构造方法详细信息 |
---|
protected SSLEngine()
SSLEngine
的构造方法,该构造方法不为内部会话重用策略提供提示。
protected SSLEngine(String peerHost, int peerPort)
SSLEngine
的构造方法。
SSLEngine
实现可以使用 peerHost
和 peerPort
参数作为其内部会话重用策略的提示。
某些密码套件(例如 Kerberos)需要远程主机名信息。为了使用 Kerberos,此类的实现应该使用这种构造方法。
该参数不由 SSLEngine
进行验证。
peerHost
- 同位体主机的名称
peerPort
- 同位体的端口号
SSLContext.createSSLEngine(String, int)
,
SSLSessionContext
方法详细信息 |
---|
public String getPeerHost()
注意,该值未经验证,不应该依赖它。
public int getPeerPort()
注意,该值未经验证,不应该依赖它。
public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException
调用此方法与调用下面的方法产生的行为完全相同:
engine.wrap(new ByteBuffer [] { src }, 0, 1, dst);
src
- 包含出站应用程序数据的
ByteBuffer
dst
- 保存出站网络数据的
ByteBuffer
SSLEngineResult
。
SSLException
- 如果处理数据时遇到的问题导致
SSLEngine
中止。关于引擎关闭的更多信息,请参见该类的描述。
ReadOnlyBufferException
- 如果
dst
缓冲区是只读的。
IllegalArgumentException
- 如果
src
或
dst
为 null。
IllegalStateException
- 如果没有设置客户端/服务器模式。
wrap(ByteBuffer [], int, int, ByteBuffer)
public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) throws SSLException
调用此方法与调用下面的方法产生的行为完全相同:
engine.wrap(srcs, 0, srcs.length, dst);
srcs
- 包含出站应用程序数据的
ByteBuffers
数组
dst
- 保存出站网络数据的
ByteBuffer
SSLEngineResult
。
SSLException
- 如果处理数据时遇到的问题导致
SSLEngine
中止。关于引擎关闭的更多信息,请参见该类的描述。
ReadOnlyBufferException
- 如果
dst
缓冲区是只读的。
IllegalArgumentException
- 如果
srcs
或
dst
为 null,或者
srcs
中的任何元素为 null。
IllegalStateException
- 如果没有设置客户端/服务器模式。
wrap(ByteBuffer [], int, int, ByteBuffer)
public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) throws SSLException
GatheringByteChannel
,有关子序列行为的更多信息,请参见
GatheringByteChannel.write(ByteBuffer[], int, int)
。
根据 SSLEngine 的状态,此方法可能生成网络数据而不使用任何应用程序数据(例如,它可能生成握手数据)。
应用程序负责把网络数据可靠地传输到同位体,同时也保证将多次调用 wrap() 所创建的数据以和它生成时相同的顺序传输。应用程序必须正确地同步对此方法的多次调用。
如果此 SSLEngine
还没有开始它的初始握手,则此方法将自动开始进行握手。
此方法将尝试生成一个 SSL/TLS 包,并使用尽可能多的源数据,但是使用的数据将从不会超过每个缓冲区中剩余字节的总和。会更新每个 ByteBuffer
的位置,以反映使用或生成的数据量。界限保持不变。
由 srcs
和 dst ByteBuffer
使用的底层内存必须不能相同。
关于引擎关闭的更多信息,请参见该类的描述。
srcs
- 包含出站应用程序数据的
ByteBuffers
数组
offset
- 第一个缓冲区(要获取该缓冲区中的字节)在缓冲区数组中的偏移量;必须为非负数并且不能大于
srcs.length
length
- 要访问的最大缓冲区编号;它必须为非负和不能大于
srcs.length
-
offset
dst
- 保存出站网络数据的
ByteBuffer
SSLEngineResult
。
SSLException
- 如果处理数据时遇到的问题导致
SSLEngine
中止。关于引擎关闭的更多信息,请参见该类的描述。
IndexOutOfBoundsException
- 如果关于
offset
和
length
参数的前提不成立。
ReadOnlyBufferException
- 如果
dst
缓冲区是只读的。
IllegalArgumentException
- 如果
srcs
或
dst
为 null, 或者指定的
srcs
子序列中的任何元素为 null。
IllegalStateException
- 如果没有设置客户端/服务器模式。
GatheringByteChannel
,
GatheringByteChannel.write( ByteBuffer[], int, int)
public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException
调用此方法与调用下面的方法产生的行为完全相同:
engine.unwrap(src, new ByteBuffer [] { dst }, 0, 1);
src
- 包含入站网络数据的
ByteBuffer
。
dst
- 保存入站应用程序数据的
ByteBuffer
。
SSLEngineResult
。
SSLException
- 如果处理数据时遇到的问题导致
SSLEngine
中止。关于引擎关闭的更多信息,请参见该类的描述。
ReadOnlyBufferException
- 如果
dst
缓冲区是只读的。
IllegalArgumentException
- 如果
src
或
dst
为 null。
IllegalStateException
- 如果没有设置客户端/服务器模式。
unwrap(ByteBuffer, ByteBuffer [], int, int)
public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException
调用此方法与调用下面的方法产生的行为完全相同:
engine.unwrap(src, dsts, 0, dsts.length);
src
- 包含入站网络数据的
ByteBuffer
。
dsts
- 保存入站应用程序数据的
ByteBuffer
数组。
SSLEngineResult
。
SSLException
- 如果处理数据时遇到的问题导致
SSLEngine
中止。关于引擎关闭的更多信息,请参见该类的描述。
ReadOnlyBufferException
- 如果任何一个
dst
缓冲区是只读的。
IllegalArgumentException
- 如果
src
或
dsts
为 null,或者
dsts
中的任何元素为 null。
IllegalStateException
- 如果没有设置客户端/服务器模式。
unwrap(ByteBuffer, ByteBuffer [], int, int)
public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws SSLException
ScatteringByteChannel
,有关子序列行为的更多信息,请参见 {
ScatteringByteChannel.read(ByteBuffer[], int, int)
。
根据 SSLEngine 的状态,此方法可能使用网络数据而不生成任何应用程序数据(例如,它可能使用握手数据)。
应用程序负责从同位体可靠地获取网络数据,并且负责以数据接收时的顺序对数据调用 unwrap()。应用程序必须正确地同步对此方法的多次调用。
如果此 SSLEngine
还没有开始它的初始握手,则此方法将自动开始进行握手。
此方法将尝试使用一个完整的 SSL/TLS 网络包,但使用的数据从不超过缓冲区中剩余字节的总和。会更新每个 ByteBuffer
的位置,以反映使用或生成的数据量。界限保持不变。
由 src
和 dsts ByteBuffer
使用的底层内存必须不能相同。
由于入站网络缓冲区可能由于调用而被修改:因此,如果由于某些次要目的而需要网络数据包,则在调用此方法前应该复制该数据。注:网络数据对第二个 SSLEngine 将不再有用,因为每个 SSLEngine 包含唯一的随机状态,该状态影响 SSL/TLS 报文。
关于引擎关闭的更多信息,请参见该类的描述。
src
- 包含入站网络数据的
ByteBuffer
。
dsts
- 保存入站应用程序数据的
ByteBuffer
数组。
offset
- 第一个缓冲区(要传递该缓冲区中的字节)在缓冲区数组中的偏移量;必须为非负数并且不能大于
dsts.length
。
length
- 要访问的最大缓冲区数;必须为非负数并且不能大于
dsts.length
-
offset
。
SSLEngineResult
。
SSLException
- 如果处理数据时遇到的问题导致
SSLEngine
中止。关于引擎关闭的更多信息,请参见该类的描述。
IndexOutOfBoundsException
- 如果关于
offset
和
length
参数的前提不成立。
ReadOnlyBufferException
- 如果任何一个
dst
缓冲区是只读的。
IllegalArgumentException
- 如果
src
或
dsts
为 null,或者指定的
dsts
子序列中的任何元素为 null。
IllegalStateException
- 如果没有设置客户端/服务器模式。
ScatteringByteChannel
,
ScatteringByteChannel.read( ByteBuffer[], int, int)
public abstract Runnable getDelegatedTask()
SSLEngine
的一个委托
Runnable
任务。
SSLEngine
操作可能需要阻塞操作的结果,或者可能花费很长的时间才能完成。此方法用来获取一个未完成的 Runnable
操作(任务)。必须分配给每个任务一个线程(可能是当前的)来执行 run
操作。一旦 run
方法返回,则不再需要 Runnable
对象,它可能被丢弃。
当创建此对象时,委托任务在 AccessControlContext
中的适当位置运行。
对此方法的一次调用恰好返回每个未完成的任务一次。
多个委托任务可以并行运行。
Runnable
任务;如果没有则返回 null。
public abstract void closeInbound() throws SSLException
SSLEngine
。
如果应用程序通过调用 closeOutbound()
发起关闭进程,则在某些情况下不要求发起方等待同位体的相应关闭报文。(有关等待关闭报警的更改信息,请参见 TLS 规范的 7.2.1 节 (RFC 2246)。)在这种情况下,不需要调用此方法。
但是如果应用程序没有发起关闭进程,如果上面的情况不适用,则每当到达 SSL/TLS 数据流的末尾时就应该调用此方法。这保证了入站端的关闭,也检查该同位体是否正确地遵循 SSL/TLS 关闭过程,从而检测可能的截断攻击。
此方法是幂等的:如果入站端已经关闭,则此方法不执行任何操作。
应该调用 wrap()
来刷新任何剩余的握手数据。
SSLException
- 如果此引擎没有从同位体接收到正确的 SSL/TLS 关闭通知报文。