0

I making an SSL connection to a server where client certificate authentication is supported but not required. In a browser, I can authenticate with a token, or I can cancel the prompt to present a certificate, and visit the site's login page to supply a username/password.

I want to test this connection using openssl s_client -connect my.server.name. The server cert verify looks good, but it's followed by

...:SSL3_READ_BYTES:sslv3 alert bad certificate:s3_pkt.c:1275:SSL alert number 42

...:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:184

As far as I can tell, the "alert bad certificate" means that the client certificate was "bad" (which makes sense, since there wasn't one.) After that, it shows the encoded server cert, then "Acceptable client certificate CA names" (with a list of CAs), and some basic status stuff.

I don't see anything to tell me whether or not those errors (bad certificate / handshake failure) are fatal, i.e., if the client certificate was required and the connection cannot continue without it. Would this information be any clearer in debug/verbose mode? Do I need to pass a separate flag to say "I know the handshake will say a client cert is optional, don't present one"?

ETA: more complete output below

> openssl s_client -connect targetserver:8443

CONNECTED(00000003)
depth=1 CN = My Private CA
verify return:1
depth=0 CN = targetserver
verify return:1
140155024262984:error:14094412:SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate:s3_pkt.c:1275:SSL alert number 42
140155024262984:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:184:
---
Certificate chain
 0 s:/CN=targetserver
   i:/CN=My Private CA
---
Server certificate
-----BEGIN CERTIFICATE-----
{{encoded cert}}
-----END CERTIFICATE-----
subject=/CN=targetserver
issuer=/CN=My Private CA
---
Acceptable client certificate CA names
{{A bunch of CA names}}
Server Temp Key: ECDH, secp521r1, 521 bits
---
SSL handshake has read 7947 bytes and written 230 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-SHA
    Session-ID: {{bytes}}
    Session-ID-ctx:
    Master-Key: {{bytes}}
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1612284655
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

2
  • Can you please provide a more detailed output? Because what you describe is strange. The server certificate and acceptable client certificate CA names should come before a client certificate is expected by the server, i.e. the order you describe do not make much sense to me. – Steffen Ullrich Feb 2 at 16:43
  • I added more output to the OP. In case it matters, this is an old openssl binary, OpenSSL 1.0.1e-fips 11 Feb 2013, on a RHEL 6 server. Also in case it matters, what I'm actually trying to address is that I'm seeing the same "alert bad certificate" error when trying to connect from Python (again, without a client cert). – Coderer Feb 2 at 16:57
1

I don't see anything to tell me whether or not those errors (bad certificate / handshake failure) are fatal, i.e., if the client certificate was required and the connection cannot continue without it.

If an SSL alert is fatal or not depends on the AlertLevel send together with the alert. Unfortunately s_client does not show these information.

Would this information be any clearer in debug/verbose mode?

The debug mode will present the full contents of the messages in hex and one would need to decode it somehow. There is no easy way to access this information with s_client.

Do I need to pass a separate flag to say "I know the handshake will say a client cert is optional, don't present one"?

That's not how client certificates work. No matter if client certificates are optional or mandatory the server will always send a CertificateRequest. There is no way for the client to determine if the client certificate is mandatory. But if the client has no certificate it will simply send none, i.e. number of provided certificates is 0. The server then decides if it will accept no certificate or not, i.e. the optional vs. mandatory is only a server-side thing.

4
  • I think I understand. Let's say I configured two identical Apache servers, one with SSLClientVerify=require and one with SSLClientVerify=optional, then used s_client to connect to each without a cert param. Normal output from both would be the same, because in both cases the server would respond with an alert. But in debug mode, the hex value would be different, because in require mode it would be AlertLevel=fatal (0x02) and in optional mode it would be AlertLevel=warning (0x01). Is that right? – Coderer Feb 3 at 10:20
  • @Coderer: Might be or might be not. What I commonly see is that there is no alert at all if the certificate is optional and some critical alert or even simply a connection close if the certificate is required. – Steffen Ullrich Feb 3 at 10:50
  • So it's server-implementation-dependent? I was afraid of that. I'm not actually using Apache SSLClientVerify, it's github.com/apereo/java-cas-client on JBoss with optional token support. It definitely works from the browser -- you get prompted for a cert, and if you cancel the prompt it redirects you the username/password login page. But from python, or with s_client, it gives "alert bad certificate", and in python at least it's thrown as an exception. I'm trying to get a repro together to report it as a bug, and thought s_client could help... – Coderer Feb 3 at 10:57
  • @Coderer: yet, it depends on the server. You can try with openssl s_server using the -verify (optional) and -Verify (required certificate). – Steffen Ullrich Feb 3 at 11:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.