Module java.base
Package javax.net.ssl

Class SSLSocket

java.lang.Object
java.net.Socket
javax.net.ssl.SSLSocket
所有已实现的接口:
Closeable, AutoCloseable

public abstract class SSLSocket extends Socket
此类扩展了Socket,并使用诸如"安全套接字层"(SSL)或IETF"传输层安全性"(TLS)协议提供安全套接字。

这些套接字是普通的流套接字,但它们在底层网络传输协议上增加了一层安全保护,例如TCP。这些保护包括:

  • 完整性保护。SSL可防止主动窃听者修改消息。
  • 身份验证。在大多数模式下,SSL提供对等身份验证。通常服务器会被验证,客户端可能会根据服务器的要求进行验证。
  • 机密性(隐私保护)。在大多数模式下,SSL加密在客户端和服务器之间发送的数据。这保护了数据的机密性,使得被动窃听者无法看到敏感数据,如金融信息或各种个人信息。

这些保护类型由"密码套件"指定,它是给定SSL连接使用的加密算法的组合。在协商过程中,两个端点必须就两个环境中都可用的密码套件达成一致。如果没有共同的套件,就无法建立SSL连接,也无法交换数据。

使用的密码套件是通过称为"握手"的协商过程建立的。此过程的目标是创建或重新加入一个"会话",该会话可以随时间保护许多连接。握手完成后,您可以通过使用getSession方法访问会话属性。在此连接上的初始握手可以通过以下三种方式之一启动:

  • 调用startHandshake显式开始握手,或
  • 在此套接字上尝试读取或写入应用程序数据会导致隐式握手,或
  • 调用getSession尝试设置会话(如果当前没有有效会话),并执行隐式握手。

如果由于任何原因握手失败,SSLSocket将被关闭,无法进行进一步通信。

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

  • 支持的密码套件:SSL实现支持的所有套件。此列表使用getSupportedCipherSuites报告。
  • 已启用的密码套件,可能少于完整支持的套件集。此组使用setEnabledCipherSuites方法设置,并使用getEnabledCipherSuites方法查询。最初,新套接字上将启用一组默认密码套件,表示建议的最低配置。

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

当创建SSLSocket时,不会执行任何握手,以便应用程序可以首先设置其通信首选项:要使用的密码套件,套接字应处于客户端还是服务器模式等。但是,一旦应用程序数据通过连接发送,安全性将始终得到保障。

您可以注册以接收握手完成的事件通知。这涉及使用两个附加类。HandshakeCompletedEvent对象传递给此API的用户注册的HandshakeCompletedListener实例。通过SSLSocketFactory创建SSLSocket,或通过从SSLServerSocket接受连接来创建SSLSocket

SSL套接字必须选择在客户端模式或服务器模式下运行。这将确定由哪一方开始握手过程,以及每个方发送哪些消息。每个连接必须有一个客户端和一个服务器,否则握手将无法正确进行。一旦初始握手开始,套接字就无法在执行重新协商时在客户端和服务器模式之间切换。

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

     String networkString = sslSocket.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值正确解码为UnicodeString。
     //
     String unicodeString = new String(bytes, StandardCharsets.UTF_8);
     if (unicodeString.equals(HTTP1_1)
             || unicodeString.equals("\uabcd\uabce\uabcf")) {
         ...
     }
 
API注释:
当连接不再需要时,客户端和服务器应用程序应分别关闭各自连接的两侧。可以通过调用Socket.close()一次性关闭,或通过分别关闭每一侧使用Socket.shutdownOutput()/Socket.shutdownInput()来关闭,这对于支持半关闭连接的协议版本很有用。

请注意,在某些情况下,关闭输入流可能取决于对等方的输出流先关闭。如果连接未按顺序关闭(例如在接收对等方的写入关闭通知之前调用了Socket.shutdownInput()),可能会引发异常以指示发生错误。

一旦SSLSocket关闭,它就不可重用:必须创建一个新的SSLSocket

自从:
1.4
参见:
  • Constructor Details

    • SSLSocket

      protected SSLSocket()
      仅由子类使用。构造一个未初始化、未连接的TCP套接字。
    • SSLSocket

      protected SSLSocket(String host, int port) throws IOException, UnknownHostException
      仅由子类使用。构造到指定端口的命名主机的TCP连接。这充当SSL客户端。

      如果存在安全管理器,则将使用主机地址和port作为参数调用其checkConnect方法。这可能导致SecurityException。

      参数:
      host - 要连接的主机名,或null表示回环地址。
      port - 服务器端口号
      抛出:
      IOException - 如果创建套接字时发生I/O错误
      SecurityException - 如果存在安全管理器且其checkConnect方法不允许该操作。
      UnknownHostException - 如果主机未知
      IllegalArgumentException - 如果端口参数超出有效端口值范围,有效端口值为0到65535(包括0和65535)。
      参见:
    • SSLSocket

      protected SSLSocket(InetAddress address, int port) throws IOException
      仅供子类使用。构造一个到指定地址和端口的服务器的TCP连接。这充当SSL客户端。

      如果存在安全管理器,则将使用主机地址和port作为参数调用其checkConnect方法。这可能导致SecurityException。

      参数:
      address - 服务器的主机
      port - 其端口
      抛出:
      IOException - 如果创建套接字时发生I/O错误
      SecurityException - 如果存在安全管理器且其checkConnect方法不允许该操作。
      IllegalArgumentException - 如果端口参数超出有效端口值范围,有效端口值为0到65535(包括0和65535)。
      NullPointerException - 如果address为null。
      参见:
    • SSLSocket

      protected SSLSocket(String host, int port, InetAddress clientAddress, int clientPort) throws IOException, UnknownHostException
      仅供子类使用。构造一个到指定地址和TCP端口的服务器的SSL连接,将连接的客户端一侧绑定到给定的地址和端口。这充当SSL客户端。

      如果存在安全管理器,则将使用主机地址和port作为参数调用其checkConnect方法。这可能导致SecurityException。

      参数:
      host - 要连接的主机名,或null表示回环地址。
      port - 服务器端口号
      clientAddress - 套接字绑定到的客户端地址,或null表示anyLocal地址。
      clientPort - 套接字绑定到的客户端端口,或zero表示系统选择的空闲端口。
      抛出:
      IOException - 如果创建套接字时发生I/O错误
      SecurityException - 如果存在安全管理器且其checkConnect方法不允许该操作。
      UnknownHostException - 如果主机未知
      IllegalArgumentException - 如果端口参数或clientPort参数超出有效端口值范围,有效端口值为0到65535(包括0和65535)。
      参见:
    • SSLSocket

      protected SSLSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort) throws IOException
      仅供子类使用。构造一个到指定地址和TCP端口的服务器的SSL连接,将连接的客户端一侧绑定到给定的地址和端口。这充当SSL客户端。

      如果存在安全管理器,则将使用主机地址和port作为参数调用其checkConnect方法。这可能导致SecurityException。

      参数:
      address - 服务器的主机
      port - 其端口
      clientAddress - 套接字绑定到的客户端地址,或null表示anyLocal地址。
      clientPort - 套接字绑定到的客户端端口,或zero表示系统选择的空闲端口。
      抛出:
      IOException - 如果创建套接字时发生I/O错误
      SecurityException - 如果存在安全管理器且其checkConnect方法不允许该操作。
      IllegalArgumentException - 如果端口参数或clientPort参数超出有效端口值范围,有效端口值为0到65535(包括0和65535)。
      NullPointerException - 如果address为null。
      参见:
  • Method Details

    • getSupportedCipherSuites

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

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

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

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

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

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

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

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

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

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

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

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

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

      public abstract String[] getEnabledProtocols()
      返回当前在此连接上启用的协议版本的名称。

      请注意,即使启用了协议,也可能永远不会使用。这可能发生在对等方不支持该协议,或者其使用受限,或者没有受协议支持的启用密码套件,或者启用了匿名套件但需要身份验证的情况下。

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

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

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

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

      public abstract SSLSession getSession()
      返回此连接正在使用的SSL会话。这些会话可以长期存在,并且通常对应于某个用户的整个登录会话。该会话指定了会话中所有连接正在使用的特定密码套件,以及会话的客户端和服务器的身份。

      如果在初始握手期间发生错误,此方法将返回一个无效的会话对象,该对象报告一个无效的密码套件"SSL_NULL_WITH_NULL_NULL"。

      返回:
      SSLSession
    • getHandshakeSession

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

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

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

      getSession()不同,此方法不会启动初始握手,并且不会阻塞直到握手完成。

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

      public abstract void addHandshakeCompletedListener(HandshakeCompletedListener listener)
      注册事件侦听器以接收SSL握手在此连接上完成的通知。
      参数:
      listener - 握手完成事件侦听器
      抛出:
      IllegalArgumentException - 如果参数为null。
      参见:
    • removeHandshakeCompletedListener

      public abstract void removeHandshakeCompletedListener(HandshakeCompletedListener listener)
      移除先前注册的握手完成侦听器。
      参数:
      listener - 握手完成事件侦听器
      抛出:
      IllegalArgumentException - 如果侦听器未注册,或参数为null。
      参见:
    • startHandshake

      public abstract void startHandshake() throws IOException
      在此连接上启动SSL握手。常见原因包括需要使用新的加密密钥,更改密码套件或启动新会话。要强制进行完整重新认证,可以在启动此握手之前使当前会话无效。

      如果在连接上已经发送了数据,则在此握手期间数据将继续流动。当握手完成时,将通过事件发出信号。对于连接上的初始握手,此方法是同步的,并在协商的握手完成时返回。某些协议可能不支持在现有套接字上进行多次握手,并可能抛出IOException。

      抛出:
      IOException - 在网络级别发生错误时
      参见:
    • setUseClientMode

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

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

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

      参数:
      mode - 如果套接字应在"客户端"模式下开始握手,则为true
      抛出:
      IllegalArgumentException - 如果在初始握手开始后尝试更改模式。
      参见:
    • getUseClientMode

      public abstract boolean getUseClientMode()
      如果套接字在握手时设置为使用客户端模式,则返回true。
      返回:
      如果套接字应在"客户端"模式下进行握手,则返回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()
      返回true,如果此套接字可以建立新的SSL会话。
      返回值:
      true表示可以创建会话;这是默认值。false表示必须恢复现有会话
      参见:
    • getSSLParameters

      public SSLParameters getSSLParameters()
      返回此SSLSocket生效的SSLParameters。返回的SSLParameters的密码套件和协议始终非空。
      返回值:
      此SSLSocket生效的SSLParameters。
      自从:
      1.6
    • setSSLParameters

      public void setSSLParameters(SSLParameters params)
      将SSLParameters应用于此套接字。

      这意味着:

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

      public String getApplicationProtocol()
      返回此连接为此连接协商的最新应用程序协议值。

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

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