Qt World Summit: Submit your Presentation

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

    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 to example.host.com viaconnectToHostEncrypted() in both QNetworkAccessmanager and QSslSocket connection is made and with setPeerVerifyMode( QSslSocket::VerifyNone) I can connect (MiTM enabled, I know, but those certificates are self signed). But when connecting to example.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 failure

    That'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 # Works, certificate is not fully verified though.
    openssl s_client -connect -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).


  • 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

    turning on non blocking io
    write R BLOCK
    read R BLOCK
    read R BLOCK
    read R BLOCK
    read R BLOCK
    Certificate chain
     0 s:/ 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
    -----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
        Protocol  : TLSv1
        Cipher    : DHE-RSA-AES256-SHA
        Session-ID: 530..............
        Master-Key: F12..........
        Key-Arg   : None
        Start Time: 1..............
        Timeout   : 300 (sec)
        Verify return code: 0 (ok)
    <title>400 Bad Request</title>
    <h1>Bad Request</h1>
    <p>Your browser sent a request that this server could not understand.<br />

    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.setLocalCertificate( QSslCertificate());
        sslConfiguration.setPrivateKey( QSslKey());
        // dont verify peer
        sslConfiguration.setPeerVerifyMode( QSslSocket::VerifyNone);
        QNetworkRequest req;
        req.setUrl( QUrl()); 
        QNetworkAccessManager->post( req, postData);

    And if QSslSocket::VerifyNone is set socket enters encrypted state (with QSslSocket::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.

Log in to reply