Module java.base
Package javax.net.ssl

Class SSLEngine

java.lang.Object
javax.net.ssl.SSLEngine

public abstract class SSLEngine extends Object
一个类,使用诸如安全套接字层(SSL)或 IETF RFC 2246“传输层安全性”(TLS)协议等协议实现安全通信,但是与传输无关。

安全通信模式包括:

  • 完整性保护。SSL/TLS/DTLS可防止主动窃听者修改消息。
  • 身份验证。在大多数模式中,SSL/TLS/DTLS提供对等方身份验证。通常服务器会被验证,客户端可能会根据服务器的要求进行验证。
  • 保密性(隐私保护)。在大多数模式中,SSL/TLS/DTLS加密在客户端和服务器之间发送的数据。这保护了数据的机密性,使得被动窃听者无法看到敏感数据,如金融信息或各种个人信息。
这些保护类型由“密码套件”指定,它是给定SSL连接使用的加密算法的组合。在协商过程中,两个端点必须就两个环境中都可用的密码套件达成一致。如果没有共同的套件,就无法建立SSL连接,也无法交换数据。

使用的密码套件是通过称为“握手”的协商过程建立的。此过程的目标是创建或重新加入一个“会话”,该会话可以随时间保护许多连接。握手完成后,您可以通过使用getSession()方法访问会话属性。

SSLSocket类提供了大部分相同的安全功能,但所有传入和传出的数据都会自动使用底层Socket进行传输,该设计使用阻塞模型。虽然这对许多应用程序是合适的,但这种模型无法提供大型服务器所需的可伸缩性。

SSLEngine的主要区别在于它在传入和传出的字节流上运行,独立于传输机制。SSLEngine用户有责任安排可靠的I/O传输到对等方。通过将SSL/TLS/DTLS抽象与I/O传输机制分离,SSLEngine可用于各种I/O类型,如非阻塞I/O(轮询)可选择的非阻塞I/OSocket和传统的Input/OutputStreams、本地ByteBuffers或字节数组、未来的异步I/O模型等。

在高层次上,SSLEngine如下:

                   应用数据

                |           ^
                |     |     |
                v     |     |
           +----+-----|-----+----+
           |          |          |
           |       SSL|Engine    |
   wrap()  |          |          |  unwrap()
           | OUTBOUND | INBOUND  |
           |          |          |
           +----+-----|-----+----+
                |     |     ^
                |     |     |
                v           |

                   网络数据
 
应用数据(也称为明文)是应用程序生成或消耗的数据。它的对应物是网络数据,它包括握手和/或密文(加密)数据,并且将通过I/O机制进行传输。传入数据是从对等方接收的数据,传出数据是发送到对等方的数据。

(在SSLEngine的上下文中,“握手数据”一词指任何用于建立和控制安全连接的数据。握手数据包括SSL/TLS/DTLS消息“alert”、“change_cipher_spec”和“handshake”)。

SSLEngine有五个明显的阶段。

  1. 创建 - SSLEngine已创建并初始化,但尚未使用。在此阶段,应用程序可以设置任何SSLEngine特定的设置(启用的密码套件,SSLEngine是否应在客户端或服务器模式下进行握手等)。但一旦握手开始,除了客户端/服务器模式之外,任何新设置(参见下文)都将用于下一次握手。
  2. 初始握手 - 初始握手是两个对等方交换通信参数直到建立SSLSession的过程。在此阶段无法发送应用程序数据。
  3. 应用程序数据 - 一旦建立通信参数并完成握手,应用程序数据可以通过SSLEngine流动。传出应用程序消息被加密和完整性保护,传入消息则反向进行。
  4. 重新握手 - 在应用程序数据阶段,任一方都可以随时请求重新协商会话。新的握手数据可以与应用程序数据混合。在开始重新握手阶段之前,应用程序可以重置SSL/TLS/DTLS通信参数,如启用的密码套件列表和是否使用客户端身份验证,但不能在客户端/服务器模式之间更改。与之前一样,一旦握手开始,任何新的SSLEngine配置设置将不会在下一次握手之前使用。
  5. 关闭 - 当连接不再需要时,客户端和服务器应用程序应分别关闭各自连接的两侧。对于SSLEngine对象,应用程序应调用closeOutbound()并将任何剩余消息发送给对等方。同样,应用程序应在调用closeInbound()之前接收来自对等方的任何剩余消息。在SSLEngine的两侧关闭后,底层传输机制可以关闭。如果连接未按顺序关闭(例如在接收对等方的写入关闭通知之前调用closeInbound()),将引发异常以指示发生错误。一旦引擎关闭,它就不可重用:必须创建一个新的SSLEngine
通过从已初始化的SSLContext调用SSLContext.createSSLEngine()来创建SSLEngine。在首次调用wrap()unwrap()beginHandshake()之前,应设置任何配置参数。这些方法都会触发初始握手。

数据通过在传出或传入数据上调用wrap()unwrap()来通过引擎移动。根据SSLEngine的状态,wrap()调用可能会从源缓冲区中消耗应用程序数据,并在目标缓冲区中生成网络数据。传出数据可能包含应用程序和/或握手数据。对unwrap()的调用将检查源缓冲区,如果数据是握手信息,则可能推进握手,或者如果数据是应用程序,则可能将应用程序数据放入目标缓冲区。底层SSL/TLS/DTLS算法的状态将决定何时消耗和生成数据。

wrap()unwrap()的调用将返回一个SSLEngineResult,指示操作的状态,以及(可选)如何与引擎交互以取得进展。

SSLEngine仅生成/消耗完整的SSL/TLS/DTLS数据包,并且在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()) {
   case BUFFER_OVERFLOW:
       // 可尝试清空dst缓冲区中已获取的任何数据,但我们将其增加到所需大小。
       int appSize = engine.getSession().getApplicationBufferSize();
       ByteBuffer b = ByteBuffer.allocate(appSize + dst.position());
       dst.flip();
       b.put(dst);
       dst = b;
       // 重试操作。
       break;
   case BUFFER_UNDERFLOW:
       int netSize = engine.getSession().getPacketBufferSize();
       // 如果需要,调整缓冲区大小。
       if (netSize > src.capacity()) {
           ByteBuffer b = ByteBuffer.allocate(netSize);
           src.flip();
           b.put(src);
           src = b;
       }
       // 获取更多传入网络数据到src,
       // 然后重试操作。
       break;
   // 其他情况:CLOSED,OK。
   }
 

SSLSocket不同,SSLEngine的所有方法都是非阻塞的。SSLEngine实现可能需要完成可能需要较长时间的任务的结果,或者甚至可能会阻塞。例如,TrustManager可能需要连接到远程证书验证服务,或者KeyManager可能需要提示用户确定要用作客户端身份验证的证书。此外,创建加密签名并验证它们可能会很慢,看起来会阻塞。

对于可能会阻塞的任何操作,SSLEngine将创建一个委托任务Runnable。当SSLEngineResult指示需要委托任务结果时,应用程序必须调用getDelegatedTask()来获取一个未完成的委托任务,并调用其run()方法(可能使用不同的线程,取决于计算策略)。应用程序应继续获取委托任务,直到不再存在为止,并尝试重新执行原始操作。

在通信会话结束时,应用程序应正确关闭SSL/TLS/DTLS链接。SSL/TLS/DTLS协议具有关闭握手消息,这些消息应在释放SSLEngine并关闭底层传输机制之前与对等方通信。关闭可以由以下之一发起:SSLException、入站关闭握手消息或关闭方法之一。在所有情况下,关闭握手消息由引擎生成,应重复调用wrap()直到生成的SSLEngineResult的状态返回"CLOSED",或者isOutboundDone()返回true。应将从wrap()方法获取的所有数据发送给对等方。

closeOutbound()用于通知引擎应用程序将不再发送任何数据。

对等方将通过发送自己的关闭握手消息来表示其关闭意图。在本地SSLEngineunwrap()调用接收并处理此消息后,应用程序可以通过调用unwrap()并查找状态为"CLOSED"的SSLEngineResult,或者如果isInboundDone()返回true来检测关闭。如果由于某种原因对等方在不发送适当的SSL/TLS/DTLS关闭消息的情况下关闭通信链接,应用程序可以检测到流的结束,并可以通过closeInbound()向引擎发出信号,表示不再有入站消息要处理。某些应用程序可能选择要求对等方发送有序关闭消息,在这种情况下,它们可以检查关闭是否由握手消息生成,而不是由流结束条件生成。

在管理密码套件时,您需要了解两组密码套件:

实现默认要求默认情况下仅启用对服务器进行身份验证并提供机密性的密码套件。仅当双方明确同意进行未经身份验证和/或非私密(未加密)通信时,才会选择此类密码套件。

每个SSL/TLS/DTLS连接必须有一个客户端和一个服务器,因此每个端点必须决定承担哪种角色。此选择确定由哪一方开始握手过程以及每个方发送哪种类型的消息。方法setUseClientMode(boolean)配置模式。请注意,新SSLEngine的默认模式是特定于提供程序的。应用程序应在调用SSLEngine的其他方法之前明确设置模式。一旦初始握手已开始,SSLEngine无法在客户端和服务器模式之间切换,即使在执行重新协商时也是如此。

此类方法返回的ApplicationProtocol String值是由对等方发送的网络字节表示。这些字节可以直接进行比较,也可以转换为其Unicode String格式进行比较。

     String networkString = sslEngine.getHandshakeApplicationProtocol();
     byte[] bytes = networkString.getBytes(StandardCharsets.ISO_8859_1);

     //
     // 使用字节进行匹配:
     //
     //   "http/1.1"                       (7位ASCII值在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) {
        ...
     }

     //
     // 或者,如果我们知道ALPN值是使用特定字符集(例如UTF-8)从String编码的,则使用string.equals()进行匹配。
     // 在使用之前,ALPN值必须首先正确解码为Unicode String。
     //
     String unicodeString = new String(bytes, StandardCharsets.UTF_8);
     if (unicodeString.equals(HTTP1_1)
             || unicodeString.equals("\uabcd\uabce\uabcf")) {
         ...
     }
 

应用程序可能选择在不同的线程中处理委托任务。创建SSLEngine时,将保存当前的AccessControlContext。将来的所有委托任务将使用此上下文进行处理:也就是说,所有访问控制决策将使用在引擎创建时捕获的上下文进行。


并发注意事项:有两个并发问题需要注意:
  1. wrap()unwrap()方法可能同时执行。
  2. SSL/TLS/DTLS协议使用有序数据包。应用程序必须确保生成的数据包按顺序传递。如果数据包到达顺序不正确,可能会导致意外或致命的结果。

    例如:

                  synchronized (outboundLock) {
                      sslEngine.wrap(src, dst);
                      outboundQueue.put(dst);
                  }
          
    作为推论,两个线程不得同时尝试调用相同的方法(wrap()unwrap()),因为无法保证最终数据包的顺序。
自1.5版本开始:
1.5
参见:
  • Constructor Details

    • SSLEngine

      protected SSLEngine()
      SSLEngine构造一个不提供内部会话重用策略的实例。
      参见:
    • SSLEngine

      protected SSLEngine(String peerHost, int peerPort)
      SSLEngine构造一个实例。

      SSLEngine实现可以使用peerHostpeerPort参数作为其内部会话重用策略的提示。

      某些密码套件(如Kerberos)需要远程主机名信息。此类的实现应使用此构造函数来使用Kerberos。

      这些参数未经SSLEngine身份验证。

      参数:
      peerHost - 对等方主机的名称
      peerPort - 对等方的端口号
      参见:
  • Method Details

    • getPeerHost

      public String getPeerHost()
      返回对等方的主机名。

      请注意,该值未经身份验证,不应依赖于它。

      返回:
      对等方的主机名,如果没有可用,则返回null。
    • getPeerPort

      public int getPeerPort()
      返回对等方的端口号。

      请注意,该值未经身份验证,不应依赖于它。

      返回:
      对等方的端口号,如果没有可用,则返回-1。
    • wrap

      public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException
      尝试将明文应用程序数据缓冲区的缓冲区编码为SSL/TLS/DTLS网络数据。

      此方法的调用行为与调用相同:

       engine.wrap(new ByteBuffer[] { src }, 0, 1, dst);
       
      参数:
      src - 包含出站应用程序数据的ByteBuffer
      dst - 用于保存出站网络数据的ByteBuffer
      返回:
      描述此操作结果的SSLEngineResult
      抛出:
      SSLException - 处理导致SSLEngine中止的数据时遇到问题。有关引擎关闭的更多信息,请参见类描述。
      ReadOnlyBufferException - 如果dst缓冲区是只读的。
      IllegalArgumentException - 如果srcdst为null。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • wrap

      public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) throws SSLException
      尝试将来自数据缓冲区序列的明文字节编码为SSL/TLS/DTLS网络数据。

      此方法的调用行为与调用相同:

       engine.wrap(srcs, 0, srcs.length, dst);
       
      参数:
      srcs - 包含出站应用程序数据的ByteBuffers数组
      dst - 用于保存出站网络数据的ByteBuffer
      返回:
      描述此操作结果的SSLEngineResult
      抛出:
      SSLException - 处理导致SSLEngine中止的数据时遇到问题。有关引擎关闭的更多信息,请参见类描述。
      ReadOnlyBufferException - 如果dst缓冲区是只读的。
      IllegalArgumentException - 如果srcsdst为null,或者srcs中的任何元素为null。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • wrap

      public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) throws SSLException
      尝试将数据缓冲区的子序列中的明文字节编码为SSL/TLS/DTLS网络数据。这种“聚合”操作在单次调用中对给定序列的一个或多个缓冲区中的字节序列进行编码。聚合包装在实现网络协议或文件格式时通常很有用,例如将数据分组为一个或多个固定长度头部后跟可变长度主体的段。有关聚合的更多信息,请参见GatheringByteChannel,有关子序列行为的更多信息,请参见GatheringByteChannel.write(ByteBuffer[], int, int)

      根据SSLEngine的状态,此方法可能会生成网络数据而不消耗任何应用程序数据(例如,可能会生成握手数据)。

      应用程序负责可靠地将网络数据传输到对等方,并确保由多次wrap()调用生成的数据以生成顺序传输。应用程序必须正确同步对此方法的多次调用。

      如果此SSLEngine尚未启动其初始握手,则此方法将自动启动握手。

      此方法将尝试生成SSL/TLS/DTLS记录,并尽可能消耗尽可能多的源数据,但永远不会消耗超过每个缓冲区中剩余字节的总和。每个ByteBuffer的位置将更新以反映消耗或生成的数据量。限制保持不变。

      srcsdst ByteBuffer使用的底层内存不能相同。

      有关引擎关闭的更多信息,请参见类描述。

      参数:
      srcs - 包含出站应用程序数据的ByteBuffers数组
      offset - 要从中检索字节的第一个缓冲区在缓冲区数组中的偏移量;必须是非负的且不大于srcs.length
      length - 要访问的最大缓冲区数;必须是非负的且不大于srcs.length - offset
      dst - 用于保存出站网络数据的ByteBuffer
      返回:
      描述此操作结果的SSLEngineResult
      抛出:
      SSLException - 在处理导致SSLEngine中止的数据时遇到问题。有关引擎关闭的更多信息,请参见类描述。
      IndexOutOfBoundsException - 如果offsetlength参数上的先决条件不成立。
      ReadOnlyBufferException - 如果dst缓冲区是只读的。
      IllegalArgumentException - 如果srcsdst为null,或者指定的srcs子序列中的任何元素为null。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • unwrap

      public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException
      尝试将SSL/TLS/DTLS网络数据解码为明文应用程序数据缓冲区。

      此方法的调用行为与调用相同。

       engine.unwrap(src, new ByteBuffer[] { dst }, 0, 1);
       
      参数:
      src - 包含入站网络数据的ByteBuffer
      dst - 用于保存入站应用程序数据的ByteBuffer
      返回:
      描述此操作结果的SSLEngineResult
      抛出:
      SSLException - 在处理导致SSLEngine中止的数据时遇到问题。有关引擎关闭的更多信息,请参见类描述。
      ReadOnlyBufferException - 如果dst缓冲区是只读的。
      IllegalArgumentException - 如果srcdst为null。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • unwrap

      public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException
      尝试将SSL/TLS/DTLS网络数据解码为一系列明文应用程序数据缓冲区。

      此方法的调用行为与调用相同。

       engine.unwrap(src, dsts, 0, dsts.length);
       
      参数:
      src - 包含入站网络数据的ByteBuffer
      dsts - 用于保存入站应用程序数据的ByteBuffer数组。
      返回:
      描述此操作结果的SSLEngineResult
      抛出:
      SSLException - 在处理导致SSLEngine中止的数据时遇到问题。有关引擎关闭的更多信息,请参见类描述。
      ReadOnlyBufferException - 如果任何dst缓冲区是只读的。
      IllegalArgumentException - 如果srcdsts为null,或者dsts中的任何元素为null。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • unwrap

      public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws SSLException
      尝试将SSL/TLS/DTLS网络数据解码为一系列明文应用程序数据缓冲区的子序列。这种“分散”操作在单次调用中将一个字节序列解码为给定序列中的一个或多个缓冲区。分散解包在实现网络协议或文件格式时通常很有用,例如将数据分组为一个或多个固定长度头部后跟可变长度主体的段。有关分散的更多信息,请参见ScatteringByteChannel,有关子序列行为的更多信息,请参见ScatteringByteChannel.read(ByteBuffer[], int, int)

      根据SSLEngine的状态,此方法可能会在不生成任何应用程序数据的情况下消耗网络数据(例如,可能会消耗握手数据)。

      应用程序负责可靠地从对等方获取网络数据,并按接收顺序调用unwrap()。应用程序必须正确同步对此方法的多次调用。

      如果此SSLEngine尚未启动其初始握手,则此方法将自动启动握手。

      此方法将尝试消耗一个完整的SSL/TLS/DTLS网络数据包,但永远不会消耗超过缓冲区中剩余字节的总和。每个ByteBuffer的位置将更新以反映消耗或生成的数据量。限制保持不变。

      srcdsts ByteBuffer使用的底层内存不能相同。

      入站网络缓冲区src可能会在调用此方法后被修改:因此,如果网络数据包对于某些次要目的是必需的,则在调用此方法之前应复制数据。注意:网络数据对于第二个SSLEngine将没有用处,因为每个SSLEngine包含影响SSL/TLS/DTLS消息的唯一随机状态。

      有关引擎关闭的更多信息,请参见类描述。

      参数:
      src - 包含入站网络数据的ByteBuffer
      dsts - 用于保存入站应用程序数据的ByteBuffer数组。
      offset - 要从中传输字节的第一个缓冲区数组中的偏移量;必须是非负的且不大于dsts.length
      length - 要访问的缓冲区的最大数量;必须是非负的且不大于dsts.length - offset
      返回:
      描述此操作结果的SSLEngineResult
      抛出:
      SSLException - 在处理导致SSLEngine中止的数据时遇到问题。有关引擎关闭的更多信息,请参阅类描述。
      IndexOutOfBoundsException - 如果offsetlength参数上的先决条件不成立。
      ReadOnlyBufferException - 如果任何dst缓冲区是只读的。
      IllegalArgumentException - 如果srcdsts为null,或者指定的dsts子序列中的任何元素为null。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • getDelegatedTask

      public abstract Runnable getDelegatedTask()
      返回此SSLEngine的委托Runnable任务。

      SSLEngine操作可能需要阻塞操作的结果,或者可能需要较长时间才能完成。此方法用于获取一个未完成的Runnable操作(任务)。必须为每个任务分配一个线程(可能是当前线程)来执行run操作。一旦run方法返回,Runnable对象就不再需要,可以丢弃。

      委托任务在创建此对象时的AccessControlContext中运行。

      调用此方法将仅一次返回每个未完成的任务。

      可以并行运行多个委托任务。

      返回:
      一个委托的Runnable任务,如果没有可用的任务则返回null。
    • closeInbound

      public abstract void closeInbound() throws SSLException
      表示不会再发送更多入站网络数据到此SSLEngine

      如果应用程序通过调用closeOutbound()启动了关闭过程,在某些情况下,不需要等待发起方等待对等方的相应关闭消息。(有关等待关闭警报的更多信息,请参阅TLS规范(RFC 2246)第7.2.1节。)在这种情况下,不需要调用此方法。

      但是,如果应用程序没有启动关闭过程,或者上述情况不适用,则应在到达SSL/TLS/DTLS数据流的末尾时调用此方法。这确保关闭入站端,并检查对等方是否正确遵循SSL/TLS/DTLS关闭过程,从而检测可能的截断攻击。

      此方法是幂等的:如果入站端已关闭,则此方法不执行任何操作。

      应调用wrap()以刷新任何剩余的握手数据。

      抛出:
      SSLException - 如果此引擎未从对等方接收到正确的SSL/TLS/DTLS关闭通知消息。
      参见:
    • isInboundDone

      public abstract boolean isInboundDone()
      返回unwrap(ByteBuffer, ByteBuffer)是否接受更多入站数据消息。
      返回:
      如果SSLEngine不会消耗任何更多网络数据(因此也不会生成任何更多应用程序数据),则返回true。
      参见:
    • closeOutbound

      public abstract void closeOutbound()
      表示不会再发送更多出站应用程序数据到此SSLEngine

      此方法是幂等的:如果出站端已关闭,则此方法不执行任何操作。

      应调用wrap(ByteBuffer, ByteBuffer)以刷新任何剩余的握手数据。

      参见:
    • isOutboundDone

      public abstract boolean isOutboundDone()
      返回wrap(ByteBuffer, ByteBuffer)是否生成任何更多出站数据消息。

      请注意,在关闭阶段期间,SSLEngine可能生成必须发送给对等方的握手关闭数据。必须调用wrap()以生成此数据。当此方法返回true时,将不会创建更多的出站数据。

      返回:
      如果SSLEngine不会生成任何更多网络数据,则返回true。
      参见:
    • getSupportedCipherSuites

      public abstract String[] getSupportedCipherSuites()
      返回可在此引擎上启用的密码套件的名称。通常,默认情况下,只有这些密码套件的子集实际上会被启用,因为此列表可能包括不符合这些默认服务质量要求的密码套件。这些密码套件可能在专门的应用程序中有用。

      返回的数组包括来自Java安全标准算法名称规范的JSSE Cipher Suite Names部分中的标准密码套件名称,还可能包括提供程序支持的其他密码套件。

      返回:
      密码套件名称的数组
      参见:
    • getEnabledCipherSuites

      public abstract String[] getEnabledCipherSuites()
      返回当前在此引擎上启用的SSL密码套件的名称。当首次创建SSLEngine时,所有启用的密码套件都支持最低服务质量。因此,在某些环境中,此值可能为空。

      请注意,即使启用了密码套件,也可能永远不会使用。如果对等方不支持它,或者其使用受限制,或者没有支持该协议的启用密码套件的必需证书(和私钥),或者启用了匿名套件但需要身份验证。

      返回的数组包括来自Java安全标准算法名称规范的JSSE Cipher Suite Names部分中的标准密码套件名称,还可能包括提供程序支持的其他密码套件。

      返回:
      密码套件名称的数组
      参见:
    • setEnabledCipherSuites

      public abstract void setEnabledCipherSuites(String[] suites)
      设置在此引擎上启用的密码套件。

      参数suites中的每个密码套件必须已在getSupportedCipherSuites()中列出,否则该方法将失败。成功调用此方法后,只有suites参数中列出的套件才能用于使用。

      请注意,标准的密码套件名称列表可以在Java安全标准算法名称规范的JSSE Cipher Suite Names部分中找到。提供程序可能支持未在此列表中找到的密码套件名称,或者可能不使用某个特定密码套件的推荐名称。

      有关特定密码套件可能永远不会在引擎上使用的更多信息,请参见getEnabledCipherSuites()

      参数:
      suites - 要启用的所有密码套件的名称
      抛出:
      IllegalArgumentException - 当参数命名的一个或多个密码不受支持时,或者参数为null时。
      参见:
    • getSupportedProtocols

      public abstract String[] getSupportedProtocols()
      返回可用于此SSLEngine的协议的名称。
      返回:
      支持的协议的数组
    • getEnabledProtocols

      public abstract String[] getEnabledProtocols()
      返回当前可用于此SSLEngine的协议版本的名称。

      请注意,即使启用了协议,也可能永远不会使用。这可能发生,如果对等方不支持该协议,或者其使用受限,或者没有受协议支持的启用密码套件,等等。

      返回:
      协议的数组
      参见:
    • setEnabledProtocols

      public abstract void setEnabledProtocols(String[] protocols)
      设置此引擎可用的协议版本。

      协议必须已在getSupportedProtocols()中列出为受支持的。成功调用此方法后,只有在protocols参数中列出的协议才能被启用。

      参数:
      protocols - 要启用的所有协议的名称。
      抛出:
      IllegalArgumentException - 当参数命名的一个或多个协议不受支持或协议参数为null时。
      参见:
    • getSession

      public abstract SSLSession getSession()
      返回此SSLEngine中正在使用的SSLSession

      这些会长期存在,并且通常对应于某个用户的整个登录会话。会话指定了会话中所有连接正在主动使用的特定密码套件,以及会话的客户端和服务器的身份。

      SSLSocket.getSession()不同,此方法不会阻塞,直到握手完成。

      在初始握手完成之前,此方法返回一个会话对象,该对象报告一个无效的密码套件为"SSL_NULL_WITH_NULL_NULL"。

      返回:
      SSLEngineSSLSession
      参见:
    • getHandshakeSession

      public SSLSession getHandshakeSession()
      返回在SSL/TLS/DTLS握手期间正在构建的SSLSession

      TLS/DTLS协议可能会协商在使用此类的实例时需要的参数,但在SSLSession完全初始化并通过getSession可用之前。例如,有效签名算法列表可能会限制可以在TrustManager决策期间使用的证书类型,或者最大的TLS/DTLS片段数据包大小可以调整以更好地支持网络环境。

      此方法提供对正在构建的SSLSession的早期访问。根据握手的进展程度,某些数据可能尚不可用。例如,如果远程服务器将发送证书链,但该链尚未被处理,SSLSessiongetPeerCertificates方法将抛出SSLPeerUnverifiedException。一旦该链已被处理,getPeerCertificates将返回正确的值。

      返回:
      如果此实例当前未进行握手,或当前握手尚未进行到足以创建基本SSLSession的程度,则返回null。否则,此方法返回当前正在协商的SSLSession
      抛出:
      UnsupportedOperationException - 如果底层提供程序未实现该操作。
      自:
      1.7
      参见:
    • beginHandshake

      public abstract void beginHandshake() throws SSLException
      在此SSLEngine上启动握手(初始或重新协商)。

      对于初始握手,不需要此方法,因为如果握手尚未开始,wrap()unwrap()方法将隐式调用此方法。

      注意,对等方也可以通过发送适当的会话重新协商握手消息来请求与此SSLEngine的会话重新协商。

      SSLSocket#startHandshake()方法不同,此方法不会阻塞,直到握手完成。

      要强制进行完整的SSL/TLS/DTLS会话重新协商,应在调用此方法之前使当前会话无效。

      某些协议可能不支持在现有引擎上进行多次握手,并可能抛出SSLException

      抛出:
      SSLException - 如果在向SSLEngine发出开始新握手的信号时遇到问题。有关引擎关闭的更多信息,请参见类描述。
      IllegalStateException - 如果客户端/服务器模式尚未设置。
      参见:
    • getHandshakeStatus

      public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus()
      返回此SSLEngine的当前握手状态。
      返回:
      当前的SSLEngineResult.HandshakeStatus
    • setUseClientMode

      public abstract void setUseClientMode(boolean mode)
      在握手时配置引擎以使用客户端(或服务器)模式。

      必须在任何握手发生之前调用此方法。一旦握手开始,该模式在引擎的生命周期内将无法重置。

      服务器通常会对自身进行身份验证,而客户端则无需这样做。

      实现注意:
      JDK SunJSSE提供程序实现此模式的默认值为false。
      参数:
      mode - 如果引擎应在"客户端"模式下开始握手,则为true
      抛出:
      IllegalArgumentException - 如果在初始握手开始后尝试更改模式。
      参见:
    • getUseClientMode

      public abstract boolean getUseClientMode()
      如果在握手时设置引擎以使用客户端模式,则返回true。
      实现注意:
      JDK SunJSSE提供程序实现除非使用setUseClientMode(boolean)将模式更改为true,否则将返回false。
      返回:
      如果引擎应在"客户端"模式下进行握手,则返回true
      参见:
    • setNeedClientAuth

      public abstract void setNeedClientAuth(boolean need)
      配置引擎以要求客户端身份验证。此选项仅适用于服务器模式的引擎。

      引擎的客户端身份验证设置如下:

      • 需要客户端身份验证
      • 请求客户端身份验证
      • 不需要客户端身份验证

      setWantClientAuth(boolean)不同,如果设置了此选项并且客户端选择不提供有关自身的身份验证信息,协商将停止,引擎将开始其关闭过程

      调用此方法将覆盖此方法或setWantClientAuth(boolean)之前的任何设置。

      参数:
      need - 如果需要客户端身份验证,则设置为true;如果不需要客户端身份验证,则设置为false。
      参见:
    • getNeedClientAuth

      public abstract boolean getNeedClientAuth()
      如果引擎将要求客户端身份验证,则返回true。此选项仅适用于服务器模式的引擎。
      返回:
      如果需要客户端身份验证,则返回true;如果不需要客户端身份验证,则返回false。
      参见:
    • setWantClientAuth

      public abstract void setWantClientAuth(boolean want)
      配置引擎以请求客户端身份验证。此选项仅适用于服务器模式的引擎。

      引擎的客户端身份验证设置如下:

      • 需要客户端身份验证
      • 请求客户端身份验证
      • 不需要客户端身份验证

      setNeedClientAuth(boolean)不同,如果设置了此选项并且客户端选择不提供有关自身的身份验证信息,协商将继续

      调用此方法将覆盖此方法或setNeedClientAuth(boolean)之前的任何设置。

      参数:
      want - 如果请求客户端身份验证,则设置为true;如果不需要客户端身份验证,则设置为false。
      参见:
    • getWantClientAuth

      public abstract boolean getWantClientAuth()
      如果引擎将请求客户端身份验证,则返回true。此选项仅适用于服务器模式的引擎。
      返回:
      如果请求客户端身份验证,则返回true;如果不需要客户端身份验证,则返回false。
      参见:
    • setEnableSessionCreation

      public abstract void setEnableSessionCreation(boolean flag)
      控制此引擎是否可以建立新的SSL会话。如果不允许创建会话,并且没有现有会话可供恢复,则不会成功握手。
      参数:
      flag - true表示可以创建会话;这是默认值。false表示必须恢复现有会话
      参见:
    • getEnableSessionCreation

      public abstract boolean getEnableSessionCreation()
      如果此引擎可以建立新的SSL会话,则返回true。
      返回:
      true表示可以创建会话;这是默认值。false表示必须恢复现有会话
      参见:
    • getSSLParameters

      public SSLParameters getSSLParameters()
      返回此SSLEngine生效的SSLParameters。返回的SSLParameters的密码套件和协议始终不为null。
      返回:
      此SSLEngine生效的SSLParameters。
      自版本:
      1.6
    • setSSLParameters

      public void setSSLParameters(SSLParameters params)
      将SSLParameters应用于此引擎。

      这意味着:

      • 如果params.getCipherSuites()不为null,则使用该值调用setEnabledCipherSuites()
      • 如果params.getProtocols()不为null,则使用该值调用setEnabledProtocols()
      • 如果params.getNeedClientAuth()params.getWantClientAuth()返回true,则分别调用setNeedClientAuth(true)setWantClientAuth(true);否则调用setWantClientAuth(false)
      • 如果params.getServerNames()不为null,则引擎将使用该值配置其服务器名称。
      • 如果params.getSNIMatchers()不为null,则引擎将使用该值配置其SNI匹配器。
      参数:
      params - 参数
      抛出:
      IllegalArgumentException - 如果调用setEnabledCipherSuites()或setEnabledProtocols()失败
      自版本:
      1.6
    • getApplicationProtocol

      public String getApplicationProtocol()
      返回此连接当前正在进行的SSL/TLS握手期间协商的最新应用程序协议值。

      如果底层SSL/TLS/DTLS实现支持,应用程序名称协商机制(如RFC 7301,应用层协议协商(ALPN))可以在对等方之间协商应用级值。

      实现要求:
      此类中的实现会抛出UnsupportedOperationException,并且不执行其他操作。
      返回:
      如果尚未确定是否可以为此连接使用应用程序协议,则返回null;如果不会使用应用程序协议值,则返回空的String;如果成功协商了一个非空的应用程序协议String,则返回该值。
      抛出:
      UnsupportedOperationException - 如果底层提供程序未实现该操作。
      自版本:
      9
    • getHandshakeApplicationProtocol

      public String getHandshakeApplicationProtocol()
      返回当前正在进行的SSL/TLS握手期间协商的应用程序协议值。

      类似于getHandshakeSession(),连接可能处于握手过程中。应用程序协议可能尚未可用。

      实现要求:
      此类中的实现会抛出UnsupportedOperationException,并且不执行其他操作。
      返回:
      如果尚未确定是否可以为此握手使用应用程序协议,则返回null;如果不会使用应用程序协议值,则返回空的String;如果成功协商了一个非空的应用程序协议String,则返回该值。
      抛出:
      UnsupportedOperationException - 如果底层提供程序未实现该操作。
      自版本:
      9
    • setHandshakeApplicationProtocolSelector

      public void setHandshakeApplicationProtocolSelector(BiFunction<SSLEngine,List<String>,String> selector)
      注册一个回调函数,用于为SSL/TLS/DTLS握手选择应用程序协议值。该函数会覆盖使用SSLParameters.setApplicationProtocols提供的任何值,并支持以下类型参数:
      SSLEngine
      函数的第一个参数允许检查当前SSLEngine,包括握手会话和配置设置。
      List<String>
      函数的第二个参数列出TLS对等方广告的应用程序协议名称。
      String
      函数的结果是一个应用程序协议名称,或null表示没有一个广告的名称是可接受的。如果返回值是空的String,则不会使用应用程序协议指示。如果返回值为null(未选择任何值)或是对等方未广告的值,则底层协议将确定采取什么操作。(例如,ALPN将发送“no_application_protocol”警报并终止连接。)
      例如,以下调用注册一个回调函数,检查TLS握手参数并选择一个应用程序协议名称:
      
           serverEngine.setHandshakeApplicationProtocolSelector(
               (serverEngine, clientProtocols) -> {
                   SSLSession session = serverEngine.getHandshakeSession();
                   return chooseApplicationProtocol(
                       serverEngine,
                       clientProtocols,
                       session.getProtocol(),
                       session.getCipherSuite());
               });
       
      API注释:
      TLS服务器应用程序应在TLS握手开始之前调用此方法。此外,此SSLEngine应配置为与回调函数选择的应用程序协议兼容的参数。例如,启用不良选择的密码套件可能导致没有合适的应用程序协议。请参阅SSLParameters
      实现要求:
      此类中的实现会抛出UnsupportedOperationException,并且不执行其他操作。
      参数:
      selector - 回调函数,如果要禁用回调功能,则为null。
      抛出:
      UnsupportedOperationException - 如果底层提供程序未实现该操作。
      自版本:
      9
    • getHandshakeApplicationProtocolSelector

      public BiFunction<SSLEngine,List<String>,String> getHandshakeApplicationProtocolSelector()
      检索在SSL/TLS/DTLS握手期间选择应用程序协议值的回调函数。有关函数的类型参数,请参见setHandshakeApplicationProtocolSelector
      实现要求:
      此类中的实现会抛出UnsupportedOperationException,并且不执行其他操作。
      返回:
      回调函数,如果未设置任何回调函数则返回null。
      抛出:
      UnsupportedOperationException - 如果底层提供程序未实现该操作。
      自版本:
      9