Java SSL服务器套接字需要身份验证选项

Java SSL服务器套接字需要身份验证选项,java,security,sockets,ssl,jsse,Java,Security,Sockets,Ssl,Jsse,关于: 如果设置为true如果客户端选择不发送证书,协商将继续。 我还注意到,如果客户机发送一个证书,但不是信任库的一部分,也会发生这种情况。在这种情况下,协商也不会失败 那么这个设置的用例是什么呢?(多个编辑,后面有许多注释。) setWantClientAuth用于请求客户端证书身份验证,但如果未提供身份验证,则保持连接setNeedClientAuth用于请求和要求客户端证书身份验证:如果没有提供合适的客户端证书,连接将终止 您可以在中找到有关此主题的更多信息。回顾一下历史: 在版本1.

关于:
如果设置为
true
如果客户端选择不发送证书,协商将继续。
我还注意到,如果客户机发送一个证书,但不是信任库的一部分,也会发生这种情况。在这种情况下,协商也不会失败

那么这个设置的用例是什么呢?

(多个编辑,后面有许多注释。)

setWantClientAuth
用于请求客户端证书身份验证,但如果未提供身份验证,则保持连接
setNeedClientAuth
用于请求和要求客户端证书身份验证:如果没有提供合适的客户端证书,连接将终止

您可以在中找到有关此主题的更多信息。回顾一下历史:

  • 在版本1.2中,它说“如果没有合适的证书可用,客户端必须发送不包含证书的证书消息。”。在此之前,它只是一个“应该”,但Sun JSSE客户端无论如何都会发送一个空列表

  • 版本1.2还添加了:

    此外,如果证书链的某些方面不可接受(例如。, 它没有由已知的、受信任的CA签名),服务器可能会在 决定是否继续握手(考虑客户) 未经验证)或发送致命警报

    这在发送不可接受的证书时提供了一些灵活性。JSSE选择发送致命警报。(
    setWantAuth
    原则上可以处理无效证书,但不能将对等方视为已验证,就好像没有发送客户端证书一样,但事实并非如此。)

    TLS规范的早期版本说,“如果服务器需要客户端身份验证才能继续握手,它可能会发出致命的握手失败警报。”。这就是JSSE中实现的“需要”和“想要”之间的区别:使用“需要”,服务器会以致命的握手失败来响应,而使用“想要”,服务器会继续连接,但不会将其视为已验证

我最初认为,当您使用“需要”时,您的客户机没有发送其证书。事实上,如果在服务器请求期间找不到由服务器发送的CA名称中列出的颁发者之一颁发的客户端证书,大多数客户端根本不会发送客户端证书(或者,如果客户端无法自行构建链,这就是)。 默认情况下,JSSE使用信任库中的CA来构建该列表。 因此,如果服务器的信任库中没有合适的颁发者,您的客户端可能根本不会发送客户端证书

您可以使用Wireshark检查是否发送了客户端证书。如果您不是在通常与SSL/TLS一起使用的端口上运行,则需要右键单击数据包并选择“解码为…->传输->SSL”

在那里,您应该会看到来自服务器的
证书请求
消息。(出于某种原因,当我使用Wireshark的默认JRE信任库时,该消息显示为“加密握手消息”,就在“服务器密钥交换”之后)消息。但是,它不是加密的:如果查看底部面板中数据包的ASCII呈现,您可以清楚地看到许多CA名称。这可能是因为此消息太长,我不确定。)使用较短的列表,例如,具有单个CA的信任存储,Wireshark将其正确解码为
证书请求
消息,您应该在“可分辨名称”部分中看到已接受CA的列表

您还应该看到来自客户机的
证书
消息(当然不是来自服务器的消息)。如果服务器请求(想要或需要)证书,您应该始终从客户端看到此消息

假设您有权访问测试CA(使用该CA颁发的客户端证书),您可以尝试以下实验

  • 如果使用该测试CA证书设置信任存储,请使用
    setWantClientAuth(true)
    ,客户端将发送其客户端证书,连接将继续。然后,服务器可以按预期从
    SSLSession
    获取客户端证书

  • 如果使用默认信任存储(不包含测试CA证书),请使用
    setWantClientAuth(true)
    ,CA DN将不在
    证书请求中。客户端将发送一条
    Certificate
    消息,但证书列表将为空(
    Certificates Length:0
    在Wireshark中)。在这里,客户机实际上没有发送客户机证书,即使其密钥库被配置为发送客户机证书,这仅仅是因为它找不到合适的匹配项。连接将继续(如果尝试从服务器上的
    SSLSession
    读取对等证书,可能会出现异常,但这不是致命的)。这是
    setWantClientAuth(true)
    的用例
    setNeedClientAuth(true)
    会立即结束连接

  • 为了进行此实验,可以用Java伪造服务器发送的DNs列表

    KeyManagerFactory kmf = //... Initialise a KMF with your server's keystore
    
    TrustManagerFactory tmf = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init((KeyStore) null); // Use the default trust store
    TrustManager[] trustManagers = tmf.getTrustManagers();
    final X509TrustManager origTrustManager = (X509TrustManager) trustManagers[0];
    final X509Certificate caCert = // Load your test CA certificate here.
    X509TrustManager fakeTrustManager = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain,
                String authType) throws CertificateException {
            // Key the behaviour of the default trust manager.
            origTrustManager.checkClientTrusted(chain, authType);
        }
    
        public void checkServerTrusted(X509Certificate[] chain,
                String authType) throws CertificateException {
            // Key the behaviour of the default trust manager.
            origTrustManager.checkServerTrusted(chain, authType);
        }
    
        public X509Certificate[] getAcceptedIssuers() {
            // This is only used for sending the list of acceptable CA DNs.
            return new X509Certificate[] { caCert };
        }
    };
    trustManagers = new X509TrustManager[] { fakeTrustManager };
    
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(kmf.getKeyManagers(), trustManagers, null);
    
    在这种情况下,服务器发送的
    证书请求
    消息应该包含测试CA的DN。但是,信任管理器实际上并不信任该CA,它仍然使用默认值

    客户端将发送其证书,但服务器将拒绝它,并说“javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径验证失败”,这将结束连接。这至少是使用SunJSSE提供程序、使用PKIX或SunX509信任管理器的实现。这也与“TrustManager的主要职责是确定所提供的身份验证凭据是否应被信任