Qt5 QWebView how to send client certificate? - qt

I use Qt5's QWebView to load a https website, which need client certificate. I have installed client certificate and used chrome and ie browser to test ok. But it didn't work in the QWebView.
QNetworkRequest request;
request.setUrl(QUrl("https://mysite.com/default2.aspx"));
QSslConfiguration conf = request.sslConfiguration();
//get the client certificate
QByteArray certData = getCertInWindowsMyStore();
QSslCertificate sslCert(certData, QSsl::Der);
conf.setLocalCertificate(sslCert);
request.setSslConfiguration(conf);
ui->webView->load(request);
I print the certicate's subjectinfo which is correct.
I seach some infomation from google. Someone says I have to set the private key, someone didn't set and it also works fine. How it works? And I think that I used chrome browser to visit that website, it works fine, but chrome wouldn't know the private key, so I think there's no need to set private key.
Does anyone know how to do? This question has tortured me very much!

I think to load private key you have to add to your code:
QByteArray keyData = customReadFile("/path/to/privatekey/PEM");
QSslKey sslPrivateKey(keyData, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "PRIVATE KEY PASS");
conf.setPrivateKey(sslPrivateKey);
You have to implement customReadFile()

Related

QNetworkAccessManager posts gets error "the remote host name was not found" but get works fine and they use the same proxy and SSL settings

I get a strange issue with my Qt-based software. The software uses QNetworkAccessManager to get and post information to a webserver using SSL encryption and the user have the option to use a proxy if needed. We do still have some limited information sent without SSL (to an http address, no added "s"), we use our own encryption for that part. Just haven't updated that part yet and that's not causing any issues right now (which actually is partially why this issue is strange).
You see, when I try to Post data using both a proxy and SSL I get error 103: "the remote host name was not found (invalid hostname)", but posting the data SSL and no proxy works fine, as does posting the data with proxy and no SSL. The exact same data can however be sent with both proxy and SSL using Postman, so there doesn't seem to be any issue with the data. Get works to get all data regardless of SSL and proxy.
Have anyone else had a similar issue or have an idea of what might be the cause? If so, have you found any solutions or workarounds?
Here's the proxy setup for the Get part:
nam = new QNetworkAccessManager(this);
connect(nam,SIGNAL(finished(QNetworkReply*)),this,SLOT(namFinished(QNetworkReply*)));
if(portalCommSettingsVO.isUseProxy()){
QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, portalCommSettingsVO.getProxyUrl(), static_cast<quint16>(portalCommSettingsVO.getProxyPort()));
if(!portalCommSettingsVO.getProxyUser().operator ==("")){
proxy.setUser(portalCommSettingsVO.getProxyUser());
proxy.setPassword(portalCommSettingsVO.getProxyPassword());
}
nam->setProxy(proxy);
}else{
nam->setProxy(QNetworkProxy::NoProxy);
}
And here's where I connect and Get the data:
QString routingUrlString = Utils::getUrlRoutingServer();
routingUrlString.append(QString::number(Id));
QUrl routingUrl(routingUrlString);
QNetworkRequest routingRequest;
routingRequest.setSslConfiguration(QSslConfiguration::defaultConfiguration());
routingRequest.setUrl(routingUrl);
reply = nam->get(routingRequest);
connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(downloadError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(const QList<QSslError>)), this,SLOT(downloadSSLError(const QList<QSslError>)));
connect(reply,SIGNAL(finished()),this,SLOT(routingDownloadFinished()));
Compared to the proxy setup for the Post part:
nam = new QNetworkAccessManager(this);
connect(nam,SIGNAL(finished(QNetworkReply*)),this,SLOT(namFinished(QNetworkReply*)));
if(portalCommSettingsVO.isUseProxy()){
QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, portalCommSettingsVO.getProxyUrl(), static_cast<quint16>(portalCommSettingsVO.getProxyPort()));
if(!portalCommSettingsVO.getProxyUser().operator ==("")){
proxy.setUser(portalCommSettingsVO.getProxyUser());
proxy.setPassword(portalCommSettingsVO.getProxyPassword());
}
nam->setProxy(proxy);
}else{
nam->setProxy(QNetworkProxy::NoProxy);
}
And where I connect and try to post the data:
QUrl url(currentObject.getUploadUrl());
QUrlQuery query;
query.addQueryItem("mode", "save");
url.setQuery(query.query());
QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(QUrl(url));
request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/x-www-form-urlencoded"));
reply = nam->post(request,arr);
connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(uploadError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(const QList<QSslError>)), this,SLOT(uploadSSLError(const QList<QSslError>)));
connect(reply,SIGNAL(finished()),this,SLOT(uploadFinished()));
Pretty similar right? I can't understand why the Post wont work!
PS. the "arr"-variable is an array of data in base64. The same data could be sent through Postman without difficulty so I doubt that's where the issue is.

Failed to load resource: net::ERR_INSECURE_RESPONSE

IS there a way to trick the server so I don't get this error:
Content was blocked because it was not signed by a valid security certificate.
I'm pulling an iframe of an html website into another website but I keep getting the console (chrome) error in the title of this question and in internet explorer it says:
Content was blocked because it was not signed by a valid security certificate.
Your resource probably use a self-signed SSL certificate over HTTPS protocol.
Chromium, so Google Chrome block by default this kind of resource considered unsecure.
You can bypass this this way :
Assuming your frame's URL is https://www.domain.com, open a new tab in chrome and go to https://www.domain.com.
Chrome will ask you to accept the SSL certificate. Accept it.
Then, if you reload your page with your frame, you could see that now it works
The problem as you can guess, is that each visitor of your website has to do this task to access your frame.
You can notice that chrome will block your URL for each navigation session, while chrome can memorise for ever that you trust this domain.
If your frame can be accessed by HTTP rather than HTTPS, I suggest you to use it, so this problem will be solved.
Sometimes Google Chrome throws this error, even if it should not.
I experienced it when Chrome had a new version, and it needed to be restarted.
After restarting the same page worked without any errors.
The error in the console was:
net::ERR_INSECURE_RESPONSE
I still experienced the problem described above on an Asus T100 Windows 10 test device for both (up to date) Edge and Chrome browser.
Solution was in the date/time settings of the device; somehow the date was not set correctly (date in the past). Restoring this by setting the correct date (and restarting the browsers) solved the issue for me. I hope I save someone a headache debugging this problem.
Offering another potential solution to this error.
If you have a frontend application that makes API calls to the backend, make sure you reference the domain name that the certificate has been issued to.
e.g.
https://example.com/api/etc
and not
https://123.4.5.6/api/etc
In my case, I was making API calls to a secure server with a certificate, but using the IP instead of the domain name. This threw a Failed to load resource: net::ERR_INSECURE_RESPONSE.
open up your console and hit the URL inside. it'll take you to the API page and then in the page accept the SSL certificate, go back to your app page and reload.
remember that SSL certificates should have been issued for your Dev environment before.
If you're developing, and you're developing with a Windows machine, simply add localhost as a Trusted Site.
And yes, per DarrylGriffiths' comment, although it may look like you're adding an Internet Explorer setting...
I believe those are Windows rather than IE settings. Although MS tend to assume that they're only IE (hence the alert next to "Enable Protected Mode" that it requries restarted IE)...
Try this code to watch for, and report, a possible net::ERR_INSECURE_RESPONSE
I was having this issue as well, using a self-signed certificate, which I have chosen not to save into the Chrome Settings. After accessing the https domain and accepting the certificate, the ajax call works fine. But once that acceptance has timed-out or before it has first been accepted, the jQuery.ajax() call fails silently: the timeout parameter does not seem help and the error() function never gets called.
As such, my code never receives a success() or error() call and therefore hangs. I believe this is a bug in jquery's handling of this error. My solution is to force the error() call after a specified timeout.
This code does assume a jquery ajax call of the form jQuery.ajax({url: required, success: optional, error: optional, others_ajax_params: optional}).
Note: You will likely want to change the function within the setTimeout to integrate best with your UI: rather than calling alert().
const MS_FOR_HTTPS_FAILURE = 5000;
$.orig_ajax = $.ajax;
$.ajax = function(params)
{
var complete = false;
var success = params.success;
var error = params.error;
params.success = function() {
if(!complete) {
complete = true;
if(success) success.apply(this,arguments);
}
}
params.error = function() {
if(!complete) {
complete = true;
if(error) error.apply(this,arguments);
}
}
setTimeout(function() {
if(!complete) {
complete = true;
alert("Please ensure your self-signed HTTPS certificate has been accepted. "
+ params.url);
if(params.error)
params.error( {},
"Connection failure",
"Timed out while waiting to connect to remote resource. " +
"Possibly could not authenticate HTTPS certificate." );
}
}, MS_FOR_HTTPS_FAILURE);
$.orig_ajax(params);
}
This problem is because of your https that means SSL certification. Try on Localhost.

Qt login to steam with QNetworkAccessManager

I'm using QT 5.0.2 prebuilt and QT creator 2.7.0 and my goal is to login to steam network programmatically using uname, password and steamguard code. Or to be more precise: get QNetworkAccessManager from its initial uninitialized state to the state where it could retrieve any data from steam related sites as if it was logged in as some user.
So the login happens in 4 steps (4 request-response combos):
NOTE: Steam site javascript uses post in all requests, but it seems that get also works.
Assume username "hyper"
1. Initial request:
post https://store.steampowered.com/login/getrsakey/?username=hyper
Here is my function:
void http::steam_auth(const QString &uname, const QString &pwd)
{
QString encrypted_password, sg, pkey_exp, pkey_mod, timestamp, emailsteamid;
QJsonDocument json_buffer;
QByteArray buffer;
QUrl rsa(steam_getrsa), login(steam_dologin); //steam login urls
QUrlQuery urlquery;
Here we send our request:
urlquery.addQueryItem("username", uname); //first step
urlquery.addQueryItem("l", "english"); //set communication language
rsa.setQuery(urlquery);
QNetworkRequest first(rsa);
QNetworkReply *reply = this->get(first);
buffer = reply->readAll();
As a response we get:
{"success":true,"publickey_mod":"AC41A964789638B6A3B3BD5ADD9F3680FA45881C70FD2032B10A3BB6D2ACF021C2B3D564DC44DE3054E3BF78A1753399223C794A0DED03A76CD1F1DFE851E30F0D6F14484C258EB6053676A47D20A68E11ECCD783F343594DDA3817AA5DCCE75FAF5C68BB197DA1FF12F22F9FCD83624D00A23CC57A367563ADBBCEDF5C8742A57B12A4BD04DC02A55C80F4F6984B1E2D5A99081510326E1C6BAB2C2723560487E8F7EA1A2CB55AEFC5594C9FBB71CAD553CB2D866214BC41156259FEC55362D38DCDBE8FADFB577FE0736CE3E37AF6D2CDDF28FA218C1E477E29B17713BFC48A227455B6B0DE09884CC0820CE40CD4877D93704D00E20A235FCF55D100A01C1","publickey_exp":"010001","timestamp":"284691100000"}
Which means everything is ok.
Now we need to encrypt our password before sending it
json_buffer = QJsonDocument::fromJson(buffer);
pkey_exp = json_buffer.object().value("publickey_exp").toString();
pkey_mod = json_buffer.object().value("publickey_mod").toString();
timestamp = json_buffer.object().value("timestamp").toString();
delete reply;
urlquery.clear();
encrypted_password = app::core::get_encrypted_password(pwd, pkey_exp, pkey_mod);
2. Send encrypted password:
urlquery.addQueryItem("username", uname);
urlquery.addQueryItem("password", encrypted_password);
urlquery.addQueryItem("emailauth", "");
urlquery.addQueryItem("captchagid", "");
urlquery.addQueryItem("captcha_text", "");
urlquery.addQueryItem("emailsteamid", "");
urlquery.addQueryItem("rsatimestamp", timestamp);
urlquery.addQueryItem("remember_login", "false");
login.setQuery(urlquery);
QNetworkRequest second(login);
second.setUrl(login);
reply = this->get(second);
buffer = reply->readAll();
We get a request string as follows:
post https://store.steampowered.com/login/dologin/?username=solidbeetle2&password=YmhTKVkRXyiCYe6wx+ZJ8PIhzj4A4BLWgJFOE5ge7nbIAM6m1G9qHh+Iqx30ZLdB0wW0xdWDNCgHBNPHKLA+P2pYhPF0DeL9v8UQsers6NCNNPZ0SFN4HhNlu6Gwh8QAjrNykev7N5FADXwJnFjPBvmvthATmrktVEtFYF54lckaPnijXYSDIpfEjmG8+bCDKT/GLaUiftA2QauUY9ap8WHSEoykiTmfL344ghzjhCA33UKx0NIgBrDdI1RLfHVcmAcU/c9NEhoHLOT93n8hqWY+YVx9VbOcKqqZPrbCiQoU2BZrqK6N7aj+K6kH0VWHH7+LD2KJx4BUJgHOmNqVDg%3D%3D&emailauth=&captchagid=&captcha_text=&emailsteamid=&rsatimestamp=50693150000&remember_login=false
It is perfectly valid as far as I can tell, JS on steam site sends ajax with the same, but...
Here is the problem
When I get a response with this it says message:Invalid login in json... But if I save the full query string from my request to file and then paste it either in browser or in HTTP Request builder inside HTTP Analyzer, it works fine displaying message:SteamGuard
What could be wrong? Is there something I'm missing? Does QNetworkAccessManager break this somehow?:c
Sorry if something is not clear, I'll try my best to explain again if needed.
Thanks in advance!
P.S. Is there any solid way to examine requests my app is sending?
P.S.S. my qt creator seems to crash when debugging projects with qml option, idk why...
I have inspected all this stuff and finally was able to achieve my goal.
In short the problem was in qt's web-oriented classes. It seemed obvious that qurlquery should encode characters such as / or = in their %-encoded form.. but it is NOT the case, therefore I had to replace them manually in QString. If someone needs a working routine, it can be found here (Its not pretty :c)

Qt creator SSL enable

I have a program built with Qt creator. I need to send an https post to Google's C2DM servers.
When I try using QSslSocket, it says that my SSL is not working.
The Qt documentation states that it doesn't ship with SSL support for legal reasons, and QSslSocket is just a wrapper and that I need to install OpenSSL myself. I've tried copying the DLLs but it still doesn't work. Can someone walk me through the steps of installing OpenSSL and getting it to work with Qt Creator?
Why not use QNetworkAccessManager? I use something like this to post JSON to a web service:
void HttpPoster::post(){
if(!manager)
manager = new QNetworkAccessManager(this);
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setPeerVerifyMode(QSslSocket::VerifyNone);
//config.setProtocol(QSsl::TlsV1);
QNetworkRequest request ;
request.setUrl(QUrl("https://somehost.somedomain"));
request.setRawHeader("User-Agent", "MyApp");
request.setRawHeader("Content-type", "text/json");
request.setSslConfiguration(config);
if(!reply){
reply = manager->post(request,m_Data);
}
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(handleErrors(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(sslErrors(QList<QSslError>)),this,SLOT(handleSSLErrors(QList<QSslError>)));
connect(reply,SIGNAL(finished()),this,SLOT(replyFinished()));
}
There's some methods omitted (handle errors, handle replies, handle ssl errors, etc). You can find much better examples in the docs.

WCF Certificate Authentication without installing on the Client

Our setup includes a WCF service and a number of clients written by us. Some of the clients include Silverlight applications, whereas others include Web and Windows applications.
I (think) I would like to authenticate clients based on X.509 certificates. Typically you would install a private key on the client to encrypt (aka digitaly sign) the messages. The server can the use the clients public key to de-crypt it to ensure the message has not been changed and prove the message is from who we expect (aka authenticated).
I dont want to install a certificate on a client machine. Its a hassel to deploy, and we cant really ask our clients to do it. I was speaking to someone the other day who sugested embeding the cert in a client assembly, reading it and using that. Is that possible?
It would be great if someone could point me to an example.
Thanks in advance,
David
Yes, you can load X509certificate2 by passing a certificate byte array with a password like
var certificate = new X509Certificate2(theByteArrary, "password");
To get the certificate byte array, you can simply copy paste the contents in .pfx file, which is a combination of .cer (public key) and .pvk (private key)
and then you can load this certificate on your client by doing:
var channelFactory = new ChannelFactory<IYourService>();
channelFactory.Credentials.ClientCertificate.Certificate =
clientCertificate;
If you use auto-generated client proxy, or you prefer configure the certificate via .config file then you might want to have a look at this from codeproject
Here is a suggestion. Could also be tweaked to use an embedded certificate.
http://www.codeproject.com/KB/WCF/wcfcertificates.aspx

Resources