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

curl ssl request to qt ssl request



  • I am trying to access a file from a remote system that has ssl authentication using a provided pfx/pem passphrase-protected file and a CA certificate. I have been success in using curl to access this file using either of the following commands:

    curl --cert-type P12 --cert ./client.pfx:PASSWORD --cacert ./client.crt https://server/
    curl --cert ./client.pem:PASSWORD --cacert ./client.crt https://server/
    

    I am now trying to do the same under Qt 5.9.7 under Linux (stuck using this version). Currently I am using the pfx file since I cannot find a way to read the passphrased pem and get both the key and certificate (is it possible in Qt), but I can use QSslCertificate::importPkcs12 with pfx file. This is what I have tried:

    #include <qcoreapplication.h>
    #include <qfile.h>
    #include <qnetworkaccessmanager.h>
    #include <qnetworkconfiguration.h>
    #include <qnetworkproxy.h>
    #include <qnetworkreply.h>
    #include <qnetworkrequest.h>
    #include <qsslcertificate.h>
    #include <qsslconfiguration.h>
    #include <qsslkey.h>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        QString qstrPfx = "./client.pfx";
        if( argc > 1 ) qstrPfx = argv[1];
    
        QString qstrPasswd = "";
        if( argc > 2 ) qstrPasswd = argv[2];
    
        QString qstrCACert = "./client.crt";
        if( argc > 3 ) qstrCACert = argv[3];
    
        QString qstrUrl = "https://server/";
        if( argc > 4 ) qstrUrl = argv[4];
    
        QSslConfiguration sslConfiguration;
    
        QFile cacertificateFile(qstrCACert);
        cacertificateFile.open(QIODevice::ReadOnly);
        QSslCertificate cacertificate(&cacertificateFile);
        cacertificateFile.close();
    
        QFile pfxFile(qstrPfx);
        pfxFile.open(QIODevice::ReadOnly);
        QSslCertificate certificate;
        QSslKey key;
        QList<QSslCertificate> importedCerts;
        bool imported = QSslCertificate::importPkcs12(&pfxFile, &key, &certificate,
                                                      &importedCerts,
                                                      qstrPasswd.toLatin1());
        pfxFile.close();
        qWarning() << "imported:" << imported;
    
        //QFile privateKeyFile("./client.key");
        //privateKeyFile.open(QIODevice::ReadOnly);
        //QSslKey privateKey(&privateKeyFile, QSsl::Rsa, QSsl::Pem,
        //                   QSsl::PrivateKey, qstrPasswd.toLatin1());
        //privateKeyFile.close();
        //qWarning() << "SslKey:" << privateKey;
    
        qWarning() << "-----------------------------------";
        qWarning() << "ssl:" << QSslSocket::supportsSsl();
        qWarning() << key;
        qWarning() << certificate;
        qWarning() << cacertificate;
        qWarning() << "-----------------------------------";
    
        sslConfiguration.setPrivateKey(key);
        sslConfiguration.setLocalCertificate(certificate);
    
        QList<QSslCertificate> caCerts = sslConfiguration.caCertificates();
        caCerts.append(importedCerts);
        caCerts.append(cacertificate);
        sslConfiguration.setCaCertificates(caCerts);
    
        qWarning() << "Url:" << qstrUrl;
        QNetworkRequest networkRequest(QUrl(qstrUrl.toLatin1().constData()));
    
        if( !qstrPfx.isEmpty() ) networkRequest.setSslConfiguration(sslConfiguration);
    
        QNetworkAccessManager networkAccessManager;
    
        QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
        //networkReply->ignoreSslErrors();
    
        QFile * pf = new QFile( "download" );
        pf->open( QIODevice::WriteOnly );
        QObject::connect( networkReply,
                          &QNetworkReply::readyRead,
                          [networkReply,pf]() {
                              pf->write( networkReply->readAll() );
                          } );
    
        QEventLoop loop;
    
        QObject::connect(&networkAccessManager, &QNetworkAccessManager::finished,
                         &loop, &QEventLoop::quit);
    
        loop.exec();
    
        pf->close();
        qWarning() << networkReply->error();
        qWarning() << networkReply->errorString();
    
        delete networkReply;
        delete pf;
    }
    

    When I run this app with "./app ./client.pfx PASSWORD ./client.crt https://server/", imported shows true and the display of the certificates look correct but it always generates QNetworkReply::NetworkError(SslHandshakeFailedError) and "SSL handshake failed" when printing the error results. If i set ignoreSslErrors, then it works, NoError and download is correct.

    This is my first attempt at using ssl for downloading, so I might be missing a step. Just not sure what else to try to resolve this.



  • Try to connect QNetworkReply::sslErrors signal and print more info from QSslError::error() and QSslError::errorString().



  • Great suggestion.

    error = 22
    errorString = "The host name did not match any of the valid hosts for this certificate"

    I did a search and it looks like most folks just ignore this error by adding QSslError::HostNameMismatch to list of expected/ignored errors and passing that to ignoreSslErrors. Is that the proper way forward?

    Is curl doing something similar since it works fine there? Just wondering what the actual error is to avoid adding any vulnerabilities to the connection.



  • ok, I think i figured out why I am getting the host name mismatch. We have had some problems with our DNS on campus so I have been using IP addresses to specify the server. So I believe that is were the error is coming from. When I switched back to the hostname, it did not generate the HostNameMismatch error. I guess curl is doing something internal to a catch that.

    not sure if this needs to be a separate topic, but wrt passphrased PEM file, is there a way in Qt to get the certificate and key? It seems for passphrased files, Qt only has QSslCertificate::importPkcs12 which does all but you need a pks12 file or QSslKey::QSslKey and you just get a key but no certificate.



  • Just finishing this off. It looks like the passphrase is really only needed for the private key information. The same file can be used (or reread) to process the certificate.

                QFile certFile("./client.pem");
                certFile.open(QIODevice::ReadOnly);
                QSslKey key( &certFile,
                             QSsl::Rsa,
                             QSsl::Pem,
                             QSsl::PrivateKey,
                             qstrPasswd.toLatin1() );
                certFile.seek(0);    // rewind to beginning for reread
                QSslCertificate certificate( &certFile, QSsl::Pem );
                certFile.close();
    

Log in to reply