SSL handshake failed on iOS
-
Our app is unable to connect securely to our servers on iOS. Other platforms are fine. When connecting to https://camcops.cpft.nhs.uk with QSsl::SecureProtocols the error is "SSL handshake failed" and "The root certificate of the certificate chain is self-signed, untrusted" . The certificates look fine to me..
Example code at https://github.com/martinburchell/qt-network-test
Main part if it here:
#include <QApplication> #include <QByteArray> #include <QDialog> #include <QListIterator> #include <QMapIterator> #include <QPlainTextEdit> #include <QPointer> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkReply> #include <QtNetwork/QSsl> #include <QtNetwork/QSslConfiguration> #include <QUrlQuery> #include <QUuid> #include <QVBoxLayout> #include <QWidget> class TestDialog : public QDialog { Q_OBJECT public: TestDialog(QWidget* parent) : QDialog(parent) { auto mainlayout = new QVBoxLayout(); setLayout(mainlayout); m_editor = new QPlainTextEdit(); m_editor->setReadOnly(true); m_editor->setTextInteractionFlags(Qt::NoTextInteraction); m_editor->setLineWrapMode(QPlainTextEdit::WidgetWidth); mainlayout->addWidget(m_editor); QNetworkRequest request; QSslConfiguration config = QSslConfiguration::defaultConfiguration(); config.setProtocol(QSsl::SecureProtocols); request.setSslConfiguration(config); QListIterator<QSslCertificate> cert_it(QSslConfiguration::systemCaCertificates()); status_message("CA Certificates:"); while (cert_it.hasNext()) { status_message(cert_it.next().toText()); } QUrl url("https://camcops.cpft.nhs.uk:443/api"); request.setUrl(url); QMap<QString, QString> dict; dict["operation"] = "check_device_registered"; dict["camcops_version"] = "2.4.15"; dict["device"] = QUuid::createUuid().toString(); QUrlQuery postdata; QMapIterator<QString, QString> dict_it(dict); while (dict_it.hasNext()) { dict_it.next(); postdata.addQueryItem(QUrl::toPercentEncoding(dict_it.key()), QUrl::toPercentEncoding(dict_it.value())); } request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); const QByteArray final_data = postdata.toString(QUrl::FullyEncoded).toUtf8(); status_message("Request to server: " + final_data); status_message(&"... sending " [ final_data.length()]); auto mgr = new QNetworkAccessManager(); QObject::connect(mgr, &QNetworkAccessManager::finished, this, &TestDialog::reply); QObject::connect(mgr, &QNetworkAccessManager::sslErrors, this, &TestDialog::ssl_errors); mgr->post(request, final_data); } void reply(QNetworkReply* reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { status_message("Network failure: " + reply->errorString()); return; } auto reply_data = reply->readAll(); status_message(&"... received " [ reply_data.length()]); status_message("Network reply (raw): " + reply_data); } void ssl_errors(QNetworkReply *reply, const QList<QSslError> &errors) { status_message("SSL Errors:"); QListIterator<QSslError> it(errors); while (it.hasNext()) { auto error = it.next(); status_message(error.errorString()); } QSslConfiguration config = reply->sslConfiguration(); status_message("Peer certificate: " + config.peerCertificate().toText()); QListIterator<QSslCertificate> cert_it(config.peerCertificateChain()); status_message("Peer certificate chain:"); while (cert_it.hasNext()) { status_message(cert_it.next().toText()); } } void status_message(const QString& msg) const { m_editor->appendPlainText(msg); } protected: QPointer<QPlainTextEdit> m_editor; }; int main(int argc, char* argv[]) { QApplication app(argc,argv); TestDialog dialog(nullptr); dialog.exec(); return app.exec(); } #include "main.moc"
Fails with Qt 5.12/OpenSSL 1.1.1c, and Qt 6.2.4/OpenSSL 1.1.1s compiled from source on iPad 7 / iOS 14.6.
I can access e.g. https://camcops.cpft.nhs.uk in Safari without any errors.Any ideas?
-
Hi,
Did you check the SSL error ? What it is saying ?
Are you sure the complete certificate chain is valid ? -
@SGaist Thanks for replying. The example code prints
SSL handshake failed
in theQNetworkAccessManager::finished
callback andThe root certificate of the certificate chain is self-signed, untrusted
in theQNetworkAccessManager::sslErrors
callback. Do you know if there are other errors I could access?I don't get any errors with the example code on other platforms such as Ubuntu desktop. Also I can access the site on the iPad with Safari and there are no errors. That makes me think the certificate chain is valid.
-
Are you building Qt yourself ? By default, it uses Apple's framework.
-
@SGaist Yes I am building Qt from source. You've pointed me to Apple's framework before and I need to investigate if we could use it. I could try my example with that at least.
I think for our app we need SQLCipher and my memory is that needs OpenSSL but I might be wrong. I also note https://wiki.qt.io/Plans_for_Modules says SecureTransport is deprecated. Is this what Qt uses?
-
@Martin-Burchell yes, SecureTransport is the current default. I would suggest to just make a minimal test with it to see if you have the same issue as with OpenSSL as backend.
As for SQLCipher, its potential need for OpenSSL is something completely independent so you are not required to rebuild Qt to use OpenSSL because of it. -
I've not got to the root cause of this but my app is working with SecureTransport instead. So I'll mark this as Solved.
I've created a ticket https://bugreports.qt.io/browse/QTBUG-111963
-
-
So that ticket has been closed with the comment "To do a verification OpenSSL needs a list of CA certs, without it, it fails to verify and the function(s) we can use on macOS to extract such certificates are not available on iOS."