An auth option is required to connect an SSL server

Regarding SSLServerSocket.setWantClientAuth :
If this parameter is set to true , if the client decided not to send a certificate, negotiations continue.
I also noticed that this also happens if the client sends the certificate but is not included in the trust store. In this case, the coordination is not interrupted.

So what is the use case for this parameter?

+4
source share
2 answers

(A few changes, following a few comments.)

setWantClientAuth used to request authentication of a client certificate, but keep the connection if authentication is not provided. setNeedClientAuth used to request and authenticate a client certificate: the connection will end if a suitable client certificate is not presented.

See the Client Certificate section of the TLS Specification for more information. A bit of history:

  • Version 1.2 says: "If a suitable certificate is not available, the client MUST send a certificate message that does not contain certificates." Before that, it was just “SHOULD”, but Sun JSSE clients send an empty list in this case.

  • Version 1.2 is also added:

    In addition, if any aspect of the certificate chain was unacceptable (for example, it was not signed by a well-known, trusted CA), the server MAY, at its discretion, either continue to shake hands (considering the client is unauthorized) or send a fatal warning.

    This gives some flexibility as to what to do when an unacceptable certificate is sent. JSSE decides to send a fatal warning. ( setWantAuth can in principle run with invalid certificates, but not treat the ad hoc network as authenticated, as if a client certificate had not been sent, but it is not).

    Previous versions of the TLS specification said: "If server authentication is required to continue the handshake, it may respond with a fatal failure failure message." This is the difference between need or need, as implemented in JSSE: using "need", the server responds with a fatal acknowledgment failure, while using "want" the server continues the connection, but does not consider it authenticated.

I originally, although your client did not send their certificate when you used the "need." Indeed, most clients will not send a client certificate at all if they cannot find the client certificate issued by one of the issuers listed in the CA names sent by the server at the time of its request (or if the client cannot create the chain itself, which is a common problem ). By default, JSSE uses certificate authorities in truststore to create this list. For this reason, your client probably will not send the client certificate at all if the appropriate issuer is not in the server’s power of attorney.

You can check if the client certificate has been sent using Wireshark. If you are not using the port commonly used with SSL / TLS, you need to right-click on the packet and select "Decode As ... → Transport → SSL".

There you will see a Certificate Request message coming from the server. (For some reason, when I use the default truststore JRE with Wireshark, this message appears as “Encrypted confirmation message” right after the “Server Key Exchange” message. However, it is not encrypted: you can clearly see the number from the CA names if you look at the ASCII rendering of the package in the bottom pane, perhaps because this message is too long, I'm not sure.) With a shorter list, for example, a trust store with one CA, Wireshark, you correctly decode this message as Certificate Request , and you should see a list of accepted CAs in the section Distinguished Names.

You should also see a Certificate message coming from the client (of course, not from the server, of course). If the server requests (if necessary or necessary) a certificate, you should always see this message from the client.

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

  • If you configured the trust store with a CA CA certificate, use setWantClientAuth(true) , the client will send its client certificate, and the connection will continue. The server can then obtain the client certificate from SSLSession , as expected.

  • If you use the default trust store (which does not contain a test CA certificate), use setWantClientAuth(true) , the CA DN will not be in the Certificate Request . The client will send a Certificate message, but the list of certificates will be empty ( Certificates Length: 0 in Wireshark). Here, the client does not actually send the client certificate, even if its key store is configured for this, simply because it cannot find a suitable match. The connection will continue (you can get an exception if you try to read the peer certificate from SSLSession on the server, but this is not fatal). This is a use case for setWantClientAuth(true) ; setNeedClientAuth(true) will terminate the connection immediately.

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

     KeyManagerFactory kmf = //... Initialise a KMF with your server 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); 

    In this case, the Certificate Request message sent by the server should contain your CA CA for testing. However, this CA is not really trusted by the trust manager, which still uses the default values.

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

The key point here is that if you are able to get the client certificate from SSLSession , this certificate must be authenticated by the trust manager (by this I mean the SSLSession that you receive after the handshake is completed, and SSLSocket.getSession() not the one you receive during a handshake using getHandshakeSession() introduced in Java 7).

In the comments, you indicate that you are using a different JSSE provider and that the client sends the client certificate anyway, regardless of whether there was a CA certificate in the server trust store, because you also had a different CA certificate in your trust store with the same DN name . Assuming that these two CA certificates have different keys (otherwise they would be virtually the same CA), this would be a pretty serious mistake: applications that use client certificate authentication have the right to expect the client certificate to be verified by the trust manager (how indicated in the JSSE reference). If you use Apache Tomcat, after receiving the client certificate, the remote connection is considered authenticated with this certificate. If at this point the servlet can use a client certificate that cannot be verified, authentication has not actually been performed, which would be a serious drawback.

+12
source

It is used when you want to know the credentials of a client if he sends them, but you do not want to stop communicating if he does not. Contrast with "needClientAuth" when a handshake fails if it does not send them.

0
source

All Articles