Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to make Qt reinitialize the OpenSsl engine, or let me take ownership of it?

How to make Qt reinitialize the OpenSsl engine, or let me take ownership of it?

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 3 Posters 479 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    Hristo Konstantinov
    wrote on last edited by Hristo Konstantinov
    #1

    I'm using OpenSsl and load pkcs11 engine for my application, since the client Ssl should be built with the private key from the Hardware Security Module (a.k.a. a flash drive with a sim card - a requirement from the government website I'm building the client for). I also need it for some XML digital signatures.
    I can't control the life time of the session, since the user can plug and unplug the usb whenever he wants. If I don't use QNetworkAccessManager, everything works okay.If I see that the x509 certificate from the HSM is empty, it means that the usb has been unplugged and I try to initialize it again, whenever user needs it. The message I get in the debug console on destruction is as follows:

    Exception thrown at 0x00007FFF1A4B474C (KernelBase.dll) in PisHis.exe: 0x80100002: The action was cancelled by an SCardCancel request.
    Exception thrown at 0x00007FFF1A4B474C (KernelBase.dll) in PisHis.exe: 0x0000071A: The remote procedure call was canceled, or if a call time-out was specified, the call timed out.
    Exception thrown at 0x00007FFF1A4B474C in PisHis.exe: Microsoft C++ exception: unsigned long at memory location 0x0000006E83AFEEF8.
    Exception thrown at 0x00007FFF1A4B474C in PisHis.exe: Microsoft C++ exception: unsigned long at memory location 0x0000006E83AFF024.
    Exception thrown at 0x00007FFF1A4B474C in PisHis.exe: Microsoft C++ exception: unsigned long at memory location 0x0000006E834FF7F8.
    'PisHis.exe' (Win32): Unloaded 'C:\Dev\IDPrimePKCS1164.dll'
    'PisHis.exe' (Win32): Unloaded 'C:\Dev\pkcs11.dll'
    

    Obviously the pkcs11 engine and the hsm module (IDPrime by Gemalto) have been unloaded and everything's cool.

    HOWEVER if I send just one single QNetworkRequest, everything goes to hell (the destructor of my custom openssl engine wrapper doesn't free the libraries), and I cannot reinitialize the engine. I'm guessing somehow Qt gets ownership of the engine and starts using it, so reinitialization of the engine by me is not possible (it gives me "could not bind to the requested symbol name" error). Is there any way to make Qt ditch the engine or just let me handle it by myself? One workaround I could think of is to keep my engine as static and notify the user with "The HSM could not be loaded, please restart the program", when the usb is accidentally unplugged but that's just plain stupid.

    My custom OpenSsl class constructor:

    CryptoSslEngine::CryptoSslEngine()
    {
       
        ERR_load_crypto_strings();
        SSL_load_error_strings();
        OpenSSL_add_all_algorithms();
        SSL_library_init();
        ENGINE_load_dynamic();
        ERR_clear_error();
     
        p_engine = ENGINE_by_id("dynamic");
        if (!p_engine) {
            throw std::exception(
                ERR_reason_error_string(ERR_get_error())
            );
        }
    
        if (!ENGINE_ctrl_cmd_string(p_engine, "SO_PATH", "C:/Dev/pkcs11.dll", 0) ||
            !ENGINE_ctrl_cmd_string(p_engine, "ID", "pkcs11", 0) ||
            !ENGINE_ctrl_cmd_string(p_engine, "LIST_ADD", "1", 0) ||
            !ENGINE_ctrl_cmd_string(p_engine, "LOAD", NULL, 0) ||
            !ENGINE_ctrl_cmd_string(p_engine, "MODULE_PATH", "c:/dev/IDPrimePKCS1164.dll", 0) ||
            !ENGINE_ctrl_cmd_string(p_engine, "VERBOSE", NULL, 1) ||
            !ENGINE_init(p_engine)// ||
          //  !ENGINE_set_default(p_engine, ENGINE_METHOD_ALL)
            )
        {
            ENGINE_free(p_engine);
    
            throw std::exception(
                ERR_reason_error_string(ERR_get_error()) //gives "could not bind to the requested symbol name"
            );
        }
    }
    

    The destructor (which DOESN'T work, if QNetworkAccessManager has been used at least once):

    CryptoSslEngine::~CryptoSslEngine()
    {
        if (p_engine) {
            ENGINE_unregister_RSA(p_engine);
            ENGINE_unregister_ciphers(p_engine);
            ENGINE_unregister_digests(p_engine);
            ENGINE_finish(p_engine);
            ENGINE_remove(p_engine);
            ENGINE_free(p_engine);
            ENGINE_cleanup();
        }
      
    }
    

    And finally, how I use the whole thing:

    void PisHis::sendRequest()
    {
         //the QNetworkAccessManager is a member variable and those two functions at least prevent some crashes
         manager.clearAccessCache();
         manager.clearConnectionCache();
    
        CryptoSslEngine engine;
     
        auto certString = engine.Ssl_x509cert();
    
        if (certString.empty()){
            showMessage("No HSM found");
            return; //the end of the scopes calls the destructor and releases the engine
        }
    
        QSslCertificate cert(certString.data());
        QSslKey key = QSslKey(Qt::HANDLE(engine.getPrivateKey()));
        
        if (key.isNull()){
            qDebug() << "PIN dialog from the module has been canceled";
            return;
        }
    
        QSslConfiguration config = QSslConfiguration::defaultConfiguration();
        config.setProtocol(QSsl::SslProtocol::TlsV1_2);
        config.setLocalCertificate(cert);
        config.setPrivateKey(key);
    
        QUrl url("https://pis.nhif.bg/ws/PISService");
       
        QNetworkRequest request(url);
        request.setSslConfiguration(config);
        request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "text/xml;charset=\"utf-8\"");
        request.setRawHeader("SOAPAction", "\"http://pis.technologica.com/view\"");
        request.setRawHeader("accept", "\"application/xml\"");
    
        manager.post(request, getQuery());
        
    }
    

    I've seen the whole idea from this project here: https://github.com/iksaif/qsslkey-p11
    However the author states that he uses a PATCHED version of Qt, which I assume fixes the problem, but the link to it is no longer available.

    kshegunovK 1 Reply Last reply
    0
    • H Offline
      H Offline
      Hristo Konstantinov
      wrote on last edited by Hristo Konstantinov
      #5

      I kind of managed to make it work . The freeing of the engine has to be followed by deleting the QNetworkAccessManager and creating a new one. However I still have problems with programmatically managing the engine. The destructor has to track whether the private key from the HSM has to be accessed or not:

      CryptoSslEngine::~CryptoSslEngine()
      {
              if (prvKeyHasBeenAccessed)
              {
                ENGINE_finish(engine);
                ENGINE_free(engine);
                ENGINE_remove(engine);
      
                ENGINE_unregister_ciphers(engine);
                ENGINE_unregister_digests(engine);
                ENGINE_finish(engine);
      
                
              }
              else
              {
                  ENGINE_finish(engine);
                  ENGINE_free(engine);
                  ENGINE_remove(engine);
              }
      }
      

      I have no idea why I have to call those functions in this particular order, but that way it works. I've noticed that only when accessing the HSM private key the module gets loaded, and then if I want to destroy the engine I have to call ENGINE_free() twice (once for the module and once for the pkcs11 engine). Probably I'll have to try and use Libp11 from OpenSC to manage these things for me, but that's not part of the Qt anyway.

      1 Reply Last reply
      0
      • H Offline
        H Offline
        Hristo Konstantinov
        wrote on last edited by
        #2

        Ok, it seems that the problem is not in QNetworkAccessManager, but in QSslKey itself. The constructor which takes a HANDLE to the key from the pkcs11 hsm doesn't release it afterwards(as written in the documentation) and freeing the engine manually has no effect, since its reference count is held up by the QNetwork backend. If there was only a way to re-initialize the whole QNetwork module, or make the SslKey release the handle, it would be great!

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #3

          Hi,

          I don't have a solution at hand but did you try to contact the author of the original code ? He may still have his patches lying around.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          0
          • H Hristo Konstantinov

            I'm using OpenSsl and load pkcs11 engine for my application, since the client Ssl should be built with the private key from the Hardware Security Module (a.k.a. a flash drive with a sim card - a requirement from the government website I'm building the client for). I also need it for some XML digital signatures.
            I can't control the life time of the session, since the user can plug and unplug the usb whenever he wants. If I don't use QNetworkAccessManager, everything works okay.If I see that the x509 certificate from the HSM is empty, it means that the usb has been unplugged and I try to initialize it again, whenever user needs it. The message I get in the debug console on destruction is as follows:

            Exception thrown at 0x00007FFF1A4B474C (KernelBase.dll) in PisHis.exe: 0x80100002: The action was cancelled by an SCardCancel request.
            Exception thrown at 0x00007FFF1A4B474C (KernelBase.dll) in PisHis.exe: 0x0000071A: The remote procedure call was canceled, or if a call time-out was specified, the call timed out.
            Exception thrown at 0x00007FFF1A4B474C in PisHis.exe: Microsoft C++ exception: unsigned long at memory location 0x0000006E83AFEEF8.
            Exception thrown at 0x00007FFF1A4B474C in PisHis.exe: Microsoft C++ exception: unsigned long at memory location 0x0000006E83AFF024.
            Exception thrown at 0x00007FFF1A4B474C in PisHis.exe: Microsoft C++ exception: unsigned long at memory location 0x0000006E834FF7F8.
            'PisHis.exe' (Win32): Unloaded 'C:\Dev\IDPrimePKCS1164.dll'
            'PisHis.exe' (Win32): Unloaded 'C:\Dev\pkcs11.dll'
            

            Obviously the pkcs11 engine and the hsm module (IDPrime by Gemalto) have been unloaded and everything's cool.

            HOWEVER if I send just one single QNetworkRequest, everything goes to hell (the destructor of my custom openssl engine wrapper doesn't free the libraries), and I cannot reinitialize the engine. I'm guessing somehow Qt gets ownership of the engine and starts using it, so reinitialization of the engine by me is not possible (it gives me "could not bind to the requested symbol name" error). Is there any way to make Qt ditch the engine or just let me handle it by myself? One workaround I could think of is to keep my engine as static and notify the user with "The HSM could not be loaded, please restart the program", when the usb is accidentally unplugged but that's just plain stupid.

            My custom OpenSsl class constructor:

            CryptoSslEngine::CryptoSslEngine()
            {
               
                ERR_load_crypto_strings();
                SSL_load_error_strings();
                OpenSSL_add_all_algorithms();
                SSL_library_init();
                ENGINE_load_dynamic();
                ERR_clear_error();
             
                p_engine = ENGINE_by_id("dynamic");
                if (!p_engine) {
                    throw std::exception(
                        ERR_reason_error_string(ERR_get_error())
                    );
                }
            
                if (!ENGINE_ctrl_cmd_string(p_engine, "SO_PATH", "C:/Dev/pkcs11.dll", 0) ||
                    !ENGINE_ctrl_cmd_string(p_engine, "ID", "pkcs11", 0) ||
                    !ENGINE_ctrl_cmd_string(p_engine, "LIST_ADD", "1", 0) ||
                    !ENGINE_ctrl_cmd_string(p_engine, "LOAD", NULL, 0) ||
                    !ENGINE_ctrl_cmd_string(p_engine, "MODULE_PATH", "c:/dev/IDPrimePKCS1164.dll", 0) ||
                    !ENGINE_ctrl_cmd_string(p_engine, "VERBOSE", NULL, 1) ||
                    !ENGINE_init(p_engine)// ||
                  //  !ENGINE_set_default(p_engine, ENGINE_METHOD_ALL)
                    )
                {
                    ENGINE_free(p_engine);
            
                    throw std::exception(
                        ERR_reason_error_string(ERR_get_error()) //gives "could not bind to the requested symbol name"
                    );
                }
            }
            

            The destructor (which DOESN'T work, if QNetworkAccessManager has been used at least once):

            CryptoSslEngine::~CryptoSslEngine()
            {
                if (p_engine) {
                    ENGINE_unregister_RSA(p_engine);
                    ENGINE_unregister_ciphers(p_engine);
                    ENGINE_unregister_digests(p_engine);
                    ENGINE_finish(p_engine);
                    ENGINE_remove(p_engine);
                    ENGINE_free(p_engine);
                    ENGINE_cleanup();
                }
              
            }
            

            And finally, how I use the whole thing:

            void PisHis::sendRequest()
            {
                 //the QNetworkAccessManager is a member variable and those two functions at least prevent some crashes
                 manager.clearAccessCache();
                 manager.clearConnectionCache();
            
                CryptoSslEngine engine;
             
                auto certString = engine.Ssl_x509cert();
            
                if (certString.empty()){
                    showMessage("No HSM found");
                    return; //the end of the scopes calls the destructor and releases the engine
                }
            
                QSslCertificate cert(certString.data());
                QSslKey key = QSslKey(Qt::HANDLE(engine.getPrivateKey()));
                
                if (key.isNull()){
                    qDebug() << "PIN dialog from the module has been canceled";
                    return;
                }
            
                QSslConfiguration config = QSslConfiguration::defaultConfiguration();
                config.setProtocol(QSsl::SslProtocol::TlsV1_2);
                config.setLocalCertificate(cert);
                config.setPrivateKey(key);
            
                QUrl url("https://pis.nhif.bg/ws/PISService");
               
                QNetworkRequest request(url);
                request.setSslConfiguration(config);
                request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "text/xml;charset=\"utf-8\"");
                request.setRawHeader("SOAPAction", "\"http://pis.technologica.com/view\"");
                request.setRawHeader("accept", "\"application/xml\"");
            
                manager.post(request, getQuery());
                
            }
            

            I've seen the whole idea from this project here: https://github.com/iksaif/qsslkey-p11
            However the author states that he uses a PATCHED version of Qt, which I assume fixes the problem, but the link to it is no longer available.

            kshegunovK Offline
            kshegunovK Offline
            kshegunov
            Moderators
            wrote on last edited by
            #4

            @Hristo-Konstantinov said in How to make Qt reinitialize the OpenSsl engine, or let me take ownership of it?:

            QSslKey key = QSslKey(Qt::HANDLE(engine.getPrivateKey()));
            

            This will take ownership of the handle, make sure that the engine is not going to delete it. Perhaps there's something like takePrivateKey() or something?

            Otherwise the code looks decent to me.

            Read and abide by the Qt Code of Conduct

            1 Reply Last reply
            0
            • H Offline
              H Offline
              Hristo Konstantinov
              wrote on last edited by Hristo Konstantinov
              #5

              I kind of managed to make it work . The freeing of the engine has to be followed by deleting the QNetworkAccessManager and creating a new one. However I still have problems with programmatically managing the engine. The destructor has to track whether the private key from the HSM has to be accessed or not:

              CryptoSslEngine::~CryptoSslEngine()
              {
                      if (prvKeyHasBeenAccessed)
                      {
                        ENGINE_finish(engine);
                        ENGINE_free(engine);
                        ENGINE_remove(engine);
              
                        ENGINE_unregister_ciphers(engine);
                        ENGINE_unregister_digests(engine);
                        ENGINE_finish(engine);
              
                        
                      }
                      else
                      {
                          ENGINE_finish(engine);
                          ENGINE_free(engine);
                          ENGINE_remove(engine);
                      }
              }
              

              I have no idea why I have to call those functions in this particular order, but that way it works. I've noticed that only when accessing the HSM private key the module gets loaded, and then if I want to destroy the engine I have to call ENGINE_free() twice (once for the module and once for the pkcs11 engine). Probably I'll have to try and use Libp11 from OpenSC to manage these things for me, but that's not part of the Qt anyway.

              1 Reply Last reply
              0
              • H Offline
                H Offline
                Hristo Konstantinov
                wrote on last edited by
                #6

                UPDATE: I've ditched completely the engine part, by just using libp11. Wrote a simple C++ wrapper around it, and now everything works without problems. However I still have to "reset" QNetworkAccessManager, but calling ClearAccessCache() does the job perfectly.

                1 Reply Last reply
                2

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved