Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QSslSocket Behaviour Linux vs macOS (possibly LibreSSL incompatibilities?)



  • Hello all!

    Disclaimer: in case I'm in the wrong forums, please move this accordingly... thx

    I am rather new in Qt development; I developed/am developing an application using QTcpServer along with a client app. Instead of QTcpSockets QSslSocket is used for obvious reasons.

    Now I am experiencing issues with QSslSocket - while my server runs fine under Linux I get errors (QAbstractSocket Errors 20/21: Internal SSL error and invalid data, respectively.
    Now, as said: the server runs fine in Linux (OpenSuSE/Ubuntu); I believe all parameters have been set as required (evidenced by the absence of errors in Linux).

    I tried all sorts of things (e.g. invoking QSslSocket::ignoreSslErrors) - nothing seems to work.
    In the end I suspect the problem indeed lies in the openssl implementation used on the Mac (latest High Sierra; apparently LibreSSL 2.2.7 is in place there).

    By any chance - did anyone stumble across similar issues and can point me in the right direction? Any ideas on workarounds/solving greatly appreciated.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    What version of Qt are you using ?
    Is it self-built ?
    Since a few versions, Qt on macOS is built using a backend that uses the Secure Transport framework from Apple.



  • @SGaist

    Thank you....

    I'm using the latest Qt 5.10 installed via the official online installer. So, no, not self built.

    Edit: I might add: The implementation by and large seems rather picky. Adding / using certificates only works when using .pem endoded certificates. While using .crt is no problem in Linux, these result in error 13 (SSLHandshakeError) on the Mac


  • Lifetime Qt Champion

    Can you provide a minimal compilable example that shows that behaviour ?



  • @SGaist

    I guess this method is where the magic happens:

    void PCITcpConnections::accept(qintptr handle, PCITcpConnection *connection) {
    QSslSocket *socket = new QSslSocket(this);

    connect(socket, &QSslSocket::encrypted, connection, &PCITcpConnection::encrypted);
    connect(socket,&QSslSocket::disconnected, this, &PCITcpConnections::disconnected);
    connect(socket,static_cast<void (QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error),this,&PCITcpConnections::error);
    
    const auto cert = QSslCertificate::fromPath("perceptronics_cert.pem", QSsl::Pem, QRegExp::Wildcard);
    const auto CAcert = QSslCertificate::fromPath("CA_cert.pem", QSsl::Pem, QRegExp::Wildcard);
    
    socket->addCaCertificates(CAcert);
    socket->setPrivateKey("perceptronics_key.pem");
    socket->setLocalCertificate(cert.at(0));
    socket->setPeerVerifyMode(QSslSocket::VerifyNone);
    
    QSslError error(QSslError::SelfSignedCertificate, cert.at(0));
    QList<QSslError> expectedSslErrors;
    expectedSslErrors.append(error);
    socket->ignoreSslErrors(expectedSslErrors);
    
    qDebug() << " ... accepting connection";
    
    if(socket->setSocketDescriptor(handle)) {
        socket->startServerEncryption();
    } else {
        qWarning() << this << "could not accept connection" << handle;
        connection->deleteLater();
        return;
    }
    
    qDebug() << " Mode: " << socket->mode();
    
    connection->moveToThread(QThread::currentThread());
    connection->setSocket(socket);
    
    tcpConnections.insert(socket,connection);
    qDebug() << this << "clients = " << tcpConnections.count();
    emit socket->connected();
    

    }



  • Follow up:

    I compiled Qt from source using the latest openssl version (also compiled from source). Unfortunately no change in behaviour.


  • Lifetime Qt Champion

    I see you are using relative paths which means that these files might not be found at run time. For testing you should put the complete path to each of them.



  • @SGaist You are right - however, in the debugger I see the files are loaded. Except maybe the key file.
    I noticed something weird I am about to dig into: even though I set the peerVerifyName in my client, the peerVerifyName on the server is empty.

    Something must be wrong there. Still does not explain why it does work under Linux but fails on the Mac.
    I also noticed: on the server the connected state is preserved despite the mentioned errors, whereas on the client a network timeout is experienced after some seconds.


  • Lifetime Qt Champion

    Might be a silly question but did you explicitly select the OpenSSL backend when compiling Qt ? Also, did you linked to OpenSSL or load at runtime ?



  • @SGaist There are no silly questions - just maybe silly answers :-)

    1: yes, I set the OpenSSL backend when compiling.
    2: I linked.

    That said this is also something to look into; I am not fully clear how Qt handles SSL and if I did it correctly. Assuming its an SSL issue I wonder why my Mac client can connect to the Linux server without issues.
    But in general I agree, this might be the root cause. Unfortunately the error messages are not exactly verbose - it just says "internal SSL error"... could be anything


  • Lifetime Qt Champion

    1. Can you check with otool -L the resulting QtNetwork framework ? Just to ensure that it indeed used your version of OpenSSL and not the one from the system.

    Are you using a custom made certificate ?



  • @SGaist Here's the otool output:

    @rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.0)

    Yes, I use self signed certificates. That is, a self signed CA certificate which in turn have been used to sign the server cert. According to the KeyChain app the certs are ok (ie trusted)


  • Lifetime Qt Champion

    It looks incomplete. That looks like just the library id.

    I get:

    @rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.1)
    @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.10.0, current version 5.10.1)
    /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
    /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 58286.31.2)
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1450.15.0)
    /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 822.19.0)
    /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 963.30.1)
    /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)
    /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork (compatibility version 1.0.0, current version 893.13.1)
    


  • @SGaist Silly me; I only pasted the one line affecting QtNetwork. Sorry for that. Here we go:

    @rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.10.0, current version 5.10.0)
    @rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.10.0, current version 5.10.0)
    @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.10.0, current version 5.10.0)
    /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
    @rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.10.0, current version 5.10.0)
    /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)

    fyi: I reverted back to the standard Qt version, ie not using the self compiled one for now. Hence the output above might not be what you expected.

    For now, I try to figure out a couple of things (e.g why my peerValidName etc are not set on the socket) and how to load ssl at runtime


  • Lifetime Qt Champion

    Indeed, it was the one from your self-compiled Qt that is of interest.

    As for loading at run time, you pass the -openssl-runtime option.



  • @SGaist Thank you for now... appreciated.

    I am about to re-compile Qt which will take a couple of hours (about 4 to 5 hours yesterday) on a 2017 MBP 15...

    Will keep you advised on my efforts


  • Lifetime Qt Champion

    Don't compile all of Qt. For your testing you likely only need qtbase. If more, then build only the modules you need after qtbase (or pass a list of -skip options for all modules you don't use in your application).



  • @SGaist I am aware, thank you. That is exactly what I am researching right now - what modules to compile. Don't want to be too restrictive though 'cause if successful I'll use this version for my client and other apps, too.


  • Lifetime Qt Champion

    What modules are you using for it currently ?



  • @SGaist

    core, widgets and network. This is only for the server, the client also requires gui.

    Its just crazy, just "make clean" takes forever


  • Lifetime Qt Champion

    So qtbase is enough.

    Use out of source builds, so if you want to start from scratch you only have to nuke the build folder and the sources stay clean.



  • @SGaist edit: after hours of investigation I got it to configure/compile. I had to manually point/symlink the openssl files located in /usr/local/lib and /usr/local/include, respectively, to the directory where openssl actually sits.

    As it seems configure bails out for openssl versions north of 1.1.0. For some reason (no idea how this came about) I had openssl 1.1.1-pre-something installed.

    Follow up: the private Key is indeed set; however, something is wrong with it because Qt marks it "isNull". That is, I can qDebug it and at first glance it looks fine. Same on Linux.

    Follow up 2: I used the code as posted in
    https://forum.qt.io/topic/45728/generating-cert-key-during-run-time-for-qsslsocket/7

    Again - as @Pradeep-P-N reported, the code crashes in the final step (setPrivateKey); that is, the privateKey is null. The said is true on macOS using openssl 1.0.2n

    Follow up 3: Under Linux, a valid key is indeed created. However, the key does not seem to be relayed to the server. Keep digging...

    Follow up 4: Have not managed to create a key via openssl that could be loaded from disk - neither on Linux nor on the Mac. Not sure what's going on, the private Key just is always null (have tried various formats...). For now solely the code as posted in the linked thread above works under Linux.

    For now I suspect main the problem has to do with the linked openssl version on the Mac: Building gives lots of warnings: object file (libcrypto.a) ws built for newer OSX version (10.13) than being linked (10.10).

    If I get that correctly this means the openssl lib used to generate the keys was built on 10.13 whereas Qt's version was build on 10.10?


  • Lifetime Qt Champion

    OpenSSL 1.1 broke API and ABI compatibility with the 1.0 series.

    Up to Qt 5.10, only 1.0 was supported. Since 5.10, you can build the 1.1 backend but Qt is still delivered with 1.0 currently.

    IIRC, Qt is built with the latest version of Xcode at the time and has a run target of "Current release" -3 (the same number of versions that Apple still supports) following Apple's policy of "build with the latest version of Xcode".



  • @SGaist Hi. Happy to be able to report that part of the experienced issues are solved using Qt compiled from source (linked against OpenSSL 1.0.2n). More specifically, the server part now accepts connections, the socket errors (20, 21) are gone.
    The code segment posted in mentioned thread above now works on the Mac as well, i.e. i can generate private keys in code now.

    That said, the QSslKey loading problem still exists. In particular, its still fails to set a private key loaded from file. I wonder whats the root cause - to me it seems like its a bug in Qt. All the samples I found online do not seem to work...


  • Lifetime Qt Champion

    Would it possible for you to provide a small client/server project(s) that allows to test that behaviour ?



  • @SGaist I could provide the client code - which should be ok for testing this?


  • Lifetime Qt Champion

    In the absolute, having also a test server would be nice (doesn't need to be C++ for that part) as it would allow to be sure that a failing connection can properly be made.



  • @SGaist I understand; still its kind of difficult to just give out the sources since some of the tech used there (unrelated to connecting) is under heavy confidentiality requirements.

    That said, a server you could connect to is available via DynDNS (edit: tested - working)


  • Lifetime Qt Champion

    No worries, that's not what I was asking you to do at all !

    Just some dummy test server using the custom certificate so that the communication can be tested/established with the test client.



  • @SGaist Here we go: https://github.com/markusmeyerhofer/SimpleServer

    Just a heads-up: this is a quickly distilled ssl server; it should work but its certainly not tested exhaustively.

    The client: https://github.com/markusmeyerhofer/QtSSLClient

    I created a small project creating certs and keys (again, using the sample provided in the mentioned thread above) and writing those to disk.
    I imported a key generated this way (recall: this key is accepted as valid by QSslKey) into a resource file and load that file in my SSL client - where the exact same key is rejected ("isNull").

    Visual inspection (qDebugging out the loaded file) suggests an actually valid key file. As it stands it looks like something happens during loading the key file. Not sure what I'm doing wrong...
    (Hint: the respective code segment is in "citcpconnection.cpp" lines 27ff, see Github Repo QtSSLClient as given above)


  • Lifetime Qt Champion

    After some testing, the certificate itself is good. What poses problem currently is the key. Poking at the sources and searching a bit, the key you are generating is a PKCS8 type of key which doesn't seem to be handled currently. PKCS1 keys on the other should be good.

    I didn't had time to go through OpenSSL to find how to generate such a key from the API however on the command line it seems to be as simple as: openssl rsa -in server.key -out server_new.key.

    Hope it helps.



  • @SGaist Thx for the heads - up. What still puzzles me is: how is it that the key generated in code (its in the client code posted) is accepted when fed into QSslKey directly; when storing the key as is and subsequently loading from file it isn't.

    Anyway, its kinda tedious - there are so many formats and how tos out there I kinda lost oversight. What I need is basically:

    1 - A certificate (self-signed) acting as CA certificate.
    2 - A server and a client key + certs, signed by the CA cert as created in 1. I did that, but, as you know, the key isn't accepted.

    There are other issues I don't quite understand: e.g. the key, even though set in the client, does not seem to appear on the server; meaning it isn't used. Similarly, setting the peer host name on the client, getting peerHostName on the server happens to be empty. Not sure why...

    The connection still is encrypted, but host identification is defunct as of now, opening a hole


  • Lifetime Qt Champion

    Looking for something else, I stumble on this bug of Curl which I wonder if it relates to the problem at end.



  • @SGaist said in QSslSocket Behaviour Linux vs macOS (possibly LibreSSL incompatibilities?):

    bug of Curl

    I doubt it: the bug seems to address Yosemite; my issues are for one: on High Sierra and for two: consistent over various platforms (OpenSuSE, Ubuntu; Mac).

    Besides - why curl? I am not knowingly using curl for that project. Is it somehow used by Qt under the hood?



  • Hello.. as a follow up: I am not aware of the changes that took place under the hood in Qt 5.12.

    However, the issues as described apparently are fixed now; i.e. I am now using Qt 5.12.0 as provided/updated using the Qt Maintenance Tool.

    In more detail: I had to use the custom compiled Qt 5.10.0 in order to be able to connect to my QSsl - based development server under macOS.
    With the latest update to Qt 5.12.0 everything works as intended out of the box, no custom Qt/SSL compilation required any more.

    Thx for fixing this!


  • Lifetime Qt Champion

    Glad it's working now and thanks for the feedback !

    Additonal note, I never wrote that this was a curl problem or bug that was affecting your application. Only that the bug report I found could give some additonal clues to look into to fix your problem.



  • @SGaist Actually the post above may have been a bit premature. Not sure what is going on: I updated 2 machines to Qt 5.12.0.

    On my workstation, it is working as intended and described in the previous post.

    Since I assumed the issues have been fixed I also updated my Macbook only to find out the failed handshake still occurs.

    Currently I am in the process of investigating what is going on - mentioned computers installations are very similar. All I know for now is that the handshake failures occur only on one computer with standard Qt installation.
    I suspect the cause is somehow tied to libssl.a/libcrypto.a, perhaps different ssl version or something like that.

    I'll keep you advised if I find out what's going on


  • Lifetime Qt Champion

    The one problem I can see is that OpenSSL is not used on macOS since a few Qt versions. Qt use the SecureTransport framework from macOS. You can still use OpenSSL though but you have to build Qt yourself for that as well as get a recent version of OpenSSL.



  • @SGaist I know - I did compile Qt with OpenSSL 1.0.2o a couple of months ago (Qt 5.10.0).
    Unfortunately later versions Qt failed to compile using this config - never figured out why; for the lack of time I just went ahead using mentioned custom compiled version.

    Yesterday I randomly checked if my server compiles/works using 5.12.0 - and to my surprise it did (on my workstation).
    It still doesn't on the Macbook what puzzles me...

    otool -L indicates everything is similar. The binary compiled on the workstation also does not work on the notebook - so clearly some dynamically linked library causing this. If only I knew which one


  • Lifetime Qt Champion

    Any difference of macOS versions ?

    Any hint from the logs on the system ?

    Just in case, IIRC, since 5.10 Qt supports also OpenSSL 1.1


Log in to reply