testssl.sh icon indicating copy to clipboard operation
testssl.sh copied to clipboard

Display whether server requests/requires a Client Certificate

Open awsles opened this issue 4 years ago • 8 comments

One of the big challenges in debugging TLS/SSL connections is determining whether the server is requesting/requiring a client certificate. With openSSL and with testssl.sh, it is unclear. Both tools output nice information about the negotiated protocol, but neglect to complete the handshake sequence afterwards. openssl outputs a cryptic message (if any).

It would be great if testssl.sh could CLEARLY indicate whether: (a) server has requested a client certificate; and (b) whether the connection fully succeeded (i.e., did server connect anyway if no client cert was provided / specified)? Having spent hours troubleshooting TLS connections with "handshake_failure" messages (a Java 'catch-all'), having this detail would be great.

This topic mentions the issue with openssl. It is possble to detect that an CertificateRequest has been sent from the server. https://security.stackexchange.com/questions/101511/determine-if-a-server-is-asking-for-a-client-certificate-using-openssl-s-client

IMPLEMENTATION CONSIDERATION: By using s_client in openssl, the CA list can be viewed and checked. However some servers only request client authentication after a specific URL is requested. To obtain the list in this case it is necessary to use the -prexit command and send an HTTP request for an appropriate page.

awsles avatar Aug 27 '20 14:08 awsles

???


 Start 2020-08-27 17:27:01        -->> 213.154.225.246:443 (secure.cacert.org) <<--

 Further IP addresses:   2001:7b8:3:9c::246 
 rDNS (213.154.225.246): 246.224-27.225.154.213.in-addr.arpa. secure.cacert.org.
 Service detected:       certificate-based authentication => skipping all HTTP checks

drwetter avatar Aug 27 '20 15:08 drwetter

Hi Dirk,

As you noted, I believe that in most cases testssl.sh will show the "certificate-based authentication => skipping all HTTP checks" message if certificate-based authentication is required. However, my reading of the code is that this message won't appear if a STARTTLS protocol is specified, since service_detection() is not called in that case.

Even if a STARTTLS protocol is specified, there is still a chance that a message will be displayed. For example, run_breach() will indicate "cannot be tested (server side requires x509 authentication)" if client authentication is required. But, if a STARTTLS protocol is specified and not all tests are performed, then nothing may be displayed.

Basically, testssl.sh informs the user that certificate-based authentication is required, if testssl.sh is prevented from performing some test as a result of the requirement, but not otherwise. For that same reason, testssl.sh does not indicate if the server requested a certificate but successfully connects even if one is not provided, since the mere request for a certificate does not interfere with any of testssl.sh's testing.

If I understand correctly, the request here is to indicate (even when STARTTLS is specified) whether the server requests a certificate, and if so whether providing a certificate is required or optional. There may additionally be a request to display the CA list that the server sends when it requests a certificate.

dcooper16 avatar Aug 27 '20 16:08 dcooper16

Hi David,

However, my reading of the code is that this message won't appear if a STARTTLS protocol is specified, since service_detection() is not called in that case

In determine_optimal_proto() we'll call sclient_auth() and that populates CLIENT_AUTH accordingly -- for both STARTTLS and plain TLS. So that should(TM) work. But honestly I can't remember myself testing STARTTLS connections using a certificate --as I've never seen a STARTTLS connection requiring a certificate on a TLS layer. Only ones which the application uses for authentication but that was way back. Are you talking about that type of certificate?

If I understand correctly, the request here is to indicate (even when STARTTLS is specified) whether the server requests a certificate, and if so whether providing a certificate is required or optional

Ok, I think we can somewhere add this more clearly.

What is optional in this context? Do you have an example or doc/RFC?

drwetter avatar Aug 28 '20 11:08 drwetter

In determine_optimal_proto() we'll call sclient_auth() and that populates CLIENT_AUTH accordingly -- for both STARTTLS and plain TLS. So that should(TM) work.

Yes, CLIENT_AUTH should be set if client authentication is required whether STARTTLS is used or not. But, sclient_auth() just sets CLIENT_AUTH, it doesn't produce any output. There are a few places where output is produced if CLIENT_AUTH is true, but they may not be clear, and the most obvious one (the one output by determine_service() won't appear if STARTTLS is used.

But honestly I can't remember myself testing STARTTLS connections using a certificate --as I've never seen a STARTTLS connection requiring a certificate on a TLS layer. Only ones which the application uses for authentication but that was way back. Are you talking about that type of certificate?

I don't know of cases in which certificate-based client authentication is used along with STARTTLS. I just assumed it was a possibility, since the I thought the TLS protocol was the same whether STARTTLS is used or not.

I think the first, easy step to address this issue would be to modify sclient_auth(). At the moment, the first line of sclient_auth() returns 0 if the call to $OPENSSL returned 0, so it will only detect if client authentication is required. If client authentication is optional, then OpenSSL's output will include CertificateRequest, but the return value will be 0.

What is optional in this context? Do you have an example or doc/RFC?

Unfortunately, the CertificateRequest message differs between TLS 1.3 and TLS 1.2 and earlier.

In TLS 1.2 and earlier (RFC 5246, Section 7.4.4), the CertificateRequest consists of:

  • The "types" of certificates the server will accept (e.g, RSA, ECDSA, DH);
  • acceptable signature algorithms; and
  • the distinguished names of acceptable CAs.

In TLS 1.3 (RFC 8446, ), the CertificateRequest contains an extensions field. The extensions can specify the distinguished names of acceptable CAs and the acceptable signature algorithms, but other information may be provided as well.

It seems that OpenSSL will print out information about contents of the CertificateRequest message, but LibreSSL will not. So, the easy way to get the CA list would be to just pull it from the output of $OPENSSL s_client. However, if we want something that will work with LibreSSL, then we'll have to extract and parse the CertificateRequest message in order to obtain the list.

The same applies if we want to provide information about the signature algorithms the server lists as acceptable. OpenSSL prints out the information, but LibreSSL does not. Providing this information would be similar to the request #1519. It would allow testssl.sh to warn if the server listed weak algorithms as acceptable.

dcooper16 avatar Sep 01 '20 20:09 dcooper16

Thanks for your PR, David.

With what I said above I didn't want to discourage you from implementing that. And actually I was about to say that but it seems it wasn't needed (PR). :-)

Just remove the WIP if you're think it's ready. (I accidentally pressed the review button).

Besides looking at the code it would be great to try that in practice. However I lack servers to test against. So any input from you or others (also @lesterw1 ) would be appreciated.

drwetter avatar Sep 04 '20 10:09 drwetter

Please see David's PR merged.

drwetter avatar Feb 08 '21 12:02 drwetter

For those who haven't noticed: @dcooper16 provided a(nother) PR for those cases where a client authentication is required per URL.

if someone has a hostname handy to test against that would be great.

drwetter avatar Dec 03 '21 11:12 drwetter

On nginx per-URL isn't that easily possible, see http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_client_certificate, otherwise I have helped here.

drwetter avatar Dec 03 '21 12:12 drwetter

Hmm... this seems done when the PR #1990 was merged.

Thus closing

drwetter avatar Dec 27 '22 08:12 drwetter