QNetwork: making queries using QSslConfiguration and PKCS12
-
Hi ! I'm trying to connect to a web service that uses PKCS12 to authentify its clients. There's a method in QSslConfiguration to import such certificates (importPkcs12), but I've been unsuccessful in using it properly: depending on the SSL protocol I select, the remote server either fails to complete the handshake (with TLSv1_0), or closes the connection (with any other protocol).
I've made a small piece of code to reproduce the issue:
#include <QApplication> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QSslConfiguration> #include <QSslKey> #include <QJsonDocument> #include <QJsonObject> #include <QFile> int main(int argc, char** argv) { QApplication app(argc, argv); QNetworkAccessManager network; QNetworkRequest request; QNetworkReply* reply; QSslConfiguration sslConfiguration; // // load p12 certificate // QFile p12_certificate("Batuz_LROE_Ziurtagiriak_KIT_Certificados_V1_0_2/EnpresaZigilua_SelloDeEmpresa.p12"); if (p12_certificate.open(QIODevice::ReadOnly)) { QList<QSslCertificate> certificateChain; QSslCertificate certificate; QSslKey sslKey; QByteArray password("IZDesa2021"); if (QSslCertificate::importPkcs12(&p12_certificate, &sslKey, &certificate, &certificateChain, password)) { sslConfiguration.QSslConfiguration::defaultConfiguration(); sslConfiguration.setProtocol(QSsl::AnyProtocol); sslConfiguration.setLocalCertificate(certificate); sslConfiguration.setLocalCertificateChain(certificateChain); sslConfiguration.setPrivateKey(sslKey); sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); } else qDebug() << "failed to import p12 certificate"; } else qDebug() << "failed to read p12 certificate"; // eus-bizkaia-n3-data QJsonObject drs_data{ {"ejer", "2022"}, {"mode", "240"} }; QJsonObject inte_data{ {"ap1", "John"}, {"ap2", "Doe"}, {"nif", "A12345678"}, {"nrs", "abcdefghijklmopqrstuvwxyzA6qLu"} }; QJsonObject n3_data{ {"apa", "1.1"}, {"con", "LROE"}, {"drs", drs_data}, {"inte", inte_data} }; // // http headers // request.setUrl(QUrl("https://pruesarrerak.bizkaia.eus/N3B4000M/aurkezpena")); request.setSslConfiguration(sslConfiguration); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream"); request.setHeader(QNetworkRequest::ContentLengthHeader, 10); request.setRawHeader("Content-Encoding", "gzip"); request.setRawHeader("Accepted-Encoding", "gzip"); request.setRawHeader("eus-bizkaia-n3-version", "1.0"); request.setRawHeader("eus-bizkaia-n3-content-type", "application/xml"); request.setRawHeader("eus-bizkaia-n3-data", QJsonDocument(n3_data).toJson()); // // performing the query // reply = network.post(request, "0123456789"); QObject::connect(reply, &QNetworkReply::finished, [reply]() { unsigned int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt(); qDebug() << "received response" << status; if (status != 200) qDebug() << "error:" << reply->errorString(); }); return app.exec(); }
It's designed to connect to the test environment for the LROE service (something from a local government in Spain).
The sample certificate used in this code can be found in the following archive:
https://www.batuz.eus/fitxategiak/batuz/lroe/batuz_lroe_ziurtagiriak_kit_certificados_v1_0_2.7zI found an implementation of this service developed in Python, and I've refactored it into a small program to check if I was able to get things working with what I had at hand. I believe the following code should do the same thing as the Qt program above... yet this one actually works:
# Must install request_pkcs12 using "pip install requests-pkcs12" from requests_pkcs12 import post as pkcs12_post import json # eus-bizkaia-n3-data data = {} data["apa"] = "1.1" data["con"] = "LROE" data["drs"] = {} data["drs"]["ejer"] = "2022" data["drs"]["mode"] = "240" data["inte"] = {} data["inte"]["ap1"] = "John" data["inte"]["ap2"] = "Doe" data["inte"]["nif"] = "A99805061" data["inte"]["nrs"] = "5Yd5j4iyKM7qjJnDKVAptFTF6A6qLu" # http headers headers = {} headers["Content-Type"] = "application/octet-stream" headers["Content-Length"] = "802332" headers["Content-Encoding"] = "gzip" headers["eus-bizkaia-n3-version"] = "1.0" headers["eus-bizkaia-n3-content-type"] = "application/xml" headers["eus-bizkaia-n3-data"] = "{}" # p12 certificate p12_password = "IZDesa2021" p12_buffer = open("Batuz_LROE_Ziurtagiriak_KIT_Certificados_V1_0_2/EnpresaZigilua_SelloDeEmpresa.p12", "rb").read() # performing the query response = pkcs12_post( "http://pruesarrerak.bizkaia.eus/N3B4000M/aurkezpena", data="0123456789", headers=headers, pkcs12_data=p12_buffer, pkcs12_password=p12_password )
So, worst case scenario, I can just write a Python program that I'll call from my Qt program.
But I'd much rather perform these tasks entirely in Qt. So here's a bottle thrown into the ocean, hoping to find the help that I so desperately need !