Reasons for OpenSSL client not sending certificate

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Reasons for OpenSSL client not sending certificate

Stephan Menzel
Hi all,

I am struggling with a client certificate problem and hope somebody can help me. I'm on windows here and develop a client/server pair using boost asio but I'll try to be unspecific. I am also not an expert in SSL speak so please forgive if I'm using layman's terms here. I'm on windows and using openssl 1.0.1e

Basically, I want to have client authentication. The server shall only accept clients that have a certificate signed by my own CA. So I have setup a self signed CA. This has issued two more certificates. One for the client and one for the server. Both signed by the CA.
I have done that quite a few times now and I am confident that I got it.

My server side also works fine. It requests client certificates and if I'm using s_client and give those certs everything works. Also if I'm using a browser and have my root CA installed as trusted and then import the client certs.

The only thing that I can't get to work is the libssl client. It always fails during the handshake and as far as I can see it will not send the client certficate:

$ openssl.exe s_server -servername localhost -bugs -CAfile myca.crt -cert server.crt -cert2 server.crt -key private/server.key -key2 private/server.key -accept 8887 -www -state -Verify 5
verify depth is 5, must return a certificate
Setting secondary ctx parameters
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
SSL3 alert read:warning:no certificate
SSL3 alert write:fatal:handshake failure
SSL_accept:error in SSLv3 read client certificate B
SSL_accept:error in SSLv3 read client certificate B
2675716:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate:s3_srvr.c:3193:
ACCEPT

I'm using this s_server as debugging tool but against my real server the same thing occurs.
s_client will work fine with the same certificates. Also, if I disable "-Verify" in the server the connection works. So it really seems just the client refusing to send it's certficate. What can be the reason for that?

Since I'm using boost asio as an SSL wrapper the code looks like this:

        m_ssl_context.set_verify_mode( asio::ssl::context::verify_peer );
        m_ssl_context.load_verify_file( "myca.crt" );
        m_ssl_context.use_certificate_file( "testclient.crt", asio::ssl::context::pem );
        m_ssl_context.use_private_key_file( "testclient.key", asio::ssl::context::pem );

I have also tried to bypass asio and access the SSL context directly by saying:

        SSL_CTX *ctx = m_ssl_context.impl();
        SSL *ssl = m_ssl_socket.impl()->ssl;
        int res = 0;
        res = SSL_CTX_use_certificate_chain_file(ctx, "myca.crt");
        if (res <= 0) {
            // handle error
        }
        res = SSL_CTX_use_certificate_file(ctx, "testclient.crt", SSL_FILETYPE_PEM);
        if (res <= 0) {
            // handle error
        }
        res = SSL_CTX_use_PrivateKey_file(ctx, "testclient.key", SSL_FILETYPE_PEM);
        if (res <= 0) {
            // handle error
        }

I can't see any difference in behavior. It should be mentioned that I am using a very old boost 1.43 asio which I cannot update but I suppose all relevant calls go more or less directly to OpenSSL anyway and the server works fine with that version so I think I can rule that out.

I have been debugging this for almost a week now and I'm really stuck. Does anyone have a suggestion on what to try? I'm out of ideas...

Cheers,
Stephan




PS: Here is what the above s_server debug out would be with s_client and the same certficate:

$ openssl s_client -CAfile ca.crt -cert testclient.crt -key private/testclient.key -verify 2 -connect myhost:8887

ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
depth=1 C = DE, // further info
verify return:1
depth=0 C = DE, // further info
verify return:1
SSL_accept:SSLv3 read client certificate A
SSL_accept:SSLv3 read client key exchange A
SSL_accept:SSLv3 read certificate verify A
SSL_accept:SSLv3 read finished A
SSL_accept:SSLv3 write session ticket A
SSL_accept:SSLv3 write change cipher spec A
SSL_accept:SSLv3 write finished A
SSL_accept:SSLv3 flush data
ACCEPT

... handshake completes and data is transferred.

LN
Reply | Threaded
Open this post in threaded view
|

using openssl API in commercial apps

LN
Hi,

Is it allowed to use the OpenSSL API in commercial applications ? What license governs the OpenSSL library ?

Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: Reasons for OpenSSL client not sending certificate

Stephan Menzel
In reply to this post by Stephan Menzel
I have continued debugging this today and can add an observation that might be of interest.
First I forced the protocol on both client and server to TLS1. Then I experimented with different combinations of SSL_CTX_use_certificate*

I have also created what I think is a certificate chain by concatenating my client certificate and the CA certificate that issued it.

When I do this:
SSL_CTX_use_certificate_chain_file(ctx, "chain.crt");

instead of
SSL_CTX_use_certificate_file(ctx, "clientcert.crt", SSL_FILETYPE_PEM);

I get further in the protocol that eventually ends with the message

SSL_accept:SSLv3 flush data
<<< TLS 1.0 Handshake [length 063d], Certificate

    ...lots of base64
    1c c0 a4 63 88 3e 7d 2d a1 f6 cb e5 a4 89 43 cf
    0b 1d e8 65 69 c9 32 6a 5d 37 38 8b 1a
depth=0 C = DE, // ***** lots of info ****
verify return:1
SSL_accept:SSLv3 read client certificate A
<<< TLS 1.0 Handshake [length 0046], ClientKeyExchange
    10 00 00 42 41 04 bf bd c4 0c 78 e7 c1 2d 20 47
    d3 0a 7c 7c 1b ae af bc 6d 6d 4c f5 83 43 d9 2c
    91 6e a3 19 f5 53 f4 18 cb 3b df f7 8f 71 33 a1
    cc 57 28 e4 cd 16 e1 f5 6d 06 b0 8b 1a ff 0c 82
    56 86 63 86 25 2c
SSL_accept:SSLv3 read client key exchange A
<<< TLS 1.0 ChangeCipherSpec [length 0001]
    01
<<< TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c 24 9b 6d fa f2 4c 08 0f fb 2d 57 8b
>>> TLS 1.0 Alert [length 0002], fatal unexpected_message
    02 0a
SSL3 alert write:fatal:unexpected_message
SSL_accept:failed in SSLv3 read certificate verify B
2675716:error:140880AE:SSL routines:SSL3_GET_CERT_VERIFY:missing verify message:s3_srvr.c:2951:
2675716:error:140940E5:SSL routines:SSL3_READ_BYTES:ssl handshake failure:s3_pkt.c:989:

Interestingly, if I do the same thing with the s_client tool, the output differs a little:

    6a 6f 78 78 54 27 b6 c0 f6 cc 43 72 32 fb 94 b4
    7c 08 b2 67 bf 06 f9 5a 99 f0 bf 79 7f 69 2e f6
    f4 a9 31 b4 e2 ce 0e 09 fe 25 1d 13 49 17 2c c8
    d8 83 47 7f 9b fe 31 4b 0e 95 d5 f5 ed 3d
depth=1 C = DE, // ***** lots of info *******
verify return:1
depth=0 C = DE, // ***** lots of info *******
verify return:1
SSL_accept:SSLv3 read client certificate A
<<< TLS 1.0 Handshake [length 0046], ClientKeyExchange
    10 00 00 42 41 04 57 d1 78 67 df 6f 64 d9 0e d0
    d3 95 d0 8a 05 13 52 0f 73 9c e1 a9 64 e6 45 d5
... handshake completes

An interesting difference is the lines starting with "depth=". I cannot print the actual info there but there's two such lines with the s_client tool and only one with my client. Also, s_client seems to send both certificates. Where I put in that comment I see the textual description of first the root CA (after depth=1) and then my client cert (after depth=0).

My client however only causes one such line and the info there is the root CA. His own client certificate doesn't appear. That leads me to believe that the client doesn't trust his own certificate and therefore doesn't send it. But since the root CA is in the chain, he sends this instead. Which in turn s_server will not accept since it doesn't state "SSL client" as purpose. Can that be? And if so, why will my client not send the certificate while the s_client will? Not really a big step forward but at this point I'm glad for half a baby step.

Cheers,
Stephan




On Mon, Jun 10, 2013 at 10:06 AM, Stephan Menzel <[hidden email]> wrote:
Hi all,

I am struggling with a client certificate problem and hope somebody can help me. I'm on windows here and develop a client/server pair using boost asio but I'll try to be unspecific. I am also not an expert in SSL speak so please forgive if I'm using layman's terms here. I'm on windows and using openssl 1.0.1e

Basically, I want to have client authentication. The server shall only accept clients that have a certificate signed by my own CA. So I have setup a self signed CA. This has issued two more certificates. One for the client and one for the server. Both signed by the CA.
I have done that quite a few times now and I am confident that I got it.

My server side also works fine. It requests client certificates and if I'm using s_client and give those certs everything works. Also if I'm using a browser and have my root CA installed as trusted and then import the client certs.

The only thing that I can't get to work is the libssl client. It always fails during the handshake and as far as I can see it will not send the client certficate:

$ openssl.exe s_server -servername localhost -bugs -CAfile myca.crt -cert server.crt -cert2 server.crt -key private/server.key -key2 private/server.key -accept 8887 -www -state -Verify 5
verify depth is 5, must return a certificate
Setting secondary ctx parameters
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
SSL3 alert read:warning:no certificate
SSL3 alert write:fatal:handshake failure
SSL_accept:error in SSLv3 read client certificate B
SSL_accept:error in SSLv3 read client certificate B
2675716:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate:s3_srvr.c:3193:
ACCEPT

I'm using this s_server as debugging tool but against my real server the same thing occurs.
s_client will work fine with the same certificates. Also, if I disable "-Verify" in the server the connection works. So it really seems just the client refusing to send it's certficate. What can be the reason for that?

Since I'm using boost asio as an SSL wrapper the code looks like this:

        m_ssl_context.set_verify_mode( asio::ssl::context::verify_peer );
        m_ssl_context.load_verify_file( "myca.crt" );
        m_ssl_context.use_certificate_file( "testclient.crt", asio::ssl::context::pem );
        m_ssl_context.use_private_key_file( "testclient.key", asio::ssl::context::pem );

I have also tried to bypass asio and access the SSL context directly by saying:

        SSL_CTX *ctx = m_ssl_context.impl();
        SSL *ssl = m_ssl_socket.impl()->ssl;
        int res = 0;
        res = SSL_CTX_use_certificate_chain_file(ctx, "myca.crt");
        if (res <= 0) {
            // handle error
        }
        res = SSL_CTX_use_certificate_file(ctx, "testclient.crt", SSL_FILETYPE_PEM);
        if (res <= 0) {
            // handle error
        }
        res = SSL_CTX_use_PrivateKey_file(ctx, "testclient.key", SSL_FILETYPE_PEM);
        if (res <= 0) {
            // handle error
        }

I can't see any difference in behavior. It should be mentioned that I am using a very old boost 1.43 asio which I cannot update but I suppose all relevant calls go more or less directly to OpenSSL anyway and the server works fine with that version so I think I can rule that out.

I have been debugging this for almost a week now and I'm really stuck. Does anyone have a suggestion on what to try? I'm out of ideas...

Cheers,
Stephan




PS: Here is what the above s_server debug out would be with s_client and the same certficate:

$ openssl s_client -CAfile ca.crt -cert testclient.crt -key private/testclient.key -verify 2 -connect myhost:8887

ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
depth=1 C = DE, // further info
verify return:1
depth=0 C = DE, // further info
verify return:1
SSL_accept:SSLv3 read client certificate A
SSL_accept:SSLv3 read client key exchange A
SSL_accept:SSLv3 read certificate verify A
SSL_accept:SSLv3 read finished A
SSL_accept:SSLv3 write session ticket A
SSL_accept:SSLv3 write change cipher spec A
SSL_accept:SSLv3 write finished A
SSL_accept:SSLv3 flush data
ACCEPT

... handshake completes and data is transferred.


Reply | Threaded
Open this post in threaded view
|

RE: using openssl API in commercial apps

J. J. Farrell-2
In reply to this post by LN

Have you tried googling for 'openssl license' or reading the second paragraph of the OpenSSL home page on the web?

 

Regards,

                          jjf

 

From: LN [mailto:[hidden email]]
Sent: Monday, June 10, 2013 3:25 PM
To: [hidden email]
Subject: using openssl API in commercial apps

 

Hi,

 

Is it allowed to use the OpenSSL API in commercial applications ? What license governs the OpenSSL library ?

 

Thanks!

Reply | Threaded
Open this post in threaded view
|

RE: Reasons for OpenSSL client not sending certificate

Dave Thompson-5
In reply to this post by Stephan Menzel
>From: [hidden email] On Behalf Of Stephan Menzel
>Sent: Monday, 10 June, 2013 11:04

>I have continued debugging this today and can add an observation
>that might be of interest.
       
>First I forced the protocol on both client and server to TLS1.

1.0 specifically, or just 1-series i.e. not SSL3 (or <gack> 2)?
TLS-any should have no effect on selecting and using a client cert,
although it does change the way of expressing lack of (usable) cert
from no C-Cert message at all to a message with an empty sequence,
which you can see in a trace or log if you look closely.

It may be desirable or helpful for other reasons, and on s_server
1.0 vs 1.1 1.2 does make the -msg display nicer.

>Then I experimented with different combinations of SSL_CTX_use_certificate*
>I have also created what I think is a certificate chain by concatenating
>my client certificate and the CA certificate that issued it.

Concatenated in what order? See below.

And you say below the CA is your own private root. There is no need
to send a root, because the peer will never trust a sent root,
only a root it has locally in its truststore. Especially since your
client and server are both using your own CA, see below.

>When I do this:
>SSL_CTX_use_certificate_chain_file(ctx, "chain.crt");
>instead of
>SSL_CTX_use_certificate_file(ctx, "clientcert.crt", SSL_FILETYPE_PEM);
>I get further in the protocol that eventually ends with the message

[apparently in s_server -msg -state, although you didn't say so]
       
>SSL_accept:SSLv3 flush data
><<< TLS 1.0 Handshake [length 063d], Certificate
<snip>
>depth=0 C = DE, // ***** lots of info ****
>verify return:1
>SSL_accept:SSLv3 read client certificate A

"lots of info" is the Subject name, see below.

><<< TLS 1.0 Handshake [length 0046], ClientKeyExchange
<snip>

>SSL_accept:SSLv3 read client key exchange A
><<< TLS 1.0 ChangeCipherSpec [length 0001]
>    01
><<< TLS 1.0 Handshake [length 0010], Finished
>    14 00 00 0c 24 9b 6d fa f2 4c 08 0f fb 2d 57 8b
>>>> TLS 1.0 Alert [length 0002], fatal unexpected_message
>    02 0a
>SSL3 alert write:fatal:unexpected_message
>SSL_accept:failed in SSLv3 read certificate verify B
>2675716:error:140880AE:SSL routines:SSL3_GET_CERT_VERIFY:
>missing verify message:s3_srvr.c:2951:
>2675716:error:140940E5:SSL routines:SSL3_READ_BYTES:
>ssl handshake failure:s3_pkt.c:989:

With client auth and a nonstatic key exchange, there
should be a client CertVerify message after ClientKX
before CCS. I have no clue why it's not there.

You talk about bypassing asio in doing the SSL_CTX setup;
can I assume the SSL_connect also goes through or is done
by asio? From the name, can I guess asio uses nonblocking
sockets and multiple calls? If so, it's conceivable it's
clobbering some data somewhere that's confusing OpenSSL.
Or possibly asio interposes something in the socket I/O
that openssl does that causes this message to get lost
maybe some kind of filter BIO instead of socket-BIO??

>Interestingly, if I do the same thing with the s_client tool,
>the output differs a little:
<snip hex>
>depth=1 C = DE, // ***** lots of info *******
>verify return:1
>depth=0 C = DE, // ***** lots of info *******
>verify return:1
>SSL_accept:SSLv3 read client certificate A

That should be your client at depth=0 and the CA at depth=1 ...

><<< TLS 1.0 Handshake [length 0046], ClientKeyExchange
>... handshake completes

Does it show CertificateVerify *then* ChangeCipherSpec & Finished?
       
>An interesting difference is the lines starting with "depth=". I cannot
>print the actual info there but there's two such lines with the s_client
>tool and only one with my client. Also, s_client seems to send both
>certificates. Where I put in that comment I see the textual description of
>first the root CA (after depth=1) and then my client cert (after depth=0).

... and it is. See below about s_client.
       
>My client however only causes one such line and the info there is
>the root CA. His own client certificate doesn't appear. That leads me
>to believe that the client doesn't trust his own certificate and
>therefore doesn't send it. But since the root CA is in the chain,
>he sends this instead. Which in turn s_server will not accept since
>it doesn't state "SSL client" as purpose. Can that be? And if so,
>why will my client not send the certificate while the s_client will?
>Not really a big step forward but at this point I'm glad for half
>a baby step.

There's no trust decision by the client about his own cert (or the
server for hers). But his cert does need to match his privatekey.
Did you put the CA root cert *first* in chain.crt? If so that tells
openssl to use the root cert as the client's cert, which would result
in sending only that cert as you report (there is no chain beyond
a root), but it should have caused _use_PrivateKey_file to error
before you got to any handshake, and your code indicated handling for
that. Unless you somehow used the same privatekey for CA and client?

I'm not sure offhand s_server (and openssl server) will reject a
client cert because EKU excludes SSL-client, but if it does it
would not be an error anything like the one you are getting.

s_client behaves differently because it can't specify a chain file
at all. Instead you specify the client cert file and openssl fills
in the chain (if possible) using its *truststore* (even though
it's arguable this isn't exactly the purpose of the truststore).
See below.

>On Mon, Jun 10, 2013 at 10:06 AM, Stephan Menzel <[hidden email]>
wrote:

>I am struggling with a client certificate problem and hope somebody
>can help me. I'm on windows here and develop a client/server pair
>using boost asio but I'll try to be unspecific. I am also not an expert
>in SSL speak so please forgive if I'm using layman's terms here.
>I'm on windows and using openssl 1.0.1e

You're doing pretty well. I have corrected only one point.

>Basically, I want to have client authentication. The server shall only
>accept clients that have a certificate signed by my own CA. So I have
>setup a self signed CA. This has issued two more certificates. One for
>the client and one for the server. Both signed by the CA.

>I have done that quite a few times now and I am confident that I got it.

>My server side also works fine. It requests client certificates and if
>I'm using s_client and give those certs everything works. Also if I'm
>using a browser and have my root CA installed as trusted and then
>import the client certs.

You mean root CA cert and client cert-AND-KEY, perhaps combined as
a PKCS#12. The client cert by itself is useless for authentication.

>The only thing that I can't get to work is the libssl client. It always
>fails during the handshake and as far as I can see it will not send
>the client certficate:
               
>$ openssl.exe s_server -servername localhost -bugs -CAfile myca.crt
>-cert server.crt -cert2 server.crt -key private/server.key
>-key2 private/server.key -accept 8887 -www -state -Verify 5

<snip>
> SSL3 alert read:warning:no certificate
> SSL3 alert write:fatal:handshake failure
> SSL_accept:error in SSLv3 read client certificate B
> SSL_accept:error in SSLv3 read client certificate B
> 2675716:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:
>peer did not return a certificate:s3_srvr.c:3193:

>I'm using this s_server as debugging tool but against my real server
>the same thing occurs.
               
>s_client will work fine with the same certificates. Also, if I disable
>"-Verify" in the server the connection works. So it really seems just
>the client refusing to send it's certficate. What can be the reason for
that?
               
>Since I'm using boost asio as an SSL wrapper the code looks like this:

> m_ssl_context.set_verify_mode( asio::ssl::context::verify_peer );
> m_ssl_context.load_verify_file( "myca.crt" );
> m_ssl_context.use_certificate_file( "testclient.crt",
asio::ssl::context::pem );
> m_ssl_context.use_private_key_file( "testclient.key",
asio::ssl::context::pem );

>I have also tried to bypass asio and access the SSL context directly by
saying:

> SSL_CTX *ctx = m_ssl_context.impl();
> SSL *ssl = m_ssl_socket.impl()->ssl;

Is an SSL pointer actually set here? See below.

> int res = 0;
> res = SSL_CTX_use_certificate_chain_file(ctx, "myca.crt");
> if (res <= 0) {
>  // handle error
> }
This is (almost certainly) not the analog of load_verify_file.
You want SSL_CTX_load_verify_locations (ctx, "myca.crt", NULL)
to set a CAfile and no CApath. However, I'm surprised this
didn't cause the client to reject the server before even
attempting to authenticate itself (during handshake) --
unless you have set or defaulted it somewhere else,
or put in a callback that overrides the verify error.

_use__chain_file of (only) a CA cert is completely wrong,
but fortunately immediately overridden by your next call.

> res = SSL_CTX_use_certificate_file(ctx, "testclient.crt",
SSL_FILETYPE_PEM);
> if (res <= 0) {
>  // handle error
> }
> res = SSL_CTX_use_PrivateKey_file(ctx, "testclient.key",
SSL_FILETYPE_PEM);
> if (res <= 0) {
>  // handle error
> }

Those two calls, assuming no error, should have caused libssl to send
the client cert (and the chain=root if available) and use the key --
as long as they occur before the SSL object is created from the SSL_CTX,
and no subsequent calls modify these settings in the SSL object.

The asio calls certainly look like they should do the same thing,
but probably only looking at asio itself can make that certain.

>I can't see any difference in behavior. It should be mentioned that I am
>using a very old boost 1.43 asio which I cannot update but I suppose all
>relevant calls go more or less directly to OpenSSL anyway and the server
>works fine with that version so I think I can rule that out.

Can you reasonably try bypassing asio for the SSL_connect, and maybe
a data I/O? If that gives the same problem, at least you have a much
simpler (no-asio) test case to debug. If it works, it tends to point
to the problem being in asio, which probably doesn't help much.

>PS: Here is what the above s_server debug out would be with s_client
>and the same certficate:
               
>$ openssl s_client -CAfile ca.crt -cert testclient.crt
>-key private/testclient.key -verify 2 -connect myhost:8887
<snip good>

Is ca.crt here the same as myca.crt above? Since you specify -key&cert
for the client, and -CAfile (presumably) the CA cert, this results in
s_client completing and sending the chain as above -- although as above,
it isn't needed because the server ignores this copy of the root.

I notice you show key files under private/ some places but not others.
Is that just for posting, or could these actually be different?


______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: Reasons for OpenSSL client not sending certificate

Stephan Menzel
Dave, thanks so much for your response.

I really was on the verge of giving up on this.
Let me start by saying that it is solved. You gave the hint that made the difference. Also, you let me understand more what was going on. You have pointed out some apparent mistakes that were indeed caused by copy and pasting and on several occasions editing stuff to omit sensitive information. What really made the difference was this:

On Tue, Jun 11, 2013 at 9:12 AM, Dave Thompson <[hidden email]> wrote:
> SSL_CTX *ctx = m_ssl_context.impl();
> SSL *ssl = m_ssl_socket.impl()->ssl;

Is an SSL pointer actually set here? See below.

Yes. Asio wraps it in a ssl_socket type object. I can access it any time. Which turned out to be a hint towards the real problem. See below.
 
> int res = 0;
> res = SSL_CTX_use_certificate_chain_file(ctx, "myca.crt");
> if (res <= 0) {
>  // handle error
> }
This is (almost certainly) not the analog of load_verify_file.
You want SSL_CTX_load_verify_locations (ctx, "myca.crt", NULL)
to set a CAfile and no CApath. However, I'm surprised this
didn't cause the client to reject the server before even
attempting to authenticate itself (during handshake) --
unless you have set or defaulted it somewhere else,
or put in a callback that overrides the verify error.

_use__chain_file of (only) a CA cert is completely wrong,
but fortunately immediately overridden by your next call.

> res = SSL_CTX_use_certificate_file(ctx, "testclient.crt",
SSL_FILETYPE_PEM);
> if (res <= 0) {
>  // handle error
> }
> res = SSL_CTX_use_PrivateKey_file(ctx, "testclient.key",
SSL_FILETYPE_PEM);
> if (res <= 0) {
>  // handle error
> }

Those two calls, assuming no error, should have caused libssl to send
the client cert (and the chain=root if available) and use the key --
as long as they occur before the SSL object is created from the SSL_CTX,
and no subsequent calls modify these settings in the SSL object.

That's the point. As long as the SSL object is created _after_ the context was modified. That was not the case. The asio code created the context and the socket upon it (that wraps the ssl*) in the initializer list, pretty much making all my modifications afterwards obsolete. As they were all in the c'tor. My server code did it right but the client was wrong. I have verified it by replacing the SSL_CTX_something(ctx...) functions with SSL_something(ssl... ) and now it works just fine. I will restructure it to reflect this and try to get back to the asio versions so I don't have to bypass that layer but in any case, it works.
 
I can now use the SSL connection and client certs and everything. The magic silver bullet indeed. And I have to say, at least in this version asio almost encourages this mistake in my opinion. Also, there's no equivalent SSL_something() functions on the socket like OpenSSL has, only the ones on the context and they were always ignored. Also, there's nothing in asio's docs hinting at this essential fact.

Well, I can utter a huge sigh of relief now and than you very much for your help.

Cheers,

Stephan