Minimal working example of QWebEngine with client certificates? - c++

I am trying to get client certificates working with the Qt web engine (version 5.15), but I can't seem to get it done!
I have a local server set up to use SSL, to expect client certificates and check them against a locally created root CA certificate. The client on the other hand, has a client certificate, signed by this local root CA.
This is as far as I can whittle it down ... which might be too far, causing the problems!
#include <QApplication>
#include <QtWebEngine>
#include <QWebEngineProfile>
#include <QWebEngineView>
class MyWebPage : public QWebEnginePage
{
public:
MyWebPage(QObject * Parent) :
QWebEnginePage{Parent}
{
QObject::connect(this, &QWebEnginePage::selectClientCertificate, this, &MyWebPage::SelectClientCertificate);
}
protected:
auto certificateError(QWebEngineCertificateError const & CertificateError) -> bool override
{
return true;
}
auto SelectClientCertificate(QWebEngineClientCertificateSelection ClientCertificateSelection) -> void
{
ClientCertificateSelection.select(ClientCertificateSelection.certificates().front());
}
};
auto main(int argc, char *argv[]) -> int
{
QtWebEngine::initialize();
auto Application = QApplication{argc, argv};
auto WebView = QWebEngineView{};
WebView.resize(1024, 750);
WebView.show();
auto ClientCertificateFile = QFile{"client.pem"};
ClientCertificateFile.open(QIODevice::ReadOnly);
auto ClientCertificate = QSslCertificate{&ClientCertificateFile};
ClientCertificateFile.close();
auto PrivateKeyFile = QFile{"client.key"};
PrivateKeyFile.open(QIODevice::ReadOnly);
auto PrivateKey = QSslKey{&PrivateKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey};
PrivateKeyFile.close();
auto WebPage = MyWebPage{&WebView};
WebPage.profile()->clientCertificateStore()->add(ClientCertificate, PrivateKey);
WebPage.setUrl(QUrl{"https://localhost/"});
WebView.setPage(&WebPage);
return Application.exec();
}
What I get is an error message in the browser window with "ERR_SSL_CLIENT_AUTH_NO_COMMON_ALGORITHMS" - so apparently browser and server don't know how to talk to each other.
I've tried curl and it works fine with either
curl --tlsv1.2 --tls-max 1.2 --insecure https://localhost/ --cert client.certificate.pem --key client.private_key.key
or
curl --tlsv1.3 --tls-max 1.3 --insecure https://localhost/ --cert client.certificate.pem --key client.private_key.key
I've tried deactivating client certificates on the server side and it works fine.
Are there any problems with my example? Did I miss some initialization steps or something?
Maybe my example WOULD be working - if only the client and server knew how to talk to each other, so that, consequently, the error really is in the cipher suites?! Is there any way I can check the exchanged and possible algorithms? Ultimately, I find that very unlikely, as we're talking about a Chrome backend on the client side talking to an Apache server, both of which are hugely popular ...
Any hints?
Thank you for any help you can offer and best regards!

Related

GRPC CreateChannel() error Could not get default pem root certs

I am using grpc 1.35.0 on Windows 10 and following sample code here to create a grpc channel for client to use. But I have a provide a root cert to create the channel, otherwise it complains below error.
Then I write my client in a python version and I can create the channel without giving root cert.
So, is this a grpc bug or I misunderstand the sample code?
GRPC sample code
// Create a default SSL ChannelCredentials object.
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// Create a channel using the credentials created in the previous step.
auto channel = grpc::CreateChannel(server_name, channel_creds);
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);
my code
const std::string SECURE_GRPC_CHANNEL_ADDRESS = <MY_SERVER>;
class GrpcChannel
{
GrpcChannel()
{
auto ca_cert = get_file_contents(cacert_path);
SslCredentialsOptions options = { ca_cert, "", "" };
auto channel_creds = SslCredentials(options);
channel_ = grpc::CreateChannel(SECURE_GRPC_CHANNEL_ADDRESS, channel_creds);
}
Turns out it's grpc document issue, grpc-core C++ for windows does not support default root cert and need specify one by user. Please refer to here.

How to know which certificate(s) QNetworkAccessManager uses during an SSL/TLS handshake?

I am using the class QNetworkAccessManager to establish an HTTPS connexion with a remote server. The connexion works well but i will need to update the certificates eventually.
I don't know much about SSL and i would like to clarify one point.
According to http://doc.qt.io/qt-5/qnetworkaccessmanager.html#encrypted, the initial SSL handshake seems to works fine because the following slot connected to the QNetworkAccessManager signal encrypted(QNetworkReply*) is called :
void MyAccessManager::onEncrypted(QNetworkReply *rep)
{
Q_UNUSED(rep)
qDebug() << "SSL/TLS handshake success";
}
This is an embedded Linux on which several .crt files are already installed in /usr/share/ca-certificates/mozilla/ and a list of .pem files in /etc/ssl/certs/
My understanding is that Qt is able to locate those files automatically because when i get the QSslConfiguration from the QNetworkRequest i can see the list of QSslCertificate in use, for example with :
QSslConfiguration conf = myNetworkRequest.sslConfiguration();
for (int i = 0; i < conf.caCertificates().size(); i++)
qDebug() << conf.caCertificates().at(i).subjectInfo(QSslCertificate::Organization);
and the output i have is :
QDEBUG : ("DigiCert Inc")
QDEBUG : ("VeriSign, Inc.")
QDEBUG : ("GlobalSign")
...
My question is how is it possible to see which of these certificates and files, were effectively used by QSslSocket for this specific connexion ?
Thank you.

grpc Google login failure

I'am trying to create a application using the cloud speech api of google.
I cloned all the repositories and created a really simple client app in C++.
#include <grpc++/grpc++.h>
#include "google/cloud/speech/v1/cloud_speech.grpc.pb.h"
namespace gs=google::cloud::speech::v1;
using gs::Speech;
using gs::RecognizeResponse;
using gs::RecognizeRequest;
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
int main(int argc, char** argv) {
try{
auto channel_creds = grpc::GoogleDefaultCredentials();
auto channel = grpc::CreateChannel("speech.googleapis.com:443", channel_creds);
auto stub = Speech::NewStub(channel);
RecognizeResponse res;
RecognizeRequest req;
ClientContext ctx;
auto status = stub->Recognize(&ctx, req, &res);
if(status.ok())
{
std::cout << "ok" << std::endl;
} else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
}
}catch(const std::exception& e)
{
std::cout<<e.what()<<std::endl;
}
std::cin.get();
}
This compiles fine, but when executed results in the following error message:
16: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
I have created a Service account and placed it's json key file next to the env variable "GOOGLE_APPLICATION_CREDENTIALS" to the correct file and I'm quite sure it is read (because if I remove it, it just crashes).
I also have enabled the speech API online.
I'm out of ideas since no matter what I do I can't get it to work.
Am I missing something ?
After some more trying and reading some more example code I finally found the reason myself.
I had to use a different URL. Changing "speech.googleapis.com:443" to "speech.googleapis.com" results in a different error, but connection and login work.
Setting up all parameters for this function correctly and supplying actual audio data returns usable results. I did not expect the port to make a difference, considering both URLs are in many example programs and grpc uses http2 (which in turn uses port 443).
Important piece: Make sure you don't include a port for use with google services.

Connecting to WCF web service using gSOAP

I am writing a simple cmd client to try to consume the WCF web service i developed in order to test how to connect to the Web service using unmanaged C++.
I have been following this tutorial http://www.blinnov.com/en/2008/01/22/wcf-service-unmanaged-client/ step by step but still not managed to consume the service successfully.
#include "BasicHttpBinding_USCOREIService1.nsmap"
#include "soapBasicHttpBinding_USCOREIService1Proxy.h"
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
BasicHttpBinding_USCOREIService1Proxy myProxy;
static const char* const endPoint = "http://localhost:50181/Service1.svc";
myProxy.soap_endpoint = endPoint;
_ns1__GetData param;
_ns1__GetDataResponse response;
param.fileName = &std::string("house.ifc");
if ( myProxy.GetData(&param, &response) == SOAP_OK) {
cout << "Hello" << endl; //Succeeded
}
else {
myProxy.soap_stream_fault(std::cerr);
}
return 0;
}
it always gives me
Error 415 fault: SOAP-ENV:Server[no subcode]
"HTTP Error"
Detail: HTTP/1.1 415 Unsupported Media Type
I have been trying all the day to get it done but still nothing new. :(
This is because gSOAP client and WCF service do not play nicely with SOAP 1.2.
It will work if you use soapcpp2 with the "-1" switch to force SOAP 1.1.
I've tested it with gSOAP 2.8.4 on Ubuntu and it works.
If your client will be installed in windows machine , the best way is to use a C++\CLI bridge that connect your client and the managed generated proxy, this is the only solution that works for all kind of wcf bindings (HTTP,TCP,MSMQ,...).
If you choose another librairy be sure that it can works perfectly only for HTTP binding.

How do I duplicate certificate authentication (Mumble (c/c++)) in Python?

Alright so, before I really get into this post, I am going to have to warn you that this might not be an easy fix. Whoever reads and is able to reply to this post must know a lot of c/c++, and at least some python to be able to answer the question I have above.
Basically, I have a connection method from Mumble (a VOIP client), that connects to a server and sends it an SSL certificate for authentication purposes. I also have a Python script that connects to the same Mumble VOIP server, but I don't send a certificate.
I need to modify my existing code to send a certificate, as the current Mumble client does.
--
Here is the C++ code that seems to send a certificate:
ServerHandler::ServerHandler() {
MumbleSSL::addSystemCA();
{
QList<QSslCipher> pref;
foreach(QSslCipher c, QSslSocket::defaultCiphers()) {
if (c.usedBits() < 128)
continue;
pref << c;
}
if (pref.isEmpty())
qFatal("No ciphers of at least 128 bit found");
QSslSocket::setDefaultCiphers(pref);
}
void ServerHandler::run() {
qbaDigest = QByteArray();
QSslSocket *qtsSock = new QSslSocket(this);
qtsSock->setPrivateKey(g.s.kpCertificate.second);
qtsSock->setLocalCertificate(g.s.kpCertificate.first.at(0));
QList<QSslCertificate> certs = qtsSock->caCertificates();
certs << g.s.kpCertificate.first;
qtsSock->setCaCertificates(certs);
cConnection = ConnectionPtr(new Connection(this, qtsSock));
qtsSock->setProtocol(QSsl::TlsV1);
qtsSock->connectToHostEncrypted(qsHostName, usPort);
void ServerHandler::serverConnectionConnected() {
tConnectionTimeoutTimer->stop();
qscCert = cConnection->peerCertificateChain();
qscCipher = cConnection->sessionCipher();
if (! qscCert.isEmpty()) {
const QSslCertificate &qsc = qscCert.last();
qbaDigest = sha1(qsc.publicKey().toDer());
bUdp = Database::getUdp(qbaDigest);
} else {
bUdp = true;
}
QStringList tokens = Database::getTokens(qbaDigest);
foreach(const QString &qs, tokens)
mpa.add_tokens(u8(qs));
QMap<int, CELTCodec *>::const_iterator i;
for (i=g.qmCodecs.constBegin(); i != g.qmCodecs.constEnd(); ++i)
mpa.add_celt_versions(i.key());
sendMessage(mpa);
--
And alas, this is what I do to connect to it right now (in python):
try:
self.socket.connect(self.host)
except:
print self.threadName,"Couldn't connect to server"
return
self.socket.setblocking(0)
print self.threadName,"connected to server"
--
Soo... what do I need to do more to my Python source to connect to servers that require a certificate? Because my source currently connects just fine to any mumble server with requirecert set to false. I need it to work on all servers, as this will be used on my own server (which ironically enough, has requirecerts on.)
I can pregenerate the certificate as a .p12 or w/e type file, so I don't need the program to generate the cert. I just need it to send the cert as the server wants it (as is done in the c++ I posted).
Please help me really soon! If you need more info, message me again.
Stripped out all irrelevant code, now it's just the code that deals with ssl.
From the C++ code it looks like you simply need to have ssl support and negotiate with the correct certification file and encrypt the payload with the correct private key. Those certifications and privates keys are most likely stored in your original program somewhere. If there are non-standard Authorities that the C++ might be loading up you'll need to find out where to put those root authorities in your python installation, or make sure python simply ignores those issues, which is less secure.
In python you can create a socket, like above, except with urllib. This library has support for HTTPs and providing the certification and private keys. URLOpener
Example Usage:
opener = urllib.URLopener(key_file = 'mykey.key', cert_file = 'mycert.cer')
self.socket = opener.open(url)
You'll probably need to make it more robust with the appropriate error checking and such, but hopefully this info will help you out.