SSL服务器套接字需要auth选项 [英] SSL Server socket want auth option

查看:162
本文介绍了SSL服务器套接字需要auth选项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于 SSLServerSocket.setWantClientAuth

如果设置为 true 如果客户端选择不发送证书,则协商将继续进行。 >
另外我注意到,如果客户端发送证书不是信任库的一部分,也会发生这种情况。在这种情况下,协商也不会失败。

Concerning SSLServerSocket.setWantClientAuth:
If this is set to true if the client chooses to not send a certificate the negotiation continues.
Also I noticed this also happens if the client sends a certificate but is not part of the truststore.The negotiation does not fail either in this case.

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

So what is the use case of this setting?

推荐答案

(多个编辑,关注一些评论。)

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

setWantClientAuth is used to request client certificate authentication, but keep the connection if no authentication is provided. setNeedClientAuth is used to request and require client certificate authentication: the connection will terminate if no suitable client certificate is presented.

您可以在TLS规范的客户端证书部分中找到有关此主题的更多信息。对于一些历史记录:

You can find more on this topic in the Client Certificate section of the TLS specification. For a bit of history:


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

  • In version 1.2, it says "If no suitable certificate is available, the client MUST send a certificate message containing no certificates.". Before that, it was just a "SHOULD", but the Sun JSSE clients send an empty list this case anyway.

版本1.2还添加了:


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

Also, if some aspect of the certificate chain was unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its discretion either continue the handshake (considering the client unauthenticated) or send a fatal alert.

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

This gives some flexibility regarding what to do when a unacceptable certificate is sent. The JSSE chooses to send a fatal alert. (setWantAuth could in principle carry on with invalid certificates, but not treat the peer as authenticated, as if no client certificate was sent, but this isn't the case.)

以前版本的TLS规范说如果服务器需要客户端身份验证才能继续握手,它可能会以致命的握手失败警报响应。 / EM>。这是在JSSE中实现的需求或需求之间的差异:使用需要,服务器响应致命的握手失败,而使用want,服务器继续进行连接,但不将其视为已验证。

Previous versions of the TLS spec said "If client authentication is required by the server for the handshake to continue, it may respond with a fatal handshake failure alert.". This is the difference between need or want as implemented in the JSSE: using "need", the server responds with a fatal handshake failure, whereas using "want", the server carries on with the connection, but doesn't treat it as authenticated.

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

I initially though that your client wasn't sending its certificate when you were using "need". Indeed, most clients won't send a client certificate at all if they can't find a client certificate that is issued by one of the issuers listed in the CA names send by the server during its request (or if the client can't build the chain themselves, which is a common problem). By default, the JSSE uses the CAs in the truststore to build that list. For this reason, your client would probably not send a client certificate at all if a suitable issuer isn't in the server's truststore.

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

You can check whether a client certificate is sent using Wireshark. If you're not running on a port normally used with SSL/TLS, you'll need to right click on a packet and choose "Decode As... -> Transport -> SSL".

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

There, you should see a Certificate Request message coming from the server. (For some reason, when I'm using the default JRE truststore with Wireshark, that message appears as an "Encrypted Handshake Message", just after the "Server Key Exchange" message. However, it's not encrypted: you can clearly see a number of CA names if you look at the ASCII rendering of the packet in the bottom panel. Perhaps this is because this message is too long, I'm not sure.) With a shorter list, for example, a trust store with a single CA, Wireshark you decode this properly as a Certificate Request message, and you should see the list of accepted CAs in the "Distinguished Names" section.

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

You should also see a Certificate message coming from the client (not the one coming from the server, of course). If the server requests (with want or need) a certificate, you should always see this message from the client anyway.

假设您有权访问测试CA(带有由该CA颁发的客户证书,您可以尝试以下实验。

Assuming you have access to a test CA (with a client certificate issued by that CA), you can try the following experiments.


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

  • If you set up your trust store with that test CA cert, use setWantClientAuth(true), the client will send its client certificate, and the connection will proceed. The server can then get the client certificate from the SSLSession, as expected.

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

If you use the default trust store (that doesn't contain your test CA cert), use setWantClientAuth(true), the CA DN will not be in the Certificate Request. The client will send a Certificate message, but the certificate list will be empty (Certificates Length: 0 in Wireshark). Here, the client is actually not sending a client certificate, even if its keystore is configured to do so, simply because it can't find a suitable match. The connection will proceed (you may get an exception if you try to read the peer certificate from the SSLSession on the server, but that's not fatal). This is the use-case for setWantClientAuth(true); setNeedClientAuth(true) would have ended the connection immediately.

为了这个实验,你可以伪造服务器用Java发送的DN列表。

For the sake of this experiment, you can fake the list of DNs sent by the server in Java.

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,它仍然使用默认值。

In this case, the Certificate Request message sent by the server should contain the your test CA's DN. However, that CA isn't actually trusted by the trust manager, which still uses the default values.

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

The client will send its certificate, but the server will reject it, saying "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed", and this will end the connection. This is at least the implementation using the SunJSSE provider, using the PKIX or SunX509 trust managers. This is also consistent with the JSSE specification of the trust manager: "The primary responsibility of the TrustManager is to determine whether the presented authentication credentials should be trusted. If the credentials are not trusted, the connection will be terminated."

这里的关键点是,如果您能够从<$ c $获得客户证书c> SSLSession ,该证书应该由信任管理员进行身份验证(我的意思是一旦握手完成后你得到的 SSLSession SSLSocket.getSession()不是在握手期间使用<7>引入的 getHandshakeSession()获得的那个。

The key point here is that if you're in a position to get the client certificate from the SSLSession, that certificate should have been authenticated by the trust manager (by this I mean the SSLSession you get once the handshake has completed, with SSLSocket.getSession() not the one you get during the handshake using getHandshakeSession(), introduced in Java 7).

您似乎在评论中表明您正在使用另一个JSSE提供商,而您的客户端是无论如何,无论CA证书是否在服务器的信任存储中,都要找到客户端证书,因为您的信任存储中还有另一个具有相同主题DN的不同CA证书。假设这两个CA证书具有不同的密钥(否则它们实际上是相同的CA),这将是一个相当严重的错误:使用客户端证书身份验证的应用程序有权期望客户端证书已由信任管理器验证(如JSSE参考指南中所述。如果您正在使用Apache Tomcat,则一旦获得客户端证书,远程连接将被视为使用此证书进行身份验证。如果此时servlet能够使用无法验证的客户端证书,则实际上没有进行身份验证,这将是一个严重的缺陷。

You seem to indicate in comments that you're using another JSSE provider, and that your client was sending a client certificate anyway, whether or not the CA cert was in the server's trust store, because you also had another different CA cert in your trust store with the same Subject DN. Assuming those two CA certificates have different keys (otherwise they would effectively be the same CA), this would be a rather serious bug: applications that use client-certificate authentication are entitled to expect the client certificate to have been verified by the trust manager (as specified in the JSSE reference guide). If you're using Apache Tomcat, once it gets the client certificate, the remote connection is considered as authenticated with this cert. If at that point the servlet is able to use a client certificate that can't have been verified, no authentication has actually been made, which would be a serious flaw.

这篇关于SSL服务器套接字需要auth选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆