Using SSL client certificate



  • Hello all,

    I would like to communicate with a webservice which requires client side certificate based authentication.
    The certificate comes in PKCS12# format.

    I have been able to extract the client certificate using openssl:
    @openssl pkcs12 -in 24d5bfcdbd55623894351e0dcdd6aee4f1a04a3d -clcerts -out x509.pem@

    My test code can be found below which reads the extracted certificate and tries to connect to the server:
    @QFile certFile("/home/mm/Projektek/qt/qtrafik/soap/certs/x509");
    certFile.open(QFile::ReadOnly);
    QSslCertificate cert(&certFile);
    certFile.close();

    qWarning() << cert.expiryDate() << cert.version() << cert.serialNumber() << cert.isNull() << cert.issuerInfo(QSslCertificate::Organization);
    qWarning() << cert.toText();
    QSslConfiguration configuration; 
    configuration.setLocalCertificate(cert);
    qWarning() << configuration.localCertificate().serialNumber();
    
    QNetworkRequest request;
    request.setSslConfiguration(configuration);
    request.setUrl(QUrl("https://adatkuldes-teszt.nemzetidohany.gov.hu:8444/nd/MasterData.svc?wsdl"));
    
    reply = manager->get(request);
    connect(reply, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(finished()), this, SLOT(finished()));@
    

    The loaded certificate information (cert.toText();) correlates 100% with the certificate details in the Firefox (I have been able to load the cert to my browser and access the server).

    Unfortunatelly my QNetworkManager emits the error(QNetworkReply::NetworkError) signal with 99 (QNetworkReply::UnknownNetworkError) error code with "Unable to init SSL Context: " errorString().

    I am working with Qt 5.0.1 on kUbuntu 13.04 64 bit.
    I have openssl installed, and the QSslSocket::supportsSsl() returns true.

    Any help, suggestion or hint will be highly appericiated!



  • I have compiled my test project with Qt 4.8.4, and it resulted a different error message:

    @Cannot provide a certificate with no key, @

    After some searching I realized that I have to extract the CA cert, and the private key from my PKCS12# file with openssl, and load them to my QSslConfiguration.

    To save it to the posterity this is how it is working for me now:

    @
    openssl pkcs12 -in 24d5bfcdbd55623894351e0dcdd6aee4f1a04a3d -clcerts -out clientcert.pem
    openssl pkcs12 -in 24d5bfcdbd55623894351e0dcdd6aee4f1a04a3d -nocerts -out privkey.key
    openssl pkcs12 -in 24d5bfcdbd55623894351e0dcdd6aee4f1a04a3d -cacerts -out cacert.pem
    @

    At the extraction of the private key I have given 1234 as password (see the code below.)

    @ QSslConfiguration configuration;

    QFile certFile&#40;"/home/mm/Projektek/qt/qtrafik/soap/certs/clientcert.pem"&#41;;
    certFile.open(QFile::ReadOnly&#41;;
    QSslCertificate cert(&certFile);
    certFile.close();
    configuration.setLocalCertificate(cert);
    
    QList<QSslCertificate> certs;
    certFile.setFileName("/home/mm/Projektek/qt/qtrafik/soap/certs/cacert.pem");
    certFile.open(QFile::ReadOnly);
    QSslCertificate caCert(&certFile);
    certFile.close();
    certs.append(caCert);
    configuration.setCaCertificates(certs);
    
    QFile keyFile&#40;"/home/mm/Projektek/qt/qtrafik/soap/certs/privkey.key"&#41;;
    keyFile.open(QFile::ReadOnly&#41;;
    QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "1234");
    qWarning() << key.isNull();
    configuration.setPrivateKey(key);
    keyFile.close();
    
    QNetworkRequest request;
    request.setSslConfiguration(configuration);
    request.setUrl(QUrl("https://adatkuldes-teszt.nemzetidohany.gov.hu:8444/nd/MasterData.svc?wsdl"));
    reply = manager->get(request);
    connect(reply, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(finished()), this, SLOT(finished()));@


  • I have been able to create a more sophisticated solution using the QCA (Qt cryptographics architecture see: http://delta.affinix.com/qca/). To be able to use it with Qt5 download the latest version of the code from here:
    git://anongit.kde.org/qca.git

    My SSL configuration initialization code is the following:
    @//initialize QCA
    QCA::init();

    if (QFile::exists(m_certificatePath)) {
        QCA::KeyBundle bundle(m_certificatePath, QCA::SecureArray(m_certificatePassword.toLocal8Bit()));
    
        QList<QSslCertificate> CACerts;
        foreach (QCA::Certificate cert, bundle.certificateChain()) {
            if (cert.isCA()) {
                QSslCertificate CACertificate(cert.toPEM().toLocal8Bit(), QSsl::Pem);
                CACerts.append(CACertificate);
            } else {
                m_SSLConfiguration.setLocalCertificate(QSslCertificate(cert.toPEM().toLocal8Bit(), QSsl::Pem));
            }
        }
    
        m_SSLConfiguration.setCaCertificates(CACerts);
    
        QSslKey key(bundle.privateKey().toDER().toByteArray(), QSsl::Rsa, QSsl::Der);
        m_SSLConfiguration.setPrivateKey(key);
    }@
    

    The application have to be linked with the QCA libraries (-lqca) and the Qt library search path have to contain the "crypto" folder of the compiled QCA library.



  • here is a clean way i used without a 3rd party lib.

    bool loadPfxCertifcate(QString certFilename, QString passphrase) {
    
        QFile certFile(certFilename);
        certFile.open(QFile::ReadOnly);
        QSslCertificate certificate;
        QSslKey key;
        QList<QSslCertificate> importedCerts;
    
        bool imported = QSslCertificate::importPkcs12(&certFile, &key, &certificate, &importedCerts, QByteArray::fromStdString(passphrase.toStdString()));
    
        certFile.close();
    
        qDebug() << "Imported cert:" << imported;
    
        if (imported) {
            QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
            QList<QSslCertificate> certs = sslConfig.caCertificates();
            QList<QSslCertificate> localCerts = sslConfig.localCertificateChain();
            localCerts.append(certificate);
            certs.append(importedCerts);
    
            sslConfig.setLocalCertificateChain(localCerts);
            sslConfig.setCaCertificates(certs);
            sslConfig.setPrivateKey(key);
            QSslConfiguration::setDefaultConfiguration(sslConfig);
        }
    
        return imported;
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.