I need to exchange data with server which requires local certificate (.crt file). I try this:
loginRequest = QNetworkRequest(QUrl("https://somesite.com/login"));
QSslConfiguration sslConf = loginRequest.sslConfiguration();
QList<QSslCertificate> certs = QSslCertificate::fromPath(Preferences::certificatePath());
qDebug() << certs.first().issuerInfo(QSslCertificate::Organization); // prints name
sslConf.setLocalCertificate(certs.first());
qDebug() << "is valid " << sslConf.localCertificate().isValid(); // true
qDebug() << "is null " << sslConf.localCertificate().isNull(); // false
qDebug() << "protocol " << sslConf.protocol(); // 0
sslConf.setProtocol(QSsl::SslV3); // i also tried Qssl::AnyProtocol
qDebug() << "protocol " << sslConf.protocol(); // 0
// if i uncomment these i expect everithing to work
//QSslConfiguration::setDefaultConfiguration(sslConf);
//QSslSocket::addDefaultCaCertificate(certs.first());
//loginRequest.setSslConfiguration(sslConf);
QObject::connect(connectionManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(printSslErrors2(QNetworkReply*,QList<QSslError>)));
m_reply = connectionManager->get(loginRequest);
QObject::connect(m_reply, SIGNAL(readyRead()), this, SLOT(getCookie()));
QObject::connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(printSslErrors(QList<QSslError>)));
When this code executes i have the following messages in WireShark (filter: tcp && ssl && ip.addr == my_addr):
Client Hello
ServerHello, Certificate
Server Key Exchange, Certificate request, Server Hello Done
Alert (level: Warning, Description: no certificate), client key exchange, change cipher spec, encrypted handshake message
Alert (level: Fatal, Description: Handshake failure)
This is expected - the code to apply certificate is commented out, but the strange thing - I do not get any ssl errors from my QNetworkAccessManager and QNetworkReply (slots printSslErrors and printSslErrors2).
If i uncomment any of these 3 lines:
//QSslConfiguration::setDefaultConfiguration(sslConf);
//QSslSocket::addDefaultCaCertificate(certs.first());
//loginRequest.setSslConfiguration(sslConf);
I get NOTHING in wireshark (few SYN, ACK and FIN tcp messages, but no http or ssl traffic). Also there are still no errors from QNetworkAccessManager and QNetworkReply, so I have no idia what is going wrong.
Is there any chance to make Qt accept my local certificate or may be there is some 3d party qt-oriented lib to help me out?
P.S.: btw - ssl and https worked just fine a few days ago, before the server was changed to require client-side certificates.
P.P.S.: certificate is self signed if it makes any difference. Also I tried to 'install' it (the p12 file) into system and both Chrome and IE7 are able to use it and communicate with server.
Complete shot in the dark and goes on the assumption that Qt may in fact be reporting an error but you're not getting the signal.
You're connecting signals from your connectionManager
to this
have you included the Q_OBJECT
macro in the header for this
?
Also examine the output when you run your application as Qt may report issues connecting the signals/slots if that is indeed the case here.
SOLUTION, Part I: I mostly solved this (the lack of connection), there were 2 reason:
1st - the apache server actually require private key (for some unknown reason, found it [here][1]), how to add private key:
QFile x(Preferences::certificateKeyPath());
x.open(QIODevice::ReadOnly);
pKey = QSslKey(x.readAll(),QSsl::Rsa);
QSslError error1(QSslError::SelfSignedCertificate, certs.first());
QSslError error2(QSslError::CertificateUntrusted, certs.first());
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error1);
expectedSslErrors.append(error2);
2d - the certificate I had was not very 'good'. I dont know what it really means or why it was not working, but when I got new certificate from server admin and added private key the handshake succeeded.
I still dont know how to catch sslErrors (for example to show user that his certificate is not working), but it is a good start
SOLUTION, Part II:
Solved the last part of the question (kina a woraround). It seems QNetworkReply not emitting SslErrors is a bug (or at least it does not work all the time or for all web-sites), found it [in Qt bug tracker][2]. And the workaround also from there: sinse we cant get SslErrors, we have to try and get smth else - [error][3], for example. It does not give detailed information about what have actually happend, but better than nothing. For me error code 6 - "the SSL/TLS handshake failed and the encrypted channel could not be established. The sslErrors() signal should have been emitted." is perfect (i dont care for anything else):
QObject::connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleSslErrors(QNetworkReply::NetworkError)));
The important part: if the user has wrong certificate and/or key - the signal is emited. But it is also emited if certificate and key are correct. It seems auth might still be not perfect, but you can easily shut it down with
QObject::connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(printSslErrors(QList<QSslError>)));
Conclusion it seems they have fixed a lot of SSL bugs in Qt 4.8, so I hope release will be soon
来源:https://stackoverflow.com/questions/7506223/qnetworkrequest-with-ssl-local-certificate