Certificate issue
-
Hello,
I do have a strange issue. I do connect to server that require client certificate to verify client. And what's strange is that it do work fine on Windows but on OsX I do get error:Error occured QNetworkReply::NetworkError(UnknownNetworkError) "SSLRead failed: -9842"
I thought that this may be issue with OpenSSL version on OsX so I did updated from 0.9.8zh to 1.0.2g but exactly same error occur.
On Windows I do use: 1.0.2d.Client certificates are given to me, so I don't know if they are actually valid. Testes access url on web browsers (Safari / Firefox) both on Windows and OsX resulted in handshake failure and on Safari, after importing client cert to keychain, they did not showed up on list). Honestly I do suspect that they are not valid with is even more puzzling that OpenSSL on win will allow connection.
-
Hi @LuGRU
SSL libraries can be a real pain to debug - often because the won't report any meaning error codes (most codes translate to little more then "read failed").
Anyway, if I understand you correctly, then a third-party has supplied you with an SSL client certificate, and now you're trying to use that client certificate to connect to that third-party's server using Qt? And this is working fine on one Windows machine, but not on an OSX machine?
I suspect you'll find that its the server's certificate that OSX has an issue with, rather than the client certificate. Put another way, the clients (both Window and OSX) will use the client certificate (and key) quite happily without any concept of trust, however, whether or not the clients trust the server's certificate will depend on many host-specific things, such as the client's list of trusted root CAs, and the completeness of the server's certificate chain.
Anyway, if I were you, I'd start with OpenSSL's s_client command to test if this is something host-specific, or application specific.
Something like:
openssl -connect example.com:443 -cert clientcert.pem
If that fails, you should at least be able to see why (you can add a
-debug
too if that helps). Once you get that working, then, then we can figure out if there's something else prevent your app from handshaking.Good luck :)
-
Thank You @Paul
Issue seams to be related to DNS.
openssl s_client -connect example.host.com/end/point -cert $cert -key $key -state -nbio
results in
gethostbyname failure connect:errno=0
so switching to IP, i.e.
openssl s_client -connect 123.456.789.099/end/point -cert $cert -key $key -state -nbio
correctly connect to peer and I do get 400 as end point accepts only POST request - response is in HTML with is readable so all fine.
End point problem, full URI is like
example.host.com/snd/point
. When connecting toexample.host.com
viaconnectToHostEncrypted()
in both QNetworkAccessmanager and QSslSocket connection is made and withsetPeerVerifyMode( QSslSocket::VerifyNone)
I can connect (MiTM enabled, I know, but those certificates are self signed). But when connecting toexample.host.com/end/point
same error occur"SSLRead failed: -9842"
Any idea why this happens?
-
Thank You @Paul
Issue seams to be related to DNS.
openssl s_client -connect example.host.com/end/point -cert $cert -key $key -state -nbio
results in
gethostbyname failure connect:errno=0
so switching to IP, i.e.
openssl s_client -connect 123.456.789.099/end/point -cert $cert -key $key -state -nbio
correctly connect to peer and I do get 400 as end point accepts only POST request - response is in HTML with is readable so all fine.
End point problem, full URI is like
example.host.com/snd/point
. When connecting toexample.host.com
viaconnectToHostEncrypted()
in both QNetworkAccessmanager and QSslSocket connection is made and withsetPeerVerifyMode( QSslSocket::VerifyNone)
I can connect (MiTM enabled, I know, but those certificates are self signed). But when connecting toexample.host.com/end/point
same error occur"SSLRead failed: -9842"
Any idea why this happens?
openssl s_client -connect example.host.com/end/point -cert $cert -key $key -state -nbio
results in gethostbyname failureThat's because the
-connect
argument takes a hostname and port. You've provided a hostname with trailing URL path (ie the '/end/point'). So, openssl is trying to resolve the "example.host.com/end/point" hostname. You need to drop the "/end/point", and add a port.For example:
openssl s_client -connect www.google.com/index # Not okay. openssl s_client -connect www.google.com/index:443 # Not okay. openssl s_client -connect www.google.com:443 # Okay :)
so switching to IP, i.e.
openssl s_client -connect 123.456.789.099/end/point -cert $cert -key $key -state -nbio
That still shouldn't work because of the invalid
-connect
format (what version of openssl are you using?).But regardless, the issue with IP addresses here, is that
s_client
won't attempt to verify the host's "subject" (ie the hostname).For example, for one of www.google.com's IP addresses:
openssl s_client -connect 198.142.186.222:443 # Works, certificate is not fully verified though. openssl s_client -connect 198.142.186.222:443 -verify_hostname example.com # Fails as expected.
So what output do you get when run:
openssl s_client -connect example.host.com:443 -cert $cert -key $key -state -nbio
If you can't show the whole output (understandable), what about just:
openssl s_client -connect example.host.com:443 -cert $cert -key $key -state -nbio | grep Verify
Finally, I'm curious about how you got a 400 error via openssl? Did you manually type (or cat) something like "GET / HTTP/1.0\n\n"? Or was you're openssl doing a whole lot more than mine does? (Just trying to make sure we're talking about the same things).
Cheers.
-
Thank You for info.
I tried more test today, and indeed I forgot about port in post (with I used in script) in connect statement.
Also I thought example.host.com/end/point:port is valid syntax.Anyways, here is output for:
openssl s_client -connect example.host.com:443 -cert $cert -key $key -state -nbio
CONNECTED(00000003) turning on non blocking io write R BLOCK read R BLOCK read R BLOCK read R BLOCK read R BLOCK --- Certificate chain 0 s:/1.3.6.1.4.1.311.60.2.1.3=xx/businessCategory=Private Organization/O=xxxxx xx/serialNumber=xxxxxxxxx/C=xx/ST=xxxxx/L=xxxx/OU=IT/CN=aaaaaa i:/C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3 1 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA 2 s:/C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3 i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA --- Server certificate -----BEGIN CERTIFICATE----- xxxxxxxxxxxxxxxxxxxxxxxxxxxx -----END CERTIFICATE----- subject=/xxxxxxxxxxxxxxxxxx=xxxx/businessCategory=Private Organization/O=xxxxxxxxxxxxx /serialNumber=xxxxxxx/C=xxxx/ST=xxxx/L=xxxxx/OU=IT/CN=aaaaaa issuer=/C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3 --- No client certificate CA names sent --- SSL handshake has read 4643 bytes and written 328 bytes --- New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : DHE-RSA-AES256-SHA Session-ID: 530.............. Session-ID-ctx: Master-Key: F12.......... Key-Arg : None Start Time: 1.............. Timeout : 300 (sec) Verify return code: 0 (ok) --- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>400 Bad Request</title> </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> 045</p> </body></html> closed
On OsX OpenSSL 0.9.8zh (14 Jan 2016).
When running openssl I do see "Turning on non blocking io" and after server OK I can write so I just type some random keys + hit enter and I do get 400 from web server.Now strange part is that I'm able to connect from Qt to
example.host.com
, i.e. (example code - based on actual impl. simplified, showing methods invoked):QSslConfiguration sslConfiguration; sslConfiguration.setDefaultConfiguration(QSslConfiguration::defaultConfiguration()); sslConfiguration.setLocalCertificate( QSslCertificate()); sslConfiguration.setPrivateKey( QSslKey()); // dont verify peer sslConfiguration.setPeerVerifyMode( QSslSocket::VerifyNone); QNetworkRequest req; req.setUrl( QUrl()); req->setSslConfiguration(sslConfiguration); //... QNetworkAccessManager->post( req, postData);
And if
QSslSocket::VerifyNone
is set socket enters encrypted state (withQSslSocket::VerifyPeer
fails as expected, both certificates are self signed so SNI will not work correctly?). Now this is not end point that POST data should go, so connecting to example.host.com/end/point is required and with that set to QUrl() in QRequest error is returned:"SSLRead failed: -9842"
-
Well, it's a bit late to reply but anyway ... The TLS back-end in question is SecureTransport, not OpenSSL. Previously we did not have the logic needed to handle a renegotiation. So SSLRead was not expected to return errSSLClientCertRequested (-9842). This sneaky (but reasonable after all) behavior is not documented and the fact that SSLRead is doing handshake also is anything, but obvious. This was fixed recently in Qt - now we can detect that SSLRead finds HelloRequest and can handle the following renegotiation properly.