Qt/QML with OpenSSL on iOS

I installed Qt 5.3.2 Open Source using the “Qt Online Installer for MacOS” from http://qt-project.org/downloads. My Qt installation path is /Users/hugo/Qt.

When using an Image element in QML, I can set its source property to an HTTPS url:

Image {
    source: "https://..."
}

However, on iOS this Image element will not render anything. Step one is to cross-compile OpenSSL for iOS:

cd /Users/hugo/tmp
git clone https://github.com/x2on/OpenSSL-for-iPhone.git
cd OpenSSL-for-iPhone/
./build-libssl.sh

Step two is to cross-compile Qt for iOS with OpenSSL support enabled (I only build the release version). First clone the Qt repository:

cd /Users/hugo/tmp
git clone git://gitorious.org/qt/qt5.git qt5
cd qt5
git checkout 5.3.2
./init-repository

Then configure Qt for iOS with OpenSSL support:

OPENSSL_LIBS='-L/Users/hugo/tmp/OpenSSL-for-iPhone/lib -lssl -lcrypto' ./configure -xplatform macx-ios-clang -release -I/Users/hugo/tmp/OpenSSL-for-iPhone/include/  -openssl-linked -c++11 -opensource --prefix=/Users/hugo/Qt/5.3/ios_ssl  -nomake examples -nomake tests -v

Finally I build and install Qt for iOS with OpenSSL support:

make -j 4
make install

Note: with this codebase, before running make install, I needed to patch avfmediaplayersession.mm as shown in https://bugreports.qt-project.org/browse/QTBUG-41136.

Now I can use the OpenSSL libs in my Qt projects. In my .pro file:

ios { 
    LIBS += -L/Users/hugo/tmp/OpenSSL-for-iPhone/lib/ -lssl -lcrypto
}

I only built the release versions, so when I run the project from XCode, I also need to set the Product Scheme to Release. Now, I have a Qt for iOS version that uses OpenSSL. However, when I run my project, the Image element prints out:

QML Image: SSL handshake failed

To handle SSL errors like this, I need to provide a custom class to the QQmlApplicationEngine that derives from QQmlNetworkAccessManagerFactory and override the create method to provide the QNetworkAccessManagers that are used in QML. On these QNetworkAccessManager instances, SSL errors can be handled by providing the ignoreSSLErrors slot. For example, to ignore all SSL errors:

class SSLSafeNetworkAccessManager : public QNetworkAccessManager
{
    Q_OBJECT

public:
    SSLSafeNetworkAccessManager(QObject *parent = 0);

public slots:
    void ignoreSSLErrors(QNetworkReply* reply,QList errors);
};

SSLSafeNetworkAccessManager::SSLSafeNetworkAccessManager(QObject *parent) :
    QNetworkAccessManager(parent)
{
    QObject::connect(this,SIGNAL(sslErrors(QNetworkReply*,QList)),
                     this,SLOT(ignoreSSLErrors(QNetworkReply*,QList)));
}

void SSLSafeNetworkAccessManager::ignoreSSLErrors(QNetworkReply* reply,QList errors)
{
   reply->ignoreSslErrors(errors);
}
class SSLSafeNetworkFactory : public QObject, public QQmlNetworkAccessManagerFactory
{
    Q_OBJECT

public:
    virtual QNetworkAccessManager *create(QObject *parent);
};

QNetworkAccessManager* SSLSafeNetworkFactory::create(QObject *parent)
{
    SSLSafeNetworkAccessManager* manager = new SSLSafeNetworkAccessManager(parent);
    return manager;
}

Finally, I need to instruct my QQmlApplicationEngine to use this factory class. This must be done before loading any QML:

QQmlApplicationEngine engine {};
engine.setNetworkAccessManagerFactory(new SSLSafeNetworkFactory);

References: