summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-11-19 03:00:59 +0100
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-11-19 03:00:59 +0100
commit59554501e666084bc69ee8c99b705d2b7eabf23a (patch)
tree5e4b5312909cc049c36ece0261311fe11be08abd
parent93c4fedb026c6f9a753de46f2d982abc95910274 (diff)
parentbcd37d8bffb422e5fcb29f6778c626a0cfc40ec3 (diff)
Merge remote-tracking branch 'origin/5.14' into 5.15
-rw-r--r--examples/opcua/opcua.pro4
-rw-r--r--examples/opcua/x509/doc/x509.qdoc36
-rw-r--r--examples/opcua/x509/main.cpp123
-rw-r--r--examples/opcua/x509/x509.pro12
-rw-r--r--src/opcua/client/client.pri10
-rw-r--r--src/opcua/client/qopcuaapplicationrecorddatatype.cpp235
-rw-r--r--src/opcua/client/qopcuaapplicationrecorddatatype.h89
-rw-r--r--src/opcua/client/qopcuabinarydataencoding.cpp4
-rw-r--r--src/opcua/client/qopcuabinarydataencoding.h57
-rw-r--r--src/opcua/client/qopcuagdsclient.cpp1883
-rw-r--r--src/opcua/client/qopcuagdsclient.h141
-rw-r--r--src/opcua/client/qopcuagdsclient_p.h173
-rw-r--r--src/opcua/client/qopcuapkiconfiguration.cpp12
-rw-r--r--src/opcua/opcua.pro4
-rw-r--r--src/opcua/x509/openssl_symbols.cpp1335
-rw-r--r--src/opcua/x509/openssl_symbols_p.h566
-rw-r--r--src/opcua/x509/qopcuakeypair.cpp198
-rw-r--r--src/opcua/x509/qopcuakeypair.h90
-rw-r--r--src/opcua/x509/qopcuakeypair_openssl.cpp264
-rw-r--r--src/opcua/x509/qopcuakeypair_p.h84
-rw-r--r--src/opcua/x509/qopcuax509certificatesigningrequest.cpp236
-rw-r--r--src/opcua/x509/qopcuax509certificatesigningrequest.h86
-rw-r--r--src/opcua/x509/qopcuax509certificatesigningrequest_openssl.cpp509
-rw-r--r--src/opcua/x509/qopcuax509certificatesigningrequest_p.h91
-rw-r--r--src/opcua/x509/qopcuax509distinguishedname.cpp167
-rw-r--r--src/opcua/x509/qopcuax509distinguishedname.h72
-rw-r--r--src/opcua/x509/qopcuax509extension.cpp126
-rw-r--r--src/opcua/x509/qopcuax509extension.h65
-rw-r--r--src/opcua/x509/qopcuax509extension_p.h63
-rw-r--r--src/opcua/x509/qopcuax509extensionbasicconstraints.cpp138
-rw-r--r--src/opcua/x509/qopcuax509extensionbasicconstraints.h65
-rw-r--r--src/opcua/x509/qopcuax509extensionextendedkeyusage.cpp140
-rw-r--r--src/opcua/x509/qopcuax509extensionextendedkeyusage.h72
-rw-r--r--src/opcua/x509/qopcuax509extensionkeyusage.cpp149
-rw-r--r--src/opcua/x509/qopcuax509extensionkeyusage.h77
-rw-r--r--src/opcua/x509/qopcuax509extensionsubjectalternativename.cpp134
-rw-r--r--src/opcua/x509/qopcuax509extensionsubjectalternativename.h67
-rw-r--r--src/opcua/x509/qopcuax509utils.cpp62
-rw-r--r--src/opcua/x509/qopcuax509utils_p.h84
-rw-r--r--src/opcua/x509/qsslsocket_openssl11_symbols_p.h178
-rw-r--r--src/opcua/x509/qsslsocket_opensslpre11_symbols_p.h196
-rw-r--r--src/opcua/x509/x509.pri33
-rw-r--r--src/plugins/opcua/open62541/qopen62541utils.h2
-rw-r--r--src/plugins/opcua/uacpp/quacppbackend.cpp2
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/x509/tst_x509.cpp274
-rw-r--r--tests/auto/x509/x509.pro8
-rw-r--r--tests/manual/gds/gds.pro8
-rw-r--r--tests/manual/gds/tst_gds.cpp329
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/tests.pro3
51 files changed, 8749 insertions, 13 deletions
diff --git a/examples/opcua/opcua.pro b/examples/opcua/opcua.pro
index eb396c5..dc5f5f4 100644
--- a/examples/opcua/opcua.pro
+++ b/examples/opcua/opcua.pro
@@ -2,7 +2,9 @@ TEMPLATE = subdirs
qtHaveModule(widgets): SUBDIRS += \
opcuaviewer \
-QT_FOR_CONFIG += opcua-private
+QT_FOR_CONFIG += opcua-private core-private
+
+qtConfig(ssl):!darwin:!winrt: SUBDIRS += x509
qtConfig(open62541) {
qtHaveModule(quick): SUBDIRS += waterpump
diff --git a/examples/opcua/x509/doc/x509.qdoc b/examples/opcua/x509/doc/x509.qdoc
new file mode 100644
index 0000000..506dc82
--- /dev/null
+++ b/examples/opcua/x509/doc/x509.qdoc
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the QtOpcUa module.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example x509
+ \ingroup qtopcua-examples
+ \title Qt OPC UA X509 Support Example
+ \brief Shows how to generate keys and certificate signing requests.
+
+ This example show how client applications can generate their own self-signed certificate
+ or generate a certificate signing request.
+*/
diff --git a/examples/opcua/x509/main.cpp b/examples/opcua/x509/main.cpp
new file mode 100644
index 0000000..b246fc3
--- /dev/null
+++ b/examples/opcua/x509/main.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QOpcUaProvider>
+#include <QOpcUaKeyPair>
+#include <QOpcUaX509CertificateSigningRequest>
+#include <QOpcUaX509ExtensionSubjectAlternativeName>
+#include <QOpcUaX509ExtensionBasicConstraints>
+#include <QOpcUaX509ExtensionKeyUsage>
+#include <QFile>
+
+int main(int argc, char **argv)
+{
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+
+ // Generate RSA Key
+ QOpcUaKeyPair key;
+ key.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+
+ // Save private key to file
+ QByteArray keyData = key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Aes128Cbc, "password");
+
+ QFile keyFile("privateKey.pem");
+ keyFile.open(QFile::WriteOnly);
+ keyFile.write(keyData);
+ keyFile.close();
+
+ // Create a certificate signing request
+ QOpcUaX509CertificateSigningRequest csr;
+
+ // Set the subject of the certificate
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CommonName, "QtOpcUaViewer");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, "DE");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, "The Qt Company");
+ csr.setSubject(dn);
+
+ // The subject alternative name extension is needed for OPC UA
+ QOpcUaX509ExtensionSubjectAlternativeName *san = new QOpcUaX509ExtensionSubjectAlternativeName;
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "foo.com");
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::URI, "urn:foo.com:The%20Qt%20Company:QtOpcUaViewer");
+ san->setCritical(true);
+ csr.addExtension(san);
+
+ // Set the certificate basic constraints
+ QOpcUaX509ExtensionBasicConstraints *bc = new QOpcUaX509ExtensionBasicConstraints;
+ bc->setCa(false);
+ bc->setCritical(true);
+ csr.addExtension(bc);
+
+ // Set the key usage constraints
+ QOpcUaX509ExtensionKeyUsage *ku = new QOpcUaX509ExtensionKeyUsage;
+ ku->setCritical(true);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DigitalSignature);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::NonRepudiation);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DataEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CertificateSigning);
+ csr.addExtension(ku);
+
+ // Now there are two options:
+ // 1. When you need to get your certificate signing request signed by a certificate authority
+ // you have to use the request data.
+ // 2. When there is no certificate authority you have to self-sign the request.
+
+ // Option 1
+ QByteArray certificateSigingRequestData = csr.createRequest(key);
+
+ // Option 2
+ QByteArray selfSignedCertificateData = csr.createSelfSignedCertificate(key);
+
+ return 0;
+}
diff --git a/examples/opcua/x509/x509.pro b/examples/opcua/x509/x509.pro
new file mode 100644
index 0000000..f098656
--- /dev/null
+++ b/examples/opcua/x509/x509.pro
@@ -0,0 +1,12 @@
+QT += opcua
+CONFIG += c++11
+DEPENDPATH += INCLUDEPATH
+
+SOURCES += main.cpp \
+
+#install
+target.path = $$[QT_INSTALL_EXAMPLES]/opcua/x509
+INSTALLS += target
+
+HEADERS += \
+
diff --git a/src/opcua/client/client.pri b/src/opcua/client/client.pri
index 2363ee1..4b82579 100644
--- a/src/opcua/client/client.pri
+++ b/src/opcua/client/client.pri
@@ -12,6 +12,7 @@ SOURCES += \
client/qopcuaaddreferenceitem.cpp \
client/qopcuaapplicationdescription.cpp \
client/qopcuaapplicationidentity.cpp \
+ client/qopcuaapplicationrecorddatatype.cpp \
client/qopcuaargument.cpp \
client/qopcuaattributeoperand.cpp \
client/qopcuaauthenticationinformation.cpp \
@@ -62,6 +63,7 @@ HEADERS += \
client/qopcuaaddreferenceitem.h \
client/qopcuaapplicationdescription.h \
client/qopcuaapplicationidentity.h \
+ client/qopcuaapplicationrecorddatatype.h \
client/qopcuaargument.h \
client/qopcuaattributeoperand.h \
client/qopcuaauthenticationinformation.h \
@@ -106,3 +108,11 @@ HEADERS += \
client/qopcuawriteitem.h \
client/qopcuawriteresult.h \
client/qopcuaxvalue.h \
+
+# Only added for platforms that have OpenSSL available
+QT_FOR_CONFIG += core-private
+qtConfig(ssl):!darwin:!winrt {
+ PUBLIC_HEADERS += client/qopcuagdsclient.h
+ SOURCES += client/qopcuagdsclient.cpp
+ HEADERS += client/qopcuagdsclient_p.h
+}
diff --git a/src/opcua/client/qopcuaapplicationrecorddatatype.cpp b/src/opcua/client/qopcuaapplicationrecorddatatype.cpp
new file mode 100644
index 0000000..6c92fb1
--- /dev/null
+++ b/src/opcua/client/qopcuaapplicationrecorddatatype.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuaapplicationrecorddatatype.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaApplicationRecordDataType
+ \inmodule QtOpcUa
+ \brief The OPC UA ApplicationRecordDataType.
+ \since 5.14
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+
+ This is the Qt OPC UA representation for the OPC UA ApplicationRecordDataType type defined in OPC-UA version 1.04 part 12, 6.3.5.
+ It is used to represent a record in the GDS.
+*/
+
+class QOpcUaApplicationRecordDataTypeData : public QSharedData
+{
+public:
+ QString applicationId;
+ QString applicationUri;
+ QOpcUaApplicationDescription::ApplicationType applicationType;
+ QString productUri;
+ QVector<QOpcUaLocalizedText> applicationNames;
+ QVector<QString> discoveryUrls;
+ QVector<QString> serverCapabilityIdentifiers;
+};
+
+/*!
+ Constructs a default ApplicationRecordDataType.
+*/
+QOpcUaApplicationRecordDataType::QOpcUaApplicationRecordDataType()
+ : data(new QOpcUaApplicationRecordDataTypeData)
+{
+ data->applicationId = QLatin1String("ns=0;i=0"); // empty node id
+}
+
+/*!
+ Constructs an ApplicationRecordDataType from \a rhs.
+*/
+QOpcUaApplicationRecordDataType::QOpcUaApplicationRecordDataType(const QOpcUaApplicationRecordDataType &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this ApplicationRecordDataType.
+*/
+QOpcUaApplicationRecordDataType &QOpcUaApplicationRecordDataType::operator=(const QOpcUaApplicationRecordDataType &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Returns \c true if this ApplicationRecordDataType has the same values as \a rhs.
+*/
+bool QOpcUaApplicationRecordDataType::operator==(const QOpcUaApplicationRecordDataType &rhs) const
+{
+ return data->applicationId == rhs.applicationId()
+ && data->applicationUri == rhs.applicationUri()
+ && data->applicationType == rhs.applicationType()
+ && data->productUri == rhs.productUri()
+ && data->applicationNames == rhs.applicationNames()
+ && data->discoveryUrls == rhs.discoveryUrls()
+ && data->serverCapabilityIdentifiers == rhs.serverCapabilityIdentifiers();
+}
+
+/*!
+ Converts this ApplicationRecordDataType to \l QVariant.
+*/
+QOpcUaApplicationRecordDataType::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+/*!
+ Destructs an ApplicationRecordDataType.
+*/
+QOpcUaApplicationRecordDataType::~QOpcUaApplicationRecordDataType()
+{
+}
+
+/*!
+ Sets the application id to \a applicationId.
+*/
+void QOpcUaApplicationRecordDataType::setApplicationId(const QString &applicationId)
+{
+ data->applicationId = applicationId;
+}
+
+/*!
+ Returns the application id.
+*/
+const QString &QOpcUaApplicationRecordDataType::applicationId() const
+{
+ return data->applicationId;
+}
+
+/*!
+ Sets the application URI to \a applicationUri.
+*/
+void QOpcUaApplicationRecordDataType::setApplicationUri(const QString &applicationUri)
+{
+ data->applicationUri = applicationUri;
+}
+
+/*!
+ Returns the application URI.
+*/
+const QString &QOpcUaApplicationRecordDataType::applicationUri() const
+{
+ return data->applicationUri;
+}
+
+/*!
+ Sets the application type to \a applicationType.
+*/
+void QOpcUaApplicationRecordDataType::setApplicationType(QOpcUaApplicationDescription::ApplicationType applicationType)
+{
+ data->applicationType = applicationType;
+}
+
+/*!
+ Returns the application type.
+*/
+QOpcUaApplicationDescription::ApplicationType QOpcUaApplicationRecordDataType::applicationType() const
+{
+ return data->applicationType;
+}
+
+/*!
+ Sets the localized application names to \a applicationNames.
+*/
+void QOpcUaApplicationRecordDataType::setApplicationNames(const QVector<QOpcUaLocalizedText> &applicationNames)
+{
+ data->applicationNames = applicationNames;
+}
+
+/*!
+ Returns the localized application names.
+*/
+const QVector<QOpcUaLocalizedText> &QOpcUaApplicationRecordDataType::applicationNames() const
+{
+ return data->applicationNames;
+}
+
+/*!
+ Sets the product URI to \a productUri.
+*/
+void QOpcUaApplicationRecordDataType::setProductUri(const QString &productUri)
+{
+ data->productUri = productUri;
+}
+
+/*!
+ Returns the product URI.
+*/
+const QString &QOpcUaApplicationRecordDataType::productUri() const
+{
+ return data->productUri;
+}
+
+/*!
+ Sets the discovery URLs to \a discoverUrls.
+*/
+void QOpcUaApplicationRecordDataType::setDiscoveryUrls(const QVector<QString> &discoveryUrls)
+{
+ data->discoveryUrls = discoveryUrls;
+}
+
+/*!
+ Returns the discovery URLs.
+*/
+const QVector<QString> &QOpcUaApplicationRecordDataType::discoveryUrls() const
+{
+ return data->discoveryUrls;
+}
+
+/*!
+ Sets the server capability identifiers to \a serverCapabilityIdentifiers.
+*/
+void QOpcUaApplicationRecordDataType::setServerCapabilityIdentifiers(const QVector<QString> &serverCapabilityIdentifiers)
+{
+ data->serverCapabilityIdentifiers = serverCapabilityIdentifiers;
+}
+
+/*!
+ Returns the server capability identifiers.
+*/
+const QVector<QString> &QOpcUaApplicationRecordDataType::serverCapabilityIdentifiers() const
+{
+ return data->serverCapabilityIdentifiers;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuaapplicationrecorddatatype.h b/src/opcua/client/qopcuaapplicationrecorddatatype.h
new file mode 100644
index 0000000..0988fd4
--- /dev/null
+++ b/src/opcua/client/qopcuaapplicationrecorddatatype.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAAPPLICATIONRECORDDATATYPE_H
+#define QOPCUAAPPLICATIONRECORDDATATYPE_H
+
+#include <QtOpcUa/qopcuaglobal.h>
+#include <QtOpcUa/qopcualocalizedtext.h>
+#include <QtOpcUa/qopcuaapplicationdescription.h>
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaApplicationRecordDataTypeData;
+class Q_OPCUA_EXPORT QOpcUaApplicationRecordDataType
+{
+public:
+ QOpcUaApplicationRecordDataType();
+ QOpcUaApplicationRecordDataType(const QOpcUaApplicationRecordDataType &);
+ QOpcUaApplicationRecordDataType &operator=(const QOpcUaApplicationRecordDataType &);
+ bool operator==(const QOpcUaApplicationRecordDataType &rhs) const;
+ operator QVariant() const;
+ ~QOpcUaApplicationRecordDataType();
+
+ void setApplicationId(const QString &nodeId);
+ const QString &applicationId() const;
+
+ void setApplicationType(QOpcUaApplicationDescription::ApplicationType applicationType);
+ QOpcUaApplicationDescription::ApplicationType applicationType() const;
+
+ void setApplicationUri(const QString &applicationUri);
+ const QString &applicationUri() const;
+
+ void setApplicationNames(const QVector<QOpcUaLocalizedText> &applicationNames);
+ const QVector<QOpcUaLocalizedText> &applicationNames() const;
+
+ void setProductUri(const QString &productUri);
+ const QString &productUri() const;
+
+ void setDiscoveryUrls(const QVector<QString> &discoveryUrls);
+ const QVector<QString> &discoveryUrls() const;
+
+ void setServerCapabilityIdentifiers(const QVector<QString> &serverCapabilityIdentifiers);
+ const QVector<QString> &serverCapabilityIdentifiers() const;
+
+private:
+ QSharedDataPointer<QOpcUaApplicationRecordDataTypeData> data;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QOpcUaApplicationRecordDataType)
+
+#endif // QOPCUAAPPLICATIONRECORDDATATYPE_H
diff --git a/src/opcua/client/qopcuabinarydataencoding.cpp b/src/opcua/client/qopcuabinarydataencoding.cpp
index 3b2739b..1746ff0 100644
--- a/src/opcua/client/qopcuabinarydataencoding.cpp
+++ b/src/opcua/client/qopcuabinarydataencoding.cpp
@@ -132,6 +132,10 @@ QT_BEGIN_NAMESPACE
\row
\li QOpcUaArgument
\li Argument
+
+ \row
+ \li QOpcUaApplicationRecordDataType
+ \li ApplicationRecordDataType
\endtable
*/
diff --git a/src/opcua/client/qopcuabinarydataencoding.h b/src/opcua/client/qopcuabinarydataencoding.h
index e613a0d..8b6e4ff 100644
--- a/src/opcua/client/qopcuabinarydataencoding.h
+++ b/src/opcua/client/qopcuabinarydataencoding.h
@@ -37,6 +37,7 @@
#ifndef QOPCUABINARYDATAENCODING_H
#define QOPCUABINARYDATAENCODING_H
+#include <QtOpcUa/qopcuaapplicationrecorddatatype.h>
#include <QtOpcUa/qopcuaargument.h>
#include <QtOpcUa/qopcuaaxisinformation.h>
#include <QtOpcUa/qopcuacomplexnumber.h>
@@ -993,6 +994,62 @@ inline bool QOpcUaBinaryDataEncoding::encodeArray(const QVector<T> &src)
return true;
}
+template <>
+inline QOpcUaApplicationRecordDataType QOpcUaBinaryDataEncoding::decode<QOpcUaApplicationRecordDataType>(bool &success)
+{
+ QOpcUaApplicationRecordDataType temp;
+
+ temp.setApplicationId(decode<QString, QOpcUa::Types::NodeId>(success));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ temp.setApplicationUri(decode<QString>(success));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ temp.setApplicationType(static_cast<QOpcUaApplicationDescription::ApplicationType>(decode<uint32_t>(success)));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ temp.setApplicationNames(decodeArray<QOpcUaLocalizedText>(success));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ temp.setProductUri(decode<QString>(success));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ temp.setDiscoveryUrls(decodeArray<QString>(success));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ temp.setServerCapabilityIdentifiers(decodeArray<QString>(success));
+ if (!success)
+ return QOpcUaApplicationRecordDataType();
+
+ return temp;
+}
+
+template <>
+inline bool QOpcUaBinaryDataEncoding::encode<QOpcUaApplicationRecordDataType>(const QOpcUaApplicationRecordDataType &src)
+{
+ if (!encode<QString, QOpcUa::NodeId>(src.applicationId()))
+ return false;
+ if (!encode<QString>(src.applicationUri()))
+ return false;
+ if (!encode<uint32_t>(src.applicationType()))
+ return false;
+ if (!encodeArray<QOpcUaLocalizedText>(src.applicationNames()))
+ return false;
+ if (!encode<QString>(src.productUri()))
+ return false;
+ if (!encodeArray<QString>(src.discoveryUrls()))
+ return false;
+ if (!encodeArray<QString>(src.serverCapabilityIdentifiers()))
+ return false;
+ return true;
+}
+
QT_END_NAMESPACE
#endif // QOPCUABINARYDATAENCODING_H
diff --git a/src/opcua/client/qopcuagdsclient.cpp b/src/opcua/client/qopcuagdsclient.cpp
new file mode 100644
index 0000000..55cc20c
--- /dev/null
+++ b/src/opcua/client/qopcuagdsclient.cpp
@@ -0,0 +1,1883 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuagdsclient_p.h"
+#include <QOpcUaProvider>
+#include <QOpcUaExtensionObject>
+#include <QOpcUaBinaryDataEncoding>
+#include <QOpcUaErrorState>
+#include <QOpcUaKeyPair>
+#include <QOpcUaX509ExtensionSubjectAlternativeName>
+#include <QOpcUaX509ExtensionBasicConstraints>
+#include <QOpcUaX509ExtensionKeyUsage>
+#include <QFile>
+#include <QTimer>
+#include <QTemporaryFile>
+#include <QSettings>
+#include <QSslCertificate>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QT_OPCUA_GDSCLIENT, "qt.opcua.gdsclient")
+
+static const QStringList elementsToResolve {
+ QLatin1String("GetApplication"),
+ QLatin1String("FindApplications"),
+ QLatin1String("RegisterApplication"),
+ QLatin1String("UnregisterApplication"),
+ QLatin1String("GetCertificateGroups"),
+ QLatin1String("GetCertificateStatus"),
+ QLatin1String("StartSigningRequest"),
+ QLatin1String("FinishRequest"),
+ QLatin1String("GetTrustList"),
+};
+
+/*!
+ \class QOpcUaGdsClient
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Handles communication with the GDS Server
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+
+ This class handles all steps needed for communication with a GDS server. Provided with
+ information about the application it does registering with the server and managing
+ key/certificates.
+
+ Only few details need to be known in order to take part in a secured network.
+
+ First time registration requires administrative privileges using username and password
+ for authentication. All further authentications are application based, using the certificate
+ which was received first.
+
+ Expecting the whole process to succeed, you have to wait for the applicationRegistered signal.
+
+ Most of the setup structs have to be the same as for the connection with QOpcUaClient afterwards
+ and can be shared.
+
+ Setting up a GDS client:
+
+ \code
+ QOpcUaGdsClient c;
+
+ // In case the credentials are needed
+ QObject::connect(&c, &QOpcUaGdsClient::authenticationRequired, [&](QOpcUaAuthenticationInformation &authInfo) {
+ authInfo.setUsernameAuthentication("root", "secret");
+ });
+
+ // Await success
+ QObject::connect(&c, &QOpcUaGdsClient::applicationRegistered, [&]() {
+ qDebug() << "Application" << c.applicationId() << "registered";
+ });
+
+ c.setBackend(...);
+ c.setEndpoint(...);
+ c.setApplicationIdentity(...);
+ c.setPkiConfiguration(...);
+ c.setApplicationRecord(...);
+ c.setCertificateSigningRequestPresets(...);
+ c.start();
+ \endcode
+*/
+
+/*!
+ \enum QOpcUaGdsClient::Error
+
+ This enum is used to specify errors, that could happen during the registration
+ process.
+
+ \value NoError
+ Everying is fine
+ \value InvalidBackend
+ The backend could not be instantiated.
+ The backend string given, does not match any backend or loading the plugin failed.
+ \value InvalidEndpoint
+ The given endpoint is not valid.
+ \value ConnectionError
+ The connection to the server endpoint failed.
+ \value DirectoryNodeNotFound
+ The directory node on the server could not be resolved
+ \value FailedToRegisterApplication
+ The registration of the application was not successful.
+ \value FailedToUnregisterApplication
+ The unregistration of the application was not successful.
+ \value FailedToGetCertificateStatus
+ The status of the current certificate could not be retrieved.
+ \value FailedToGetCertificate
+ A certificate could not be retrieved from the server.
+*/
+
+/*!
+ \enum QOpcUaGdsClient::State
+
+ This enum is used to specify the current state of the registration of the GDS
+ client.
+
+ \value Idle
+ The client was not started yet.
+ \value BackendInstantiated
+ The backend was instantiated
+ \value Connecting
+ A connecting to the server is being made
+ \value Connected
+ The connection to the server endpoint was successful.
+ \value RegisteringApplication
+ The application is being registered with the server.
+ \value ApplicationRegistered
+ Registering the application with the server was successful.
+ \value Error
+ An error happened. See the return value of error() and the terminal output
+ for more details.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::stateChanged(State state)
+
+ This signal is emitted when the internal state of the client changes.
+ The \a state indicates the new state.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::errorChanged(Error error)
+
+ This signal is emitted when an error occurred.
+ The \a state indicates the new state.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::applicationRegistered()
+
+ This signal is emitted when an application was registered successfully.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::certificateGroupsReceived(QStringList certificateGroups)
+
+ This signal is emitted when the GDS client receives a new list of certificateGroups
+ for this application.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::certificateUpdateRequired()
+
+ This signal is emitted when the GDS client detects that an update of the currently
+ used certificate is necessary.
+
+ This could be caused by the server, requesting the client to update the certificate,
+ when the certificate's due date is met or if the certificate is self-signed.
+
+ The certificate update is handled automatically. This signal is only for informational
+ purpose that an update is going to happen.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::certificateUpdated()
+
+ This signal is emitted when the GDS client received a new certificate that was
+ stored on disk.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::unregistered()
+
+ This signal is emitted when the GDS client has unregistered the application.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::trustListUpdated()
+
+ This signal is emitted when the GDS client has received a new trust list from the
+ server and stored to disk.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::trustListUpdated()
+
+ This signal is emitted when the GDS client has received a new trust list from the
+ server and stored to disk.
+*/
+
+/*!
+ \fn QOpcUaGdsClient::authenticationRequired(QOpcUaAuthenticationInformation &authInfo)
+
+ This signal is emitted when the GDS client tries to do a first time authentication
+ with a server, that requires administrative privileges.
+
+ \a authInfo has to be filled with valid authentication information.
+ This slot must not be used crossing thread boundaries.
+*/
+
+/*
+ Step 1
+ Create a self-signed certificate for the application.
+ Previously created key and certificate are reused if present.
+
+ Step 2
+ Connect to the given endpoint and wait for the namespace array to be updated.
+
+ Step 3
+ Find the root node of the GDS API
+
+ Step 4
+ From the root node find the "Directory" node where the method nodes are located.
+ - resolveDirectoryNode()
+ - lambda
+
+ Step 5
+ Resolve the method nodes that are being used.
+ - resolveMethodNodes()
+ - _q_handleResolveBrowsePathFinished()
+
+ Step 6a
+ In case a previous appliation id is known, check if the application registration is still valid.
+ In case it is still valid, reuse it.
+ - getApplication()
+ - handleGetApplicationFinished()
+
+ Step 6b
+ In case there is no valid application id known, try to find a previous registration by the application URI.
+ - findRegisteredApplication()
+ - handleFindApplicationsFinished()
+
+ Step 6c
+ In case an application id was not found by the application URI, register the application.
+ - registerApplication()
+ - handleRegisterApplicationFinished()
+
+ Step 7
+ Get the certificate groups of the application
+ - getCertificateGroups()
+ - handleGetCertificateGroupsFinished()
+ - emit signal certificateGroupsReceived
+
+ Step 8
+ Resolve the certificate types from the certificate groups (CertificateGroup->CertificateTypes)
+ - resolveCertificateTypes()
+ - lambda
+
+ Step 9
+ Read certificate type value from the resolved nodes
+ - getCertificateTypes()
+ - lambda
+
+ Step 10
+ The application now is using a application id from 6a, 6b or 6c, resolved the certificates and is ready to interact with the GDS.
+ - registrationDone()
+ - emit signal applicationRegistered
+ -> Start a certificate check
+ -> Start a trust list update
+
+ Depending on the certificate state, the certificate needs to be updated by the client.
+ This may happen due to
+ - the server requires it (GetCertificateStatus)
+ - the expiry date is due
+ - the certificate is self-signed from the initial connection
+
+ Step 1
+ A timer will trigger the certificate check regularly by triggering
+ - _q_certificateCheckTimeout()
+ - emit signal certificateUpdateRequired
+
+ Step 2a
+ Check locally first, if a renew condition is met.
+ Otherwise ask the server.
+
+ Step 2b
+ Ask the server if the certificate needs renewal
+ - getCertificateStatus()
+ - handleGetCertificateStatusFinished()
+ - emit signal certificateUpdateRequired
+
+ Step 3
+ Start a certificate signing request
+ - startCertificateRequest()
+ - handleStartSigningRequestFinished()
+
+ Step 4
+ Regularly ask the server for the certificate signing result
+ - finishCertificateRequest()
+ - handleFinishRequestFinished()
+ - emit signal certificateUpdated
+ This will return a new certificate.
+*/
+
+/*!
+ Constructs a GDS client
+*/
+QOpcUaGdsClient::QOpcUaGdsClient(QObject *parent)
+ : QObject(*(new QOpcUaGdsClientPrivate()), parent)
+{
+ Q_D(QOpcUaGdsClient);
+ d->initializePrivateConnections();
+}
+
+/*!
+ Destructs a GDS client
+*/
+QOpcUaGdsClient::~QOpcUaGdsClient()
+{
+
+}
+
+/*!
+ Sets the backend to be used by the client to communicate with the server to \a backend.
+
+ This function has to be called before starting the GDS client.
+ Changing this setting afterwards has no effect.
+
+ \sa QOpcUaProvider::availableBackends() start()
+*/
+void QOpcUaGdsClient::setBackend(const QString &backend)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setBackend(backend);
+}
+
+/*!
+ Returns the current backend setting.
+
+ If the backend was changed after starting the client, it will return
+ the changed setting, but not the actually used instance.
+*/
+const QString &QOpcUaGdsClient::backend() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->backend();
+}
+
+/*!
+ Sets the endpoint to be used by the client to communicate with the server to \a endpoint.
+
+ This function has to be called before starting the GDS client.
+ Changing this setting afterwards has no effect.
+
+ Communication to a GDS server is only possible through an encrypted endpoint.
+ Using an unencrypted endpoint will fail.
+*/
+void QOpcUaGdsClient::setEndpoint(const QOpcUaEndpointDescription &endpoint)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setEndpoint(endpoint);
+}
+
+/*!
+ Returns the current endpoint setting.
+
+ If the endpoint was changed after starting the client, it will return
+ the changed setting, but not the actually used endpoint.
+*/
+const QOpcUaEndpointDescription &QOpcUaGdsClient::endpoint() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->endpoint();
+}
+
+/*!
+ Sets the PKI configuration to be used by the client.
+
+ All certificates, keys and trust lists will be used from or stored to
+ the locations given. In order to use the certificate received from
+ the GDS, the same configuration has to be used with QOpcUaClient.
+
+ This function has to be called before starting the GDS client.
+ Changing this setting afterwards has no effect.
+*/
+void QOpcUaGdsClient::setPkiConfiguration(const QOpcUaPkiConfiguration &pkiConfig)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setPkiConfiguration(pkiConfig);
+}
+
+/*!
+ Returns the current pkiConfiguration.
+*/
+const QOpcUaPkiConfiguration &QOpcUaGdsClient::pkiConfiguration() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->pkiConfiguration();
+}
+
+/*!
+ Sets the application identity to be used by the client.
+
+ This identity is used to register with the GDS server.
+ This function has to be called before starting the GDS client.
+ Changing this setting afterwards has no effect.
+*/
+void QOpcUaGdsClient::setApplicationIdentity(const QOpcUaApplicationIdentity &appIdentity)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setApplicationIdentity(appIdentity);
+}
+
+/*!
+ Returns the current applicationIdentity.
+*/
+const QOpcUaApplicationIdentity &QOpcUaGdsClient::applicationIdentity() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->applicationIdentity();
+}
+
+/*!
+ Sets the application record data to be used by the client.
+
+ This data is used to register with the GDS server.
+ This function has to be called before starting the GDS client.
+
+ Most of the data is the same as in the application identity.
+ After registration the assigned application id can be retrieved.
+
+ \sa setApplicationIdentity
+*/
+void QOpcUaGdsClient::setApplicationRecord(const QOpcUaApplicationRecordDataType &appRecord)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setApplicationRecord(appRecord);
+}
+
+/*!
+ Returns the application record data that is used by the client.
+*/
+const QOpcUaApplicationRecordDataType &QOpcUaGdsClient::applicationRecord() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->applicationRecord();
+}
+
+/*!
+ Returns the application id assigned by the server.
+
+ Is is a shortcut to receive the application id from the application record data.
+
+ \sa applicationRecord()
+*/
+QString QOpcUaGdsClient::applicationId() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->applicationId();
+}
+
+/*!
+ Sets the presets for certificate siging requests.
+
+ When creating a certificate signing request some additional information is needed,
+ that is not provided by the application identity.
+
+ This function has to be called before starting the GDS client.
+
+ \sa setApplicationIdentity()
+*/
+void QOpcUaGdsClient::setCertificateSigningRequestPresets(const QOpcUaX509DistinguishedName &dn, const QString &dns)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setCertificateSigningRequestPresets(dn, dns);
+}
+
+/*!
+ Returns the distinguished name preset for certificate siging requests.
+*/
+const QOpcUaX509DistinguishedName &QOpcUaGdsClient::distinguishedNameCertificateSigningRequestPreset() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->distinguishedNameCertificateSigningRequestPreset();
+}
+
+/*!
+ Returns the DNS preset for certificate siging requests.
+*/
+const QString &QOpcUaGdsClient::dnsCertificateSigningRequestPreset() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->dnsCertificateSigningRequestPreset();
+}
+
+/*!
+ Sets the interval in milliseconds for checking the validity of the client certificate
+ to \a interval.
+*/
+void QOpcUaGdsClient::setCertificateCheckInterval(int interval)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setCertificateCheckInterval(interval);
+}
+
+/*!
+ Returns the interval in milliseconds for checking the validity of the client certificate.
+*/
+int QOpcUaGdsClient::certificateCheckInterval() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->certificateCheckInterval();
+}
+
+/*!
+ Sets the interval in milliseconds for updating the trust list from the server
+ to \a interval.
+*/
+void QOpcUaGdsClient::setTrustListUpdateInterval(int interval)
+{
+ Q_D(QOpcUaGdsClient);
+ d->setTrustListUpdateInterval(interval);
+}
+
+/*!
+ Returns the interval in milliseconds for updating the trust list from the server.
+*/
+int QOpcUaGdsClient::trustListUpdateInterval() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->trustListUpdateInterval();
+}
+
+/*!
+ Returns the current error state.
+*/
+QOpcUaGdsClient::Error QOpcUaGdsClient::error() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->error();
+}
+
+/*!
+ Returns the current client state.
+*/
+QOpcUaGdsClient::State QOpcUaGdsClient::state() const
+{
+ Q_D(const QOpcUaGdsClient);
+ return d->state();
+}
+
+/*!
+ Starts the client process.
+
+ After setting up all information,
+ the client can be started.
+
+ \list
+ \li setBackend
+ \li setEndpoing
+ \li setApplicationIdentity
+ \li setPkiConfiguration
+ \li setApplicationRecord
+ \li setCertificateSigingRequestPresets
+ \endlist
+*/
+void QOpcUaGdsClient::start()
+{
+ Q_D(QOpcUaGdsClient);
+ return d->start();
+}
+
+/*!
+ Unregisters an application from the server.
+
+ This function can be used when an application has to be removed permanently from
+ the network. It does not need to be called when rebooting or shutting down.
+*/
+void QOpcUaGdsClient::unregisterApplication()
+{
+ Q_D(QOpcUaGdsClient);
+ return d->unregisterApplication();
+}
+
+QOpcUaGdsClientPrivate::QOpcUaGdsClientPrivate()
+ : QObjectPrivate()
+ , m_certificateCheckTimer(new QTimer)
+ , m_trustListUpdateTimer(new QTimer)
+{
+ m_certificateCheckTimer->setInterval(60*60*1000);
+ m_trustListUpdateTimer->setInterval(60*60*1000);
+}
+
+QOpcUaGdsClientPrivate::~QOpcUaGdsClientPrivate()
+{
+ delete m_client;
+ delete m_directoryNode;
+ delete m_certificateGroupNode;
+ delete m_certificateTypesNode;
+ delete m_certificateFinishTimer;
+ delete m_certificateCheckTimer;
+ delete m_trustListUpdateTimer;
+ delete m_trustListNode;
+}
+
+void QOpcUaGdsClientPrivate::initializePrivateConnections()
+{
+ Q_Q(QOpcUaGdsClient);
+ QObject::connect(m_certificateCheckTimer, SIGNAL(timeout()), q, SLOT(_q_certificateCheckTimeout()));
+ QObject::connect(m_trustListUpdateTimer, SIGNAL(timeout()), q, SLOT(_q_updateTrustList()));
+}
+
+void QOpcUaGdsClientPrivate::setEndpoint(const QOpcUaEndpointDescription &endpoint)
+{
+ m_endpoint = endpoint;
+}
+
+const QOpcUaEndpointDescription &QOpcUaGdsClientPrivate::endpoint() const
+{
+ return m_endpoint;
+}
+
+void QOpcUaGdsClientPrivate::setBackend(const QString &backend)
+{
+ m_backend = backend;
+}
+
+const QString &QOpcUaGdsClientPrivate::backend() const
+{
+ return m_backend;
+}
+
+QString QOpcUaGdsClientPrivate::applicationId() const
+{
+ return m_appRecord.applicationId();
+}
+
+void QOpcUaGdsClientPrivate::setError(QOpcUaGdsClient::Error error)
+{
+ Q_Q(QOpcUaGdsClient);
+ m_error = error;
+ setState(QOpcUaGdsClient::State::Error);
+ emit q->errorChanged(m_error);
+}
+
+void QOpcUaGdsClientPrivate::setState(QOpcUaGdsClient::State state)
+{
+ Q_Q(QOpcUaGdsClient);
+ m_state = state;
+ emit q->stateChanged(state);
+}
+
+void QOpcUaGdsClientPrivate::start()
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (m_backend.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Backend name not set";
+ setError(QOpcUaGdsClient::Error::InvalidBackend);
+ return;
+ }
+
+ if (!m_client) {
+ QOpcUaProvider provider;
+ setState(QOpcUaGdsClient::State::BackendInstantiated);
+ m_client = provider.createClient(m_backend);
+ if (!m_client) {
+ setError(QOpcUaGdsClient::Error::InvalidBackend);
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid backend";
+ return;
+ }
+
+ QObject::connect(m_client, &QOpcUaClient::namespaceArrayUpdated, [this]() {
+ setState(QOpcUaGdsClient::State::Connected);
+ this->resolveDirectoryNode();
+ });
+
+ QObject::connect(m_client, &QOpcUaClient::errorChanged, q, [this](QOpcUaClient::ClientError error) {
+ if (error == QOpcUaClient::InvalidUrl) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid URL";
+ setError(QOpcUaGdsClient::Error::InvalidEndpoint);
+ } else {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Connection error";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ }
+ });
+
+ QObject::connect(m_client, &QOpcUaClient::disconnected, q, [this]() {
+ delete m_directoryNode;
+ m_directoryNode = nullptr;
+ m_directoryNodes.clear();
+ setState(QOpcUaGdsClient::State::Idle);
+ if (m_restartRequired) {
+ m_restartRequired = false;
+ this->start();
+ }
+ });
+
+ QObject::connect(m_client, &QOpcUaClient::connectError, [](QOpcUaErrorState *errorState) {
+ // Ignore all client side errors and continue
+ if (errorState->isClientSideError())
+ errorState->setIgnoreError();
+ });
+ }
+
+ setState(QOpcUaGdsClient::State::Connecting);
+ if (m_endpoint.endpointUrl().isEmpty()) {
+ setError(QOpcUaGdsClient::Error::InvalidEndpoint);
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid endpoint";
+ return;
+ }
+
+ QOpcUaKeyPair keyPair;
+ QFile keyFile(m_pkiConfig.privateKeyFile());
+
+ if (!keyFile.exists()) {
+ // File does not exist: Create a key
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Creating a key";
+ keyPair.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+
+ if (!keyFile.open(QFile::WriteOnly | QFile::NewOnly)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to open private key file" << keyFile.fileName() << "for writing:" << keyFile.errorString();
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ auto data = keyPair.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString());
+
+ if (!keyFile.resize(data.size())) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to set file size";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ if (!keyFile.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to set permissions";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ keyFile.write(data);
+ keyFile.close();
+ } else {
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Using private key" << keyFile.fileName();
+
+ if (!keyFile.open(QFile::ReadOnly)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to open private key file" << keyFile.fileName() << "for reading:" << keyFile.errorString();
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ auto data = keyFile.readAll();
+ keyFile.close();
+
+ if (!keyPair.loadFromPemData(data)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to load private key";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ if (!keyPair.hasPrivateKey()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Private key does not contain a private key";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+ }
+
+ QFile certFile(m_pkiConfig.clientCertificateFile());
+ if (!certFile.exists()) {
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Creating self-signed certificate in" << certFile.fileName();
+
+ auto csr = createSigningRequest();
+ csr.setEncoding(QOpcUaX509CertificateSigningRequest::Encoding::DER);
+ auto selfSigned = csr.createSelfSignedCertificate(keyPair);
+
+ if (!certFile.open(QFile::WriteOnly | QFile::NewOnly)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to open certificate file" << certFile.fileName() << "for writing:" << certFile.errorString();
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ if (!certFile.resize(selfSigned.size())) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to set file size 2";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ certFile.write(selfSigned);
+ certFile.close();
+ }
+
+ // Load persistent data
+ QSettings settings;
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Using settings from" << settings.fileName();
+ const auto applicationId = settings.value(QLatin1String("gds/applicationId"), QString()).toString();
+ if (applicationId.isEmpty())
+ qCInfo(QT_OPCUA_GDSCLIENT) << "No application ID in persistent storage";
+ else
+ m_appRecord.setApplicationId(applicationId);
+
+ m_client->setApplicationIdentity(m_appIdentitiy);
+ m_client->setPkiConfiguration(m_pkiConfig);
+ m_client->connectToEndpoint(m_endpoint);
+}
+
+void QOpcUaGdsClientPrivate::resolveDirectoryNode()
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ delete m_directoryNode;
+ m_directoryNode = m_client->node(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ObjectsFolder)); // ns=0;i=85
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Root node not found";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ m_gdsNamespaceIndex = m_client->namespaceArray().indexOf(QLatin1String("http://opcfoundation.org/UA/GDS/"));
+ if (m_gdsNamespaceIndex < 0) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Namespace not found";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ QObject::connect(m_directoryNode, &QOpcUaNode::resolveBrowsePathFinished, [this, q](QVector<QOpcUaBrowsePathTarget> targets,
+ QVector<QOpcUaRelativePathElement> path,
+ QOpcUa::UaStatusCode statusCode) {
+ m_directoryNode->deleteLater();
+ m_directoryNode = nullptr;
+
+ if (path.size() != 1) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid path size";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (path[0].targetName().name() != QLatin1String("Directory")) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid resolve name" << path[0].targetName().name();
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Resolving directory failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (targets.size() != 1) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid number of results";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (!targets[0].isFullyResolved()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Directory not fully resolved";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ m_directoryNode = m_client->node(targets[0].targetId());
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid directory node";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ QObject::connect(m_directoryNode, SIGNAL(resolveBrowsePathFinished(QVector<QOpcUaBrowsePathTarget>, QVector<QOpcUaRelativePathElement>, QOpcUa::UaStatusCode)),
+ q, SLOT(_q_handleResolveBrowsePathFinished(QVector<QOpcUaBrowsePathTarget>, QVector<QOpcUaRelativePathElement>, QOpcUa::UaStatusCode)));
+ QObject::connect(m_directoryNode, SIGNAL(methodCallFinished(QString, QVariant, QOpcUa::UaStatusCode)),
+ q, SLOT(_q_handleDirectoryNodeMethodCallFinished(QString, QVariant, QOpcUa::UaStatusCode)));
+
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Directory node resolved:" << m_directoryNode->nodeId();
+ this->resolveMethodNodes();
+ });
+
+ QOpcUaRelativePathElement pathElement(QOpcUaQualifiedName(m_gdsNamespaceIndex, QLatin1String("Directory")),
+ QOpcUa::ReferenceTypeId::Organizes);
+ QVector<QOpcUaRelativePathElement> browsePath { pathElement };
+
+ if (!m_directoryNode->resolveBrowsePath(browsePath)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to resolve directory node";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ delete m_directoryNode;
+ m_directoryNode = nullptr;
+ return;
+ }
+}
+
+void QOpcUaGdsClientPrivate::resolveMethodNodes()
+{
+ // See OPC UA Specification 1.04 part 12 6.3.2 "Directory"
+
+ QOpcUaRelativePathElement pathElement(QOpcUaQualifiedName(m_gdsNamespaceIndex, QLatin1String()),
+ QOpcUa::ReferenceTypeId::HasComponent);
+ QVector<QOpcUaRelativePathElement> browsePath { pathElement };
+
+
+ // Resolve all needed nodes from the directory
+ for (const auto &key : qAsConst(elementsToResolve)) {
+ if (!m_directoryNodes.value(key).isEmpty())
+ continue; // Already resolved
+
+ auto target = browsePath[0].targetName();
+ target.setName(key);
+ browsePath[0].setTargetName(target);
+
+ if (!m_directoryNode->resolveBrowsePath(browsePath)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not resolve Directory node";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+ }
+}
+
+void QOpcUaGdsClientPrivate::_q_handleResolveBrowsePathFinished(QVector<QOpcUaBrowsePathTarget> targets, QVector<QOpcUaRelativePathElement> path, QOpcUa::UaStatusCode statusCode) {
+ if (path.size() != 1) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid path size";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (m_directoryNodes.contains(path[0].targetName().name()) || !elementsToResolve.contains(path[0].targetName().name())) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid resolve name" << path[0].targetName().name();
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Resolving directory failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (targets.size() != 1) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid number of results";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ if (!targets[0].isFullyResolved()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Directory not fully resolved";
+ setError(QOpcUaGdsClient::Error::DirectoryNodeNotFound);
+ return;
+ }
+
+ m_directoryNodes[path[0].targetName().name()] = targets[0].targetId().nodeId();
+
+ if (elementsToResolve.size() == m_directoryNodes.size()) {
+ qCDebug(QT_OPCUA_GDSCLIENT) << "All symbols resolved";
+
+ if (m_appRecord.applicationId().isEmpty() || m_appRecord.applicationId() == QLatin1String("ns=0;i=0"))
+ this->findRegisteredApplication();
+ else
+ this->getApplication();
+ }
+}
+
+void QOpcUaGdsClientPrivate::getApplication()
+{
+ // See OPC UA Specification 1.04 part 12 6.3.9 "GetApplication"
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("GetApplication")],
+ QVector<QPair<QVariant, QOpcUa::Types>> { qMakePair(QVariant(m_appRecord.applicationId()), QOpcUa::Types::NodeId) })) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call method GetApplication";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleGetApplicationFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ if (statusCode == QOpcUa::BadNotFound) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No application with ID" << m_appRecord.applicationId() << "registered";
+ // Clear application ID and register
+ m_appRecord.setApplicationId(QString());
+
+ // Remove invalid id from settings
+ QSettings settings;
+ settings.remove(QLatin1String("gds/applicationId"));
+ settings.sync();
+
+ restartWithCredentials();
+ return;
+ }
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Get application failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ auto extensionObject = result.value<QOpcUaExtensionObject>();
+ if (!extensionObject.encodingTypeId().isEmpty()) {
+ if (extensionObject.encodingTypeId() != QOpcUa::nodeIdFromInteger(m_gdsNamespaceIndex, ApplicationRecordDataType_Encoding_DefaultBinary)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Unexpected return type:" << extensionObject.encodingTypeId();
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ auto buffer = extensionObject.encodedBody();
+ QOpcUaBinaryDataEncoding decoder(&buffer);
+ bool ok;
+ QOpcUaApplicationRecordDataType appRecord = decoder.decode<QOpcUaApplicationRecordDataType>(ok);
+
+ if (!ok) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to decode data";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ if (m_appRecord.applicationId() != appRecord.applicationId())
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Returned application record contains a different application ID" << m_appRecord.applicationId() << appRecord.applicationId();
+
+ // FIXME: To be removed
+ appRecord.setApplicationId(m_appRecord.applicationId());
+
+ m_appRecord = appRecord;
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Reusing application ID" << m_appRecord.applicationId() << "which is already registered at the server";
+ }
+ getCertificateGroups();
+}
+
+void QOpcUaGdsClientPrivate::findRegisteredApplication()
+{
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("FindApplications")],
+ QVector<QPair<QVariant, QOpcUa::Types>> { qMakePair(QVariant(m_appRecord.applicationUri()), QOpcUa::Types::String) })) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call method";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleFindApplicationsFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Find application failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ auto extensionObject = result.value<QOpcUaExtensionObject>();
+
+ if (extensionObject.encodingTypeId().isEmpty()) {
+ qCInfo(QT_OPCUA_GDSCLIENT) << "No application with URI" << m_appIdentitiy.applicationUri() << "registered";
+ this->registerApplication();
+ return;
+ }
+
+ if (extensionObject.encodingTypeId() != QOpcUa::nodeIdFromInteger(m_gdsNamespaceIndex, ApplicationRecordDataType_Encoding_DefaultBinary)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Unexpected return type:" << extensionObject.encodingTypeId();
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ auto buffer = extensionObject.encodedBody();
+ QOpcUaBinaryDataEncoding decoder(&buffer);
+ bool ok;
+ QOpcUaApplicationRecordDataType appRecord = decoder.decode<QOpcUaApplicationRecordDataType>(ok);
+
+ if (!ok) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to decode data";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ m_appRecord = appRecord;
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Reusing application ID" << appRecord.applicationId() << "registered for URI" << appRecord.applicationUri();
+ getCertificateGroups();
+}
+
+void QOpcUaGdsClientPrivate::registerApplication()
+{
+ if (!m_appRecord.applicationId().isEmpty() && m_appRecord.applicationId() != QLatin1String("ns=0;i=0"))
+ return;
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ QByteArray buffer;
+ QOpcUaBinaryDataEncoding encoder(&buffer);
+ if (!encoder.encode(m_appRecord)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to encode body";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ QOpcUaExtensionObject parameter;
+ parameter.setBinaryEncodedBody(buffer, QOpcUa::nodeIdFromInteger(m_gdsNamespaceIndex, ApplicationRecordDataType_Encoding_DefaultBinary));
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("RegisterApplication")],
+ QVector<QOpcUa::TypedVariant> { QOpcUa::TypedVariant(parameter, QOpcUa::ExtensionObject) })) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call method RegisterApplication";
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleRegisterApplicationFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ if (statusCode != QOpcUa::Good) {
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Requesting user credentials";
+ if (statusCode == QOpcUa::BadUserAccessDenied && m_client->authenticationInformation().authenticationType() == QOpcUaUserTokenPolicy::Anonymous) {
+ restartWithCredentials();
+ return;
+ }
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Register application failed with" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToRegisterApplication);
+ return;
+ }
+
+ m_appRecord.setApplicationId(result.toString());
+
+ QSettings settings;
+ settings.setValue(QLatin1String("gds/applicationId"), m_appRecord.applicationId());
+ settings.sync();
+
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Registered application with id" << m_appRecord.applicationId();
+ getCertificateGroups();
+}
+
+void QOpcUaGdsClientPrivate::getCertificateGroups()
+{
+ // OPC UA Specification Version 1.04 Part 4 Chapter 7.6.6 GetCertificateGroups
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QVector<QOpcUa::TypedVariant> arguments;
+ arguments.push_back(QOpcUa::TypedVariant(m_appRecord.applicationId(), QOpcUa::NodeId));
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("GetCertificateGroups")], arguments)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call GetCertificateGroups";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleGetCertificateGroupsFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Getting certificate groups failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ m_certificateGroups = result.value<QStringList>();
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Certificate groups:" << m_certificateGroups;
+
+ emit q->certificateGroupsReceived(m_certificateGroups);
+ resolveCertificateTypes();
+}
+
+void QOpcUaGdsClientPrivate::resolveCertificateTypes()
+{
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (m_certificateGroups.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Certificate groups is empty";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ delete m_certificateGroupNode;
+ m_certificateGroupNode = m_client->node(m_certificateGroups[0]);
+
+ if (!m_certificateGroupNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Certificate node failed";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QObject::connect(m_certificateGroupNode, &QOpcUaNode::resolveBrowsePathFinished, [this](QVector<QOpcUaBrowsePathTarget> targets,
+ QVector<QOpcUaRelativePathElement> path,
+ QOpcUa::UaStatusCode statusCode) {
+ if (path.size() != 1) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid path size";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (path[0].targetName().name() != QLatin1String("CertificateTypes")) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid resolve name" << path[0].targetName().name();
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Resolving certificate types failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (targets.size() != 1) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Invalid number of results";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (!targets[0].isFullyResolved()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Certificate types not fully resolved";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ m_certificateTypesNodeId = targets[0].targetId().nodeId();
+ this->getCertificateTypes();
+ });
+
+ QOpcUaRelativePathElement pathElement(QOpcUaQualifiedName(0, QLatin1String("CertificateTypes")),
+ QOpcUa::ReferenceTypeId::Unspecified);
+ QVector<QOpcUaRelativePathElement> browsePath { pathElement };
+
+
+ if (!m_certificateGroupNode->resolveBrowsePath(browsePath)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not resolve certificate type";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+}
+
+void QOpcUaGdsClientPrivate::getCertificateTypes()
+{
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (m_certificateTypesNodeId.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Certificate types node id is empty";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ delete m_certificateTypesNode;
+ m_certificateTypesNode = m_client->node(m_certificateTypesNodeId);
+
+ if (!m_certificateTypesNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Certificate types node failed";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QObject::connect(m_certificateTypesNode, &QOpcUaNode::attributeUpdated, [this](QOpcUa::NodeAttribute attr, QVariant value) {
+ if (attr == QOpcUa::NodeAttribute::Value)
+ qCWarning(QT_OPCUA_GDSCLIENT) << "possible certificate types" << value;
+ registrationDone();
+ });
+
+ if (!m_certificateTypesNode->readValueAttribute()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not read certificate types";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+}
+
+void QOpcUaGdsClientPrivate::registrationDone()
+{
+ Q_Q(QOpcUaGdsClient);
+
+ setState(QOpcUaGdsClient::State::ApplicationRegistered);
+ emit q->applicationRegistered();
+
+ m_certificateCheckTimer->start();
+ _q_certificateCheckTimeout(); // Force a check now
+
+ m_trustListUpdateTimer->start();
+ _q_updateTrustList(); // Force a check now
+}
+
+void QOpcUaGdsClientPrivate::getCertificateStatus()
+{
+ // OPC UA Specification Version 1.04 Part 4 Chapter 7.6.8 GetCertificateStatus
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (m_certificateGroups.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No certificate groups received";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QString certificateType = QLatin1String("ns=0;i=0"); // null node
+
+ QVector<QOpcUa::TypedVariant> arguments;
+ arguments.push_back(QOpcUa::TypedVariant(m_appRecord.applicationId(), QOpcUa::NodeId));
+ // Let the server choose the certificate group id
+ arguments.push_back(QOpcUa::TypedVariant(m_certificateGroups[0], QOpcUa::NodeId));
+ // Let the server choose the certificate type id
+ arguments.push_back(QOpcUa::TypedVariant(certificateType, QOpcUa::NodeId));
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("GetCertificateStatus")], arguments)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call GetCertificateStatus";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleGetCertificateStatusFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Getting certificate status failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ if (result.toBool()) {
+ qInfo(QT_OPCUA_GDSCLIENT) << "Certificate needs update";
+ emit q->certificateUpdateRequired();
+ startCertificateRequest();
+ }
+}
+
+void QOpcUaGdsClientPrivate::startCertificateRequest()
+{
+ // OPC UA Specification Version 1.04 Part 4 Chapter 7.6.3 StartSigningRequest
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QOpcUaKeyPair keyPair;
+ QFile keyFile(m_pkiConfig.privateKeyFile());
+
+ qCDebug(QT_OPCUA_GDSCLIENT) << "Using private key" << keyFile.fileName();
+
+ if (!keyFile.open(QFile::ReadOnly)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to open private key file" << keyFile.fileName() << "for reading:" << keyFile.errorString();
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ auto data = keyFile.readAll();
+ keyFile.close();
+
+ if (!keyPair.loadFromPemData(data)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to load private key";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ if (!keyPair.hasPrivateKey()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Private key does not contain a private key";
+ setError(QOpcUaGdsClient::Error::ConnectionError);
+ return;
+ }
+
+ auto csr = createSigningRequest();
+ csr.setEncoding(QOpcUaX509CertificateSigningRequest::Encoding::DER);
+ const auto csrData = csr.createRequest(keyPair);
+
+
+ QVector<QOpcUa::TypedVariant> arguments;
+ arguments.push_back(QOpcUa::TypedVariant(m_appRecord.applicationId(), QOpcUa::NodeId));
+ // Let the server choose the certificate group id
+ arguments.push_back(QOpcUa::TypedVariant(QLatin1String("ns=0;i=0"), QOpcUa::NodeId));
+ // Let the server choose the certificate type id
+ arguments.push_back(QOpcUa::TypedVariant(QLatin1String("ns=0;i=0"), QOpcUa::NodeId));
+
+ arguments.push_back(QOpcUa::TypedVariant(csrData, QOpcUa::ByteString));
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("StartSigningRequest")], arguments)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call StartSigningRequest";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleStartSigningRequestFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Getting certificate failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ m_certificateRequestId = result.toString();
+
+ if (!m_certificateFinishTimer)
+ m_certificateFinishTimer = new QTimer;
+
+ m_certificateFinishTimer->setInterval(2000);
+ m_certificateFinishTimer->setSingleShot(true);
+ QObject::connect(m_certificateFinishTimer, &QTimer::timeout, [this]() {
+ this->finishCertificateRequest();
+ });
+
+ m_certificateFinishTimer->start();
+}
+
+void QOpcUaGdsClientPrivate::finishCertificateRequest()
+{
+ // OPC UA Specification Version 1.04 Part 4 Chapter 7.6.5 FinishRequest
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (m_certificateRequestId.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No certificate request id";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QVector<QOpcUa::TypedVariant> arguments;
+ arguments.push_back(QOpcUa::TypedVariant(m_appRecord.applicationId(), QOpcUa::NodeId));
+ arguments.push_back(QOpcUa::TypedVariant(m_certificateRequestId, QOpcUa::NodeId));
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("FinishRequest")], arguments)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call FinishRequest";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleFinishRequestFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (statusCode == QOpcUa::BadNothingToDo) {
+ // Server not finished yet: Try again later
+ m_certificateFinishTimer->setInterval(m_certificateFinishTimer->interval() * 2);
+ m_certificateFinishTimer->start();
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Server not finished yet: Trying again in" << m_certificateFinishTimer->interval() / 1000 << "s";
+ return;
+ }
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Getting certificate failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ m_certificateRequestId.clear();
+ const auto resultList = result.toList();
+
+ if (resultList.size() != 3) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Expected list of 3 results but got" << resultList.size();
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ const auto certificate = resultList[0].toByteArray();
+ //const auto privateKey = resultList[1].toByteArray();
+ const auto issuerList = resultList[2].toByteArray();
+
+ if (certificate.isEmpty() || issuerList.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Certificates are empty";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Received new certificate" << certificate;
+
+ QFile certificateFile(m_pkiConfig.clientCertificateFile());
+ if (!certificateFile.open(QFile::WriteOnly)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not store certificate";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ if (certificateFile.write(certificate) != certificate.size()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not store certificate data";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ certificateFile.close();
+
+ // FIMXE: How to store this?
+ QTemporaryFile issuerFile(m_pkiConfig.issuerListDirectory() + QLatin1String("/XXXXXX.der"));
+ if (!issuerFile.open()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not store issuer certificates to" << m_pkiConfig.issuerListDirectory();
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ if (issuerFile.write(issuerList) != issuerList.size()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not store certificate data";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ qCDebug(QT_OPCUA_GDSCLIENT) << "issuer list stored to" << issuerFile.fileName();
+ issuerFile.close();
+ issuerFile.setAutoRemove(false);
+
+ emit q->certificateUpdated();
+}
+
+void QOpcUaGdsClientPrivate::unregisterApplication()
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (m_appRecord.applicationId().isEmpty() || m_appRecord.applicationId() == QLatin1String("ns=0;i=0")) {
+ emit q->unregistered();
+ return;
+ }
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ setError(QOpcUaGdsClient::Error::FailedToUnregisterApplication);
+ return;
+ }
+
+ if (!m_directoryNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No directory node";
+ setError(QOpcUaGdsClient::Error::FailedToUnregisterApplication);
+ return;
+ }
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("UnregisterApplication")],
+ QVector<QOpcUa::TypedVariant> { QOpcUa::TypedVariant(m_appRecord.applicationId(), QOpcUa::NodeId) })) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call method UnregisterApplication";
+ setError(QOpcUaGdsClient::Error::FailedToUnregisterApplication);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleUnregisterApplicationFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ Q_Q(QOpcUaGdsClient);
+ Q_UNUSED(result);
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Unregister application failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToUnregisterApplication);
+ return;
+ }
+
+ m_client->disconnectFromEndpoint();
+ emit q->unregistered();
+}
+
+void QOpcUaGdsClientPrivate::setPkiConfiguration(const QOpcUaPkiConfiguration &pkiConfig)
+{
+ m_pkiConfig = pkiConfig;
+}
+
+const QOpcUaPkiConfiguration &QOpcUaGdsClientPrivate::pkiConfiguration() const
+{
+ return m_pkiConfig;
+}
+
+void QOpcUaGdsClientPrivate::setApplicationIdentity(const QOpcUaApplicationIdentity &appIdentity)
+{
+ m_appIdentitiy = appIdentity;
+}
+
+const QOpcUaApplicationIdentity &QOpcUaGdsClientPrivate::applicationIdentity() const
+{
+ return m_appIdentitiy;
+}
+
+void QOpcUaGdsClientPrivate::setApplicationRecord(const QOpcUaApplicationRecordDataType &appRecord)
+{
+ m_appRecord = appRecord;
+}
+
+const QOpcUaApplicationRecordDataType &QOpcUaGdsClientPrivate::applicationRecord() const
+{
+ return m_appRecord;
+}
+
+void QOpcUaGdsClientPrivate::setCertificateSigningRequestPresets(const QOpcUaX509DistinguishedName &dn, const QString &dns)
+{
+ csrPresets.dn = dn;
+ csrPresets.dns = dns;
+}
+
+const QOpcUaX509DistinguishedName &QOpcUaGdsClientPrivate::distinguishedNameCertificateSigningRequestPreset() const
+{
+ return csrPresets.dn;
+}
+
+const QString &QOpcUaGdsClientPrivate::dnsCertificateSigningRequestPreset() const
+{
+ return csrPresets.dns;
+}
+
+void QOpcUaGdsClientPrivate::setCertificateCheckInterval(int interval)
+{
+ m_certificateCheckTimer->setInterval(interval);
+}
+
+int QOpcUaGdsClientPrivate::certificateCheckInterval() const
+{
+ return m_certificateCheckTimer->interval();
+}
+
+void QOpcUaGdsClientPrivate::setTrustListUpdateInterval(int interval)
+{
+ m_trustListUpdateTimer->setInterval(interval);
+}
+
+int QOpcUaGdsClientPrivate::trustListUpdateInterval() const
+{
+ return m_trustListUpdateTimer->interval();
+}
+
+QOpcUaGdsClient::Error QOpcUaGdsClientPrivate::error() const
+{
+ return m_error;
+}
+
+QOpcUaGdsClient::State QOpcUaGdsClientPrivate::state() const
+{
+ return m_state;
+}
+
+QOpcUaX509CertificateSigningRequest QOpcUaGdsClientPrivate::createSigningRequest() const
+{
+ QOpcUaX509CertificateSigningRequest csr;
+ QOpcUaX509DistinguishedName dn = csrPresets.dn;
+ // Overwrite the application name because it has to match the identity
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CommonName, m_appIdentitiy.applicationName());
+ csr.setSubject(dn);
+
+ QOpcUaX509ExtensionSubjectAlternativeName *san = new QOpcUaX509ExtensionSubjectAlternativeName;
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::URI, m_appIdentitiy.applicationUri());
+ if (!csrPresets.dns.isEmpty())
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, csrPresets.dns);
+ san->setCritical(true);
+ csr.addExtension(san);
+
+ QOpcUaX509ExtensionBasicConstraints *bc = new QOpcUaX509ExtensionBasicConstraints;
+ bc->setCa(false);
+ bc->setCritical(true);
+ csr.addExtension(bc);
+
+ QOpcUaX509ExtensionKeyUsage *ku = new QOpcUaX509ExtensionKeyUsage;
+ ku->setCritical(true);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DigitalSignature);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::NonRepudiation);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DataEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CertificateSigning);
+ csr.addExtension(ku);
+
+ return csr;
+}
+
+void QOpcUaGdsClientPrivate::_q_handleDirectoryNodeMethodCallFinished(QString methodNodeId, QVariant result, QOpcUa::UaStatusCode statusCode)
+{
+ if (methodNodeId == m_directoryNodes[QLatin1String("UnregisterApplication")]) {
+ this->handleUnregisterApplicationFinished(result, statusCode);
+ } else if ( methodNodeId == m_directoryNodes[QLatin1String("FinishRequest")]) {
+ this->handleFinishRequestFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("StartSigningRequest")]) {
+ this->handleStartSigningRequestFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("GetCertificateStatus")]) {
+ this->handleGetCertificateStatusFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("GetCertificateGroups")]) {
+ this->handleGetCertificateGroupsFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("RegisterApplication")]) {
+ this->handleRegisterApplicationFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("FindApplications")]) {
+ this->handleFindApplicationsFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("GetApplication")]) {
+ this->handleGetApplicationFinished(result, statusCode);
+ } else if (methodNodeId == m_directoryNodes[QLatin1String("GetTrustList")]) {
+ this->handleGetTrustListFinished(result, statusCode);
+ } else {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Result unexpeced method call received" << methodNodeId;
+ }
+}
+
+void QOpcUaGdsClientPrivate::_q_certificateCheckTimeout()
+{
+ Q_Q(QOpcUaGdsClient);
+
+ QFile file(m_pkiConfig.clientCertificateFile());
+ if (!file.open(QFile::ReadOnly)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to load certificate from file" << m_pkiConfig.clientCertificateFile();
+ }
+
+ QSslCertificate cert(&file, QSsl::Der);
+ if (cert.isNull()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to load certificate from file" << m_pkiConfig.clientCertificateFile();
+ return;
+ }
+
+ bool needsUpdate = false;
+
+ if (cert.isSelfSigned()) {
+ needsUpdate = true;
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Certificate is self-signed: requesting update";
+ }
+
+ if (QDateTime::currentDateTime().addSecs(24*60*60) > cert.expiryDate()) {
+ needsUpdate = true;
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Certificate expiry date is due: requesting update";
+ }
+
+ if (!needsUpdate)
+ getCertificateStatus();
+
+ if (needsUpdate) {
+ emit q->certificateUpdateRequired();
+ startCertificateRequest();
+ }
+}
+
+void QOpcUaGdsClientPrivate::_q_updateTrustList()
+{
+ // OPC UA Specification Version 1.04 Part 4 Chapter 7.6.7 GetTrustList
+
+ if (!m_client || m_client->state() != QOpcUaClient::Connected) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No connection";
+ return;
+ }
+
+ if (!m_certificateGroupNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "No certificate group node";
+ return;
+ }
+
+ QVector<QOpcUa::TypedVariant> arguments;
+ arguments.push_back(QOpcUa::TypedVariant(m_appRecord.applicationId(), QOpcUa::NodeId));
+ arguments.push_back(QOpcUa::TypedVariant(m_certificateGroupNode->nodeId(), QOpcUa::NodeId));
+
+ if (!m_directoryNode->callMethod(m_directoryNodes[QLatin1String("GetTrustList")], arguments)) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Failed to call GetTrustList";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ }
+}
+
+void QOpcUaGdsClientPrivate::handleGetTrustListFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode)
+{
+ Q_Q(QOpcUaGdsClient);
+
+ if (statusCode != QOpcUa::Good) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Getting trust list node failed" << statusCode;
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ const auto trustListNodeId = result.toString();
+
+ if (trustListNodeId.isEmpty()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Trust list node id is empty";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificate);
+ return;
+ }
+
+ delete m_trustListNode;
+ m_trustListNode = m_client->node(trustListNodeId);
+
+ if (!m_trustListNode) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Trust list node failed";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+
+ QObject::connect(m_trustListNode, &QOpcUaNode::attributeUpdated, [this, q](QOpcUa::NodeAttribute attr, QVariant value) {
+ Q_UNUSED(value);
+ if (attr == QOpcUa::NodeAttribute::Value)
+ emit q->trustListUpdated();
+ });
+
+ if (!m_trustListNode->readValueAttribute()) {
+ qCWarning(QT_OPCUA_GDSCLIENT) << "Could not read trust list";
+ setError(QOpcUaGdsClient::Error::FailedToGetCertificateStatus);
+ return;
+ }
+}
+
+// When it is detected that authentication credentials are required this function
+// is used to re-connect to the server after requesting credentials
+void QOpcUaGdsClientPrivate::restartWithCredentials()
+{
+ Q_Q(QOpcUaGdsClient);
+
+ QOpcUaAuthenticationInformation authInfo;
+ emit q->authenticationRequired(authInfo);
+ m_client->setAuthenticationInformation(authInfo);
+
+ qCInfo(QT_OPCUA_GDSCLIENT) << "Restarting connection with credentials";
+ m_restartRequired = true;
+ m_client->disconnectFromEndpoint();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qopcuagdsclient.cpp"
diff --git a/src/opcua/client/qopcuagdsclient.h b/src/opcua/client/qopcuagdsclient.h
new file mode 100644
index 0000000..e4281d2
--- /dev/null
+++ b/src/opcua/client/qopcuagdsclient.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAGDSCLIENT_H
+#define QOPCUAGDSCLIENT_H
+
+#include <QtCore/QObject>
+#include <QtOpcUa/qopcuaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaApplicationIdentity;
+class QOpcUaAuthenticationInformation;
+class QOpcUaEndpointDescription;
+class QOpcUaPkiConfiguration;
+class QOpcUaGdsClientPrivate;
+class QOpcUaApplicationRecordDataType;
+class QOpcUaX509DistinguishedName;
+
+class Q_OPCUA_EXPORT QOpcUaGdsClient : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOpcUaGdsClient)
+
+public:
+ enum class Error {
+ NoError,
+ InvalidBackend,
+ InvalidEndpoint,
+ ConnectionError,
+ DirectoryNodeNotFound,
+ FailedToRegisterApplication,
+ FailedToUnregisterApplication,
+ FailedToGetCertificateStatus,
+ FailedToGetCertificate,
+ };
+ Q_ENUM(Error)
+
+ enum class State {
+ Idle,
+ BackendInstantiated,
+ Connecting,
+ Connected,
+ RegisteringApplication,
+ ApplicationRegistered,
+ //
+ Error,
+ };
+ Q_ENUM(State)
+
+ QOpcUaGdsClient(QObject *parent = nullptr);
+ virtual ~QOpcUaGdsClient();
+
+ void setBackend(const QString &backend);
+ const QString &backend() const;
+
+ void setEndpoint(const QOpcUaEndpointDescription &endpoint);
+ const QOpcUaEndpointDescription &endpoint() const;
+
+ void setPkiConfiguration(const QOpcUaPkiConfiguration &pkiConfig);
+ const QOpcUaPkiConfiguration &pkiConfiguration() const;
+
+ void setApplicationIdentity(const QOpcUaApplicationIdentity &appIdentity);
+ const QOpcUaApplicationIdentity &applicationIdentity() const;
+
+ void setApplicationRecord(const QOpcUaApplicationRecordDataType &appRecord);
+ const QOpcUaApplicationRecordDataType &applicationRecord() const;
+
+ QString applicationId() const;
+
+ void setCertificateSigningRequestPresets(const QOpcUaX509DistinguishedName &dn, const QString &dns);
+ const QOpcUaX509DistinguishedName &distinguishedNameCertificateSigningRequestPreset() const;
+ const QString &dnsCertificateSigningRequestPreset() const;
+
+ void setCertificateCheckInterval(int interval);
+ int certificateCheckInterval() const;
+
+ void setTrustListUpdateInterval(int interval);
+ int trustListUpdateInterval() const;
+
+ Error error() const;
+ State state() const;
+
+ void start();
+ void unregisterApplication();
+
+Q_SIGNALS:
+ void stateChanged(State);
+ void errorChanged(Error);
+ void applicationRegistered();
+ void certificateGroupsReceived(QStringList certificateGroups);
+ void certificateUpdateRequired();
+ void certificateUpdated();
+ void unregistered();
+ void trustListUpdated();
+ void authenticationRequired(QOpcUaAuthenticationInformation &authInfo);
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_handleDirectoryNodeMethodCallFinished(QString, QVariant, QOpcUa::UaStatusCode))
+ Q_PRIVATE_SLOT(d_func(), void _q_handleResolveBrowsePathFinished(QVector<QOpcUaBrowsePathTarget>, QVector<QOpcUaRelativePathElement>, QOpcUa::UaStatusCode))
+ Q_PRIVATE_SLOT(d_func(), void _q_certificateCheckTimeout())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateTrustList())
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAGDSCLIENT_H
+
diff --git a/src/opcua/client/qopcuagdsclient_p.h b/src/opcua/client/qopcuagdsclient_p.h
new file mode 100644
index 0000000..1a8155e
--- /dev/null
+++ b/src/opcua/client/qopcuagdsclient_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAGDSCLIENTPRIVATE_H
+#define QOPCUAGDSCLIENTPRIVATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QOpcUaClient>
+#include <QOpcUaAuthenticationInformation>
+#include <QOpcUaApplicationRecordDataType>
+#include <QOpcUaEndpointDescription>
+#include <QOpcUaPkiConfiguration>
+#include <private/qobject_p.h>
+#include "qopcuagdsclient.h"
+#include <QOpcUaX509CertificateSigningRequest>
+
+QT_BEGIN_NAMESPACE
+
+class QTimer;
+
+#define ApplicationRecordDataType_Encoding_DefaultBinary 134
+
+class QOpcUaGdsClientPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QOpcUaGdsClient)
+public:
+ QOpcUaGdsClientPrivate();
+ ~QOpcUaGdsClientPrivate();
+
+ void initializePrivateConnections();
+
+ void setEndpoint(const QOpcUaEndpointDescription &url);
+ const QOpcUaEndpointDescription &endpoint() const;
+
+ void setBackend(const QString &);
+ const QString &backend() const;
+
+ void setPkiConfiguration(const QOpcUaPkiConfiguration &pkiConfig);
+ const QOpcUaPkiConfiguration &pkiConfiguration() const;
+
+ void setApplicationIdentity(const QOpcUaApplicationIdentity &appIdentity);
+ const QOpcUaApplicationIdentity &applicationIdentity() const;
+
+ void setApplicationRecord(const QOpcUaApplicationRecordDataType &appIdentity);
+ const QOpcUaApplicationRecordDataType &applicationRecord() const;
+
+ void setCertificateSigningRequestPresets(const QOpcUaX509DistinguishedName &dn, const QString &dns);
+ const QOpcUaX509DistinguishedName &distinguishedNameCertificateSigningRequestPreset() const;
+ const QString &dnsCertificateSigningRequestPreset() const;
+
+ void setCertificateCheckInterval(int timeout);
+ int certificateCheckInterval() const;
+
+ void setTrustListUpdateInterval(int interval);
+ int trustListUpdateInterval() const;
+
+ void start();
+ void unregisterApplication();
+ QString applicationId() const;
+
+ QOpcUaGdsClient::Error error() const;
+ QOpcUaGdsClient::State state() const;
+
+
+private:
+ void setError(QOpcUaGdsClient::Error);
+ void setState(QOpcUaGdsClient::State);
+ void _q_handleDirectoryNodeMethodCallFinished(QString methodNodeId, QVariant result, QOpcUa::UaStatusCode statusCode);
+ void _q_handleResolveBrowsePathFinished(QVector<QOpcUaBrowsePathTarget> targets, QVector<QOpcUaRelativePathElement> path, QOpcUa::UaStatusCode statusCode);
+ void _q_certificateCheckTimeout();
+ void _q_updateTrustList();
+
+ void getApplication();
+ void registerApplication();
+ void resolveMethodNodes();
+ void resolveDirectoryNode();
+ void findRegisteredApplication();
+ void getCertificateGroups();
+ void resolveCertificateTypes();
+ void getCertificateTypes();
+ void getCertificateStatus();
+ void startCertificateRequest();
+ void finishCertificateRequest();
+ void localCertificateCheck();
+ void registrationDone();
+ void restartWithCredentials();
+ void handleUnregisterApplicationFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleFinishRequestFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleStartSigningRequestFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleGetCertificateStatusFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleGetCertificateGroupsFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleRegisterApplicationFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleFindApplicationsFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleGetApplicationFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ void handleGetTrustListFinished(const QVariant &result, QOpcUa::UaStatusCode statusCode);
+ QOpcUaX509CertificateSigningRequest createSigningRequest() const;
+
+ QOpcUaClient *m_client = nullptr;
+ QOpcUaEndpointDescription m_endpoint;
+ QOpcUaPkiConfiguration m_pkiConfig;
+ QOpcUaApplicationIdentity m_appIdentitiy;
+ QOpcUaApplicationRecordDataType m_appRecord;
+ QString m_backend;
+ QStringList m_certificateGroups;
+ QOpcUaGdsClient::Error m_error = QOpcUaGdsClient::Error::NoError;
+ QOpcUaGdsClient::State m_state = QOpcUaGdsClient::State::Idle;
+ QMap<QString, QString> m_directoryNodes;
+ QOpcUaNode *m_directoryNode = nullptr;
+ QOpcUaNode *m_certificateGroupNode = nullptr;
+ QString m_certificateTypesNodeId;
+ int m_gdsNamespaceIndex = -1;
+ QOpcUaNode *m_certificateTypesNode = nullptr;
+ QOpcUaNode *m_trustListNode = nullptr;
+ QTimer *m_certificateFinishTimer = nullptr;
+ QTimer *m_certificateCheckTimer = nullptr;
+ QString m_certificateRequestId;
+ QTimer *m_trustListUpdateTimer = nullptr;
+ bool m_restartRequired = false;
+ QString m_configFilePath;
+
+ struct {
+ QOpcUaX509DistinguishedName dn;
+ QString dns;
+ } csrPresets;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAGDSCLIENTPRIVATE_H
+
diff --git a/src/opcua/client/qopcuapkiconfiguration.cpp b/src/opcua/client/qopcuapkiconfiguration.cpp
index 746aa75..f5cc371 100644
--- a/src/opcua/client/qopcuapkiconfiguration.cpp
+++ b/src/opcua/client/qopcuapkiconfiguration.cpp
@@ -58,12 +58,12 @@ Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_SECURITY);
QOpcUaPkiConfiguration pkiConfig;
const QString pkiDir = QCoreApplication::applicationDirPath() + "/pki";
- pkiConfig.setClientCertificateDirectory(pkidir + "/own/certs/application.der");
- pkiConfig.setPrivateKeyDirectory(pkidir + "/own/private/application.pem");
- pkiConfig.setTrustListDirectory(pkidir + "/trusted/certs");
- pkiConfig.setRevocationListLocation(pkidir + "/trusted/crl");
- pkiConfig.setIssuerListDirectory(pkidir + "/issuers/certs");
- pkiConfig.setIssuerRevocationListDirectory(pkidir + "/issuers/crl");
+ pkiConfig.setClientCertificateFile(pkiDir + "/own/certs/application.der");
+ pkiConfig.setPrivateKeyFile(pkiDir + "/own/private/application.pem");
+ pkiConfig.setTrustListDirectory(pkiDir + "/trusted/certs");
+ pkiConfig.setRevocationListDirectory(pkiDir + "/trusted/crl");
+ pkiConfig.setIssuerListDirectory(pkiDir + "/issuers/certs");
+ pkiConfig.setIssuerRevocationListDirectory(pkiDir + "/issuers/crl");
client->setPkiConfiguration(pkiConfig);
\endcode
diff --git a/src/opcua/opcua.pro b/src/opcua/opcua.pro
index 52daeb8..a67ff03 100644
--- a/src/opcua/opcua.pro
+++ b/src/opcua/opcua.pro
@@ -1,9 +1,11 @@
TARGET = QtOpcUa
-QT += core-private network
+QT += core-private network network-private
QT -= gui
+QT_FOR_CONFIG += core-private
include(core/core.pri)
include(client/client.pri)
+qtConfig(ssl):!darwin:!winrt: include(x509/x509.pri)
MODULE_PLUGIN_TYPES = opcua
QMAKE_DOCS = $$PWD/doc/qtopcua.qdocconf
diff --git a/src/opcua/x509/openssl_symbols.cpp b/src/opcua/x509/openssl_symbols.cpp
new file mode 100644
index 0000000..b3207a4
--- /dev/null
+++ b/src/opcua/x509/openssl_symbols.cpp
@@ -0,0 +1,1335 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#include "openssl_symbols_p.h"
+#include <QtCore/qurl.h>
+
+#ifdef Q_OS_WIN
+# include <private/qsystemlibrary_p.h>
+#elif QT_CONFIG(library)
+# include <QtCore/qlibrary.h>
+#endif
+#include <QtCore/qmutex.h>
+#include <QtCore/qdatetime.h>
+#if defined(Q_OS_UNIX)
+#include <QtCore/qdir.h>
+#endif
+#include <QtCore/private/qmemory_p.h>
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
+#include <link.h>
+#endif
+#ifdef Q_OS_DARWIN
+#include "private/qcore_mac_p.h"
+#endif
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcSsl, "qt.opcua.ssl");
+
+
+/*
+ Note to maintainer:
+ -------------------
+
+ We load OpenSSL symbols dynamically. Because symbols are known to
+ disappear, and signatures sometimes change, between releases, we need to
+ be careful about how this is done. To ensure we don't end up dereferencing
+ null function pointers, and continue running even if certain functions are
+ missing, we define helper functions for each of the symbols we load from
+ OpenSSL, all prefixed with "q_" (declared in
+ qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect
+ directly, we call q_SSL_connect, which is a function that checks if the
+ actual SSL_connect fptr is null, and returns a failure if it is, or calls
+ SSL_connect if it isn't.
+
+ This requires a somewhat tedious process of declaring each function we
+ want to call in OpenSSL thrice: once with the q_, in _p.h, once using the
+ DEFINEFUNC macros below, and once in the function that actually resolves
+ the symbols, below the DEFINEFUNC declarations below.
+
+ There's one DEFINEFUNC macro declared for every number of arguments
+ exposed by OpenSSL (feel free to extend when needed). The easiest thing to
+ do is to find an existing entry that matches the arg count of the function
+ you want to import, and do the same.
+
+ The first macro arg is the function return type. The second is the
+ verbatim name of the function/symbol. Then follows a list of N pairs of
+ argument types with a variable name, and just the variable name (char *a,
+ a, char *b, b, etc). Finally there's two arguments - a suitable return
+ statement for the error case (for an int function, return 0 or return -1
+ is usually right). Then either just "return" or DUMMYARG, the latter being
+ for void functions.
+
+ Note: Take into account that these macros and declarations are processed
+ at compile-time, and the result depends on the OpenSSL headers the
+ compiling host has installed, but the symbols are resolved at run-time,
+ possibly with a different version of OpenSSL.
+*/
+
+#ifndef QT_LINKED_OPENSSL
+
+namespace {
+void qsslSocketUnresolvedSymbolWarning(const char *functionName)
+{
+ qCWarning(lcSsl, "QSslSocket: cannot call unresolved function %s", functionName);
+}
+
+#if QT_CONFIG(library)
+void qsslSocketCannotResolveSymbolWarning(const char *functionName)
+{
+ qCWarning(lcSsl, "QSslSocket: cannot resolve %s", functionName);
+}
+#endif
+
+}
+
+#endif // QT_LINKED_OPENSSL
+
+#if QT_CONFIG(opensslv11)
+
+// Below are the functions first introduced in version 1.1:
+
+DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return nullptr, return)
+DEFINEFUNC2(int, OPENSSL_init_ssl, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
+DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
+DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return)
+DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return)
+DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return)
+DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
+DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
+DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
+DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return)
+DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
+DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG)
+DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG)
+DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return)
+DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
+DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
+#ifdef TLS1_3_VERSION
+DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return)
+#endif
+DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return)
+DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return)
+DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
+DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op, return 0, return)
+
+DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
+DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
+DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
+DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
+DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
+DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return)
+DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG)
+DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return)
+DEFINEFUNC(const char *, OpenSSL_version, int a, a, return nullptr, return)
+DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return)
+DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG)
+DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return)
+
+DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
+DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
+DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
+DEFINEFUNC(int, BIO_get_shutdown, BIO *a, a, return -1, return)
+DEFINEFUNC2(void, BIO_set_shutdown, BIO *a, a, int shut, shut, return, DUMMYARG)
+
+
+#else // QT_CONFIG(opensslv11)
+
+// Functions below are either deprecated or removed in OpenSSL >= 1.1:
+
+DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return nullptr, return)
+
+#ifdef SSLEAY_MACROS
+DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return nullptr, return)
+#endif
+DEFINEFUNC2(BIO *, BIO_new_file, const char *filename, filename, const char *mode, mode, return nullptr, return)
+DEFINEFUNC(void, ERR_clear_error, DUMMYARG, DUMMYARG, return, DUMMYARG)
+DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return nullptr, return)
+DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
+DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG)
+DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG)
+DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG)
+DEFINEFUNC(unsigned long, ERR_peek_last_error, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(void, ERR_free_strings, void, DUMMYARG, return, DUMMYARG)
+DEFINEFUNC(void, EVP_CIPHER_CTX_cleanup, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC(void, EVP_CIPHER_CTX_init, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
+
+#ifdef SSLEAY_MACROS
+DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return nullptr, return)
+DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return nullptr, return)
+#endif // SSLEAY_MACROS
+
+DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return)
+DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
+
+DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG)
+DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG)
+DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return nullptr, return)
+
+DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return)
+DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG)
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L
+
+DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get_chain, X509_STORE_CTX *a, a, return nullptr, return)
+
+#ifdef SSLEAY_MACROS
+DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return)
+DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC2(int, i2d_ECPrivateKey, const EC_KEY *a, a, unsigned char **b, b, return -1, return)
+#endif
+DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return nullptr, return)
+DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return nullptr, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return nullptr, return)
+#endif
+#endif
+
+DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG)
+DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG)
+DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return)
+DEFINEFUNC(const char *, SSLeay_version, int a, a, return nullptr, return)
+DEFINEFUNC(void, ERR_load_crypto_strings, void, DUMMYARG, return, return)
+
+#endif // QT_CONFIG(opensslv11)
+
+DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return)
+DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return)
+DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return)
+DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return)
+DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
+DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return)
+DEFINEFUNC(void, BIO_free_all, BIO *a, a, return, return)
+DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return nullptr, return)
+DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
+
+DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
+DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
+DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return)
+DEFINEFUNC2(int, BN_set_word, const BIGNUM *a, a, BN_ULONG w, w, return 0, return)
+DEFINEFUNC(BIGNUM *, BN_new, void, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, BN_clear, BIGNUM *bignum, bignum, return, return)
+DEFINEFUNC(void, BN_free, BIGNUM *bignum, bignum, return, return)
+DEFINEFUNC(void, BN_clear_free, BIGNUM *bignum, bignum, return, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return nullptr, return)
+DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
+#endif
+DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
+DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return nullptr, return)
+DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return nullptr, return)
+DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG)
+DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, EVP_CIPHER_CTX_free, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return)
+DEFINEFUNC2(int, EVP_CIPHER_CTX_set_key_length, EVP_CIPHER_CTX *ctx, ctx, int keylen, keylen, return 0, return)
+DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *type, type, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
+DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
+DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return)
+DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return)
+DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return)
+DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_MD *, EVP_sha256, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_256_gcm, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
+#endif
+DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
+DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(DH *, EVP_PKEY_get1_DH, EVP_PKEY *a, a, return nullptr, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return)
+#endif
+DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return)
+DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return)
+DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return nullptr, return)
+DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return nullptr, return)
+DEFINEFUNC(int, OBJ_sn2nid, const char *s, s, return 0, return)
+DEFINEFUNC(int, OBJ_ln2nid, const char *s, s, return 0, return)
+DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return)
+DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, return -1, return)
+
+DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return)
+DEFINEFUNC4(X509_EXTENSION* , X509V3_EXT_conf_nid, LHASH_OF(CONF_VALUE) *conf, conf, X509V3_CTX *ctx, ctx, int ext_nid, ext_nid, char *value, value, return NULL, return)
+
+#ifndef SSLEAY_MACROS
+DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+#endif
+DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_PKCS8PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+#endif
+#endif // !SSLEAY_MACROS
+DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+#endif
+DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
+DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
+DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return)
+#endif
+DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
+DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
+DEFINEFUNC2(int, RAND_bytes, unsigned char *b, b, int n, n, return 0, return)
+DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC4(int, RSA_generate_key_ex, RSA *rsa, rsa, int bits, bits, BIGNUM *e, e, BN_GENCB *cb, cb, return 0, return)
+DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
+DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return nullptr, return)
+DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return)
+DEFINEFUNC4(int, X509_digest, const X509 *x509, x509, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return -1, return)
+#ifndef SSLEAY_MACROS
+DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return)
+#endif
+DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG);
+DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
+//Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return)
+DEFINEFUNC(void, ASN1_TIME_free, ASN1_TIME *t, t, return, DUMMYARG)
+DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return)
+DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
+DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
+DEFINEFUNC(const X509V3_EXT_METHOD *, X509V3_EXT_get, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void *, X509V3_EXT_d2i, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(int, X509_EXTENSION_get_critical, X509_EXTENSION *a, a, return 0, return)
+DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void, BASIC_CONSTRAINTS_free, BASIC_CONSTRAINTS *a, a, return, DUMMYARG)
+DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG)
+DEFINEFUNC(void, GENERAL_NAME_free, GENERAL_NAME *a, a, return, DUMMYARG)
+DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, const ASN1_STRING *b, b, return 0, return)
+DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return)
+DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return)
+DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return nullptr, return)
+DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return)
+DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return)
+DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return nullptr, return)
+DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return nullptr, return)
+DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return nullptr, return)
+DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG)
+DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return)
+DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return)
+DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return)
+DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return)
+DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return)
+DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return)
+DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return)
+DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return)
+DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
+DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return)
+DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
+DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen,
+ const unsigned char *in, in, unsigned int inlen, inlen,
+ const unsigned char *client, client, unsigned int client_len, client_len,
+ return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
+ const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+DEFINEFUNC3(int, SSL_set_alpn_protos, SSL *s, s, const unsigned char *protos, protos,
+ unsigned protos_len, protos_len, return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_alpn_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char **data, data,
+ unsigned *len, len, return, DUMMYARG)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
+DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
+DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
+DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
+DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return)
+
+DEFINEFUNC3(void *, CRYPTO_malloc, size_t num, num, const char *file, file, int line, line, return nullptr, return)
+DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
+DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
+DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return)
+DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return)
+DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return)
+DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return)
+DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
+DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return)
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif // OPENSSL_NO_EC
+
+DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \
+ X509 **cert, cert, STACK_OF(X509) **ca, ca, return 1, return);
+DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return nullptr, return);
+DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG)
+
+DEFINEFUNC(X509_REQ*, X509_REQ_new, void, DUMMYARG, return NULL, return);
+DEFINEFUNC(void, X509_REQ_free, X509_REQ *req, req, return, return);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+DEFINEFUNC(X509_NAME*, X509_REQ_get_subject_name, X509_REQ *req, req, return NULL, return);
+DEFINEFUNC2(EVP_PKEY_CTX*, EVP_PKEY_CTX_new_id, int id, id, ENGINE *e, e, return NULL, return);
+DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return);
+DEFINEFUNC(int, EVP_PKEY_keygen_init, EVP_PKEY_CTX *ctx, ctx, return 0, return);
+DEFINEFUNC2(int, EVP_PKEY_keygen, EVP_PKEY_CTX *ctx, ctx, EVP_PKEY **pkey, pkey, return 0, return);
+DEFINEFUNC5(int, RSA_pkey_ctx_ctrl, EVP_PKEY_CTX *ctx, ctx, int optype, optype, int cmd, cmd, int p1, p1, void *p2, p2, return 0, return)
+
+#endif
+DEFINEFUNC7(int, X509_NAME_add_entry_by_txt, X509_NAME *name, name, const char *field, field, int type, type, const unsigned char *bytes, bytes, int len, len, int loc, loc, int set, set, return 0, return);
+DEFINEFUNC7(int, X509_NAME_add_entry_by_OBJ, X509_NAME *name, name, const ASN1_OBJECT *obj, obj, int type, type, const unsigned char *bytes, bytes, int len, len, int loc, loc, int set, set, return 0, return);
+DEFINEFUNC2(ASN1_OBJECT *, OBJ_txt2obj, const char *s, s, int no_name, no_name, return NULL, return);
+DEFINEFUNC2(int, X509_REQ_set_pubkey, X509_REQ *x, x, EVP_PKEY *pkey, pkey, return 0, return);
+DEFINEFUNC3(int, X509_REQ_sign, X509_REQ *x, x, EVP_PKEY *pkey, pkey, const EVP_MD *md, md, return 0, return);
+DEFINEFUNC2(int, PEM_write_bio_X509_REQ, BIO *bp, bp, X509_REQ *x, x, return 0, return);
+DEFINEFUNC2(int, PEM_write_bio_X509_REQ_NEW, BIO *bp, bp, X509_REQ *x, x, return 0, return);
+DEFINEFUNC2(int, X509_REQ_set_version, X509_REQ *x, x, long version, version, return 0, return);
+DEFINEFUNC2(int, X509_REQ_add_extensions, X509_REQ *req, req, STACK_OF(X509_EXTENSION) *exts, exts, return 0, return)
+DEFINEFUNC(void, X509_EXTENSION_free, X509_EXTENSION *ext, ext, return, return)
+DEFINEFUNC2(int, X509_EXTENSION_set_critical, X509_EXTENSION *ex, ex, int crit, crit, return 0, return)
+DEFINEFUNC3(X509*, X509_REQ_to_X509, X509_REQ *r, r, int days, days, EVP_PKEY *pkey, pkey, return NULL, return)
+DEFINEFUNC2(int, PEM_write_bio_X509, BIO *bp, bp, X509 *x, x, return 0, return)
+DEFINEFUNC(X509*, X509_new, void, DUMMYARG, return NULL, return)
+DEFINEFUNC2(int, ASN1_INTEGER_set, ASN1_INTEGER *a, a, long v, v, return 0, return)
+DEFINEFUNC2(int, X509_set_pubkey,X509 *x, x, EVP_PKEY *key, key, return 0, return)
+DEFINEFUNC2(int, X509_set_issuer_name, X509 *x, x, X509_NAME *name, name, return 0, return)
+DEFINEFUNC3(int, X509_sign, X509 *x, x, EVP_PKEY *key, key, const EVP_MD *md, md, return 0, return)
+DEFINEFUNC3(int, X509_add_ext, X509 *x, x, X509_EXTENSION *ex, ex, int loc, loc, return 0, return)
+DEFINEFUNC2(int, X509_set_version, X509 *x, x, long version, version, return 0, return)
+DEFINEFUNC2(int, X509_set_subject_name, X509 *x, x, X509_NAME *name, name, return 0, return)
+DEFINEFUNC(ASN1_OCTET_STRING *, ASN1_OCTET_STRING_new, void, DUMMYARG, return NULL, return)
+DEFINEFUNC4(int, X509_pubkey_digest, const X509 *data, data, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return 0, return)
+DEFINEFUNC3(int, ASN1_OCTET_STRING_set, ASN1_OCTET_STRING *str, str, const unsigned char *data, data, int len, len, return 0, return)
+DEFINEFUNC5(int, X509_add1_ext_i2d, X509 *x, x, int nid, nid, void *value, value, int crit, crit, unsigned long flags, flags, return 0, return)
+DEFINEFUNC(void, ASN1_OCTET_STRING_free, ASN1_OCTET_STRING *a, a, return, return)
+DEFINEFUNC(ASN1_INTEGER *, ASN1_INTEGER_new, void, DUMMYARG, return NULL, return)
+DEFINEFUNC(GENERAL_NAMES *, GENERAL_NAMES_new, void, DUMMYARG, return NULL, return)
+DEFINEFUNC(GENERAL_NAME *, GENERAL_NAME_new, void, DUMMYARG, return NULL, return)
+DEFINEFUNC(X509_NAME *, X509_NAME_dup, X509_NAME *xn, xn, return NULL, return)
+DEFINEFUNC2(int, X509_set_serialNumber, X509 *x, x, ASN1_INTEGER *serial, serial, return 0, return)
+DEFINEFUNC(AUTHORITY_KEYID *, AUTHORITY_KEYID_new, void, DUMMYARG, return NULL, return)
+DEFINEFUNC(ASN1_INTEGER *, ASN1_INTEGER_dup, const ASN1_INTEGER *x, x, return NULL, return)
+DEFINEFUNC4(int, X509_NAME_digest, const X509_NAME *data, data, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return 0, return)
+DEFINEFUNC(void, ASN1_INTEGER_free, ASN1_INTEGER *a, a, return, return)
+DEFINEFUNC2(int, i2d_X509_REQ_bio, BIO *bp, bp, X509_REQ *req, req, return 0, return)
+DEFINEFUNC2(int, i2d_X509_bio, BIO *bp, bp, X509 *x509, x509, return 0, return)
+
+#define RESOLVEFUNC(func) \
+ if (!(_q_##func = _q_PTR_##func(libs.ssl->resolve(#func))) \
+ && !(_q_##func = _q_PTR_##func(libs.crypto->resolve(#func)))) \
+ qsslSocketCannotResolveSymbolWarning(#func);
+
+#if !defined QT_LINKED_OPENSSL
+
+#if !QT_CONFIG(library)
+bool q_resolveOpenSslSymbols()
+{
+ qCWarning(lcSsl, "QSslSocket: unable to resolve symbols. Qt is configured without the "
+ "'library' feature, which means runtime resolving of libraries won't work.");
+ qCWarning(lcSsl, "Either compile Qt statically or with support for runtime resolving "
+ "of libraries.");
+ return false;
+}
+#else
+
+# ifdef Q_OS_UNIX
+struct NumericallyLess
+{
+ typedef bool result_type;
+ result_type operator()(const QStringRef &lhs, const QStringRef &rhs) const
+ {
+ bool ok = false;
+ int b = 0;
+ int a = lhs.toInt(&ok);
+ if (ok)
+ b = rhs.toInt(&ok);
+ if (ok) {
+ // both toInt succeeded
+ return a < b;
+ } else {
+ // compare as strings;
+ return lhs < rhs;
+ }
+ }
+};
+
+struct LibGreaterThan
+{
+ typedef bool result_type;
+ result_type operator()(const QString &lhs, const QString &rhs) const
+ {
+ const QVector<QStringRef> lhsparts = lhs.splitRef(QLatin1Char('.'));
+ const QVector<QStringRef> rhsparts = rhs.splitRef(QLatin1Char('.'));
+ Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1);
+
+ // note: checking rhs < lhs, the same as lhs > rhs
+ return std::lexicographical_compare(rhsparts.begin() + 1, rhsparts.end(),
+ lhsparts.begin() + 1, lhsparts.end(),
+ NumericallyLess());
+ }
+};
+
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
+static int dlIterateCallback(struct dl_phdr_info *info, size_t size, void *data)
+{
+ if (size < sizeof (info->dlpi_addr) + sizeof (info->dlpi_name))
+ return 1;
+ QSet<QString> *paths = (QSet<QString> *)data;
+ QString path = QString::fromLocal8Bit(info->dlpi_name);
+ if (!path.isEmpty()) {
+ QFileInfo fi(path);
+ path = fi.absolutePath();
+ if (!path.isEmpty())
+ paths->insert(path);
+ }
+ return 0;
+}
+#endif
+
+static QStringList libraryPathList()
+{
+ QStringList paths;
+# ifdef Q_OS_DARWIN
+ paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH"))
+ .split(QLatin1Char(':'), QString::SkipEmptyParts);
+
+ // search in .app/Contents/Frameworks
+ UInt32 packageType;
+ CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, nullptr);
+ if (packageType == FOUR_CHAR_CODE('APPL')) {
+ QUrl bundleUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyBundleURL(CFBundleGetMainBundle())));
+ QUrl frameworksUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyPrivateFrameworksURL(CFBundleGetMainBundle())));
+ paths << bundleUrl.resolved(frameworksUrl).path();
+ }
+# else
+ paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH"))
+ .split(QLatin1Char(':'), QString::SkipEmptyParts);
+# endif
+ paths << QLatin1String("/lib") << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib");
+ paths << QLatin1String("/lib64") << QLatin1String("/usr/lib64") << QLatin1String("/usr/local/lib64");
+ paths << QLatin1String("/lib32") << QLatin1String("/usr/lib32") << QLatin1String("/usr/local/lib32");
+
+#if defined(Q_OS_ANDROID)
+ paths << QLatin1String("/system/lib");
+#elif defined(Q_OS_LINUX)
+ // discover paths of already loaded libraries
+ QSet<QString> loadedPaths;
+ dl_iterate_phdr(dlIterateCallback, &loadedPaths);
+ paths.append(loadedPaths.values());
+#endif
+
+ return paths;
+}
+
+Q_NEVER_INLINE
+static QStringList findAllLibs(QLatin1String filter)
+{
+ const QStringList paths = libraryPathList();
+ QStringList found;
+ const QStringList filters((QString(filter)));
+
+ for (const QString &path : paths) {
+ QDir dir(path);
+ QStringList entryList = dir.entryList(filters, QDir::Files);
+
+ std::sort(entryList.begin(), entryList.end(), LibGreaterThan());
+ for (const QString &entry : qAsConst(entryList))
+ found << path + QLatin1Char('/') + entry;
+ }
+
+ return found;
+}
+
+static QStringList findAllLibSsl()
+{
+ return findAllLibs(QLatin1String("libssl.*"));
+}
+
+static QStringList findAllLibCrypto()
+{
+ return findAllLibs(QLatin1String("libcrypto.*"));
+}
+# endif
+
+#ifdef Q_OS_WIN
+
+struct LoadedOpenSsl {
+ std::unique_ptr<QSystemLibrary> ssl, crypto;
+};
+
+static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, LoadedOpenSsl &result)
+{
+ auto ssleay32 = qt_make_unique<QSystemLibrary>(ssleay32LibName);
+ if (!ssleay32->load(false)) {
+ return FALSE;
+ }
+
+ auto libeay32 = qt_make_unique<QSystemLibrary>(libeay32LibName);
+ if (!libeay32->load(false)) {
+ return FALSE;
+ }
+
+ result.ssl = std::move(ssleay32);
+ result.crypto = std::move(libeay32);
+ return TRUE;
+}
+
+static LoadedOpenSsl loadOpenSsl()
+{
+ LoadedOpenSsl result;
+
+#if QT_CONFIG(opensslv11)
+ // With OpenSSL 1.1 the names have changed to libssl-1_1(-x64) and libcrypto-1_1(-x64), for builds using
+ // MSVC and GCC, (-x64 suffix for 64-bit builds).
+
+#ifdef Q_PROCESSOR_X86_64
+#define QT_SSL_SUFFIX "-x64"
+#else // !Q_PROCESSOFR_X86_64
+#define QT_SSL_SUFFIX
+#endif // !Q_PROCESSOR_x86_64
+
+ tryToLoadOpenSslWin32Library(QLatin1String("libssl-1_1" QT_SSL_SUFFIX),
+ QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), result);
+
+#undef QT_SSL_SUFFIX
+
+#else // QT_CONFIG(opensslv11)
+
+ // When OpenSSL is built using MSVC then the libraries are named 'ssleay32.dll' and 'libeay32'dll'.
+ // When OpenSSL is built using GCC then different library names are used (depending on the OpenSSL version)
+ // The oldest version of a GCC-based OpenSSL which can be detected by the code below is 0.9.8g (released in 2007)
+ if (!tryToLoadOpenSslWin32Library(QLatin1String("ssleay32"), QLatin1String("libeay32"), result)) {
+ if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-10"), QLatin1String("libcrypto-10"), result)) {
+ if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-8"), QLatin1String("libcrypto-8"), result)) {
+ tryToLoadOpenSslWin32Library(QLatin1String("libssl-7"), QLatin1String("libcrypto-7"), result);
+ }
+ }
+ }
+#endif // !QT_CONFIG(opensslv11)
+
+ return result;
+}
+#else
+
+struct LoadedOpenSsl {
+ std::unique_ptr<QLibrary> ssl, crypto;
+};
+
+static LoadedOpenSsl loadOpenSsl()
+{
+ LoadedOpenSsl result = {qt_make_unique<QLibrary>(), qt_make_unique<QLibrary>()};
+
+# if defined(Q_OS_UNIX)
+ QLibrary * const libssl = result.ssl.get();
+ QLibrary * const libcrypto = result.crypto.get();
+
+ // Try to find the libssl library on the system.
+ //
+ // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that
+ // is, libssl.so on most Unix systems. However, the .so file isn't present in
+ // user installations because it's considered a development file.
+ //
+ // The right thing to do is to load the library at the major version we know how
+ // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h)
+ //
+ // However, OpenSSL is a well-known case of binary-compatibility breakage. To
+ // avoid such problems, many system integrators and Linux distributions change
+ // the soname of the binary, letting the full version number be the soname. So
+ // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that
+ // reason, we will search a few common paths (see findAllLibSsl() above) in hopes
+ // we find one that works.
+ //
+ // If that fails, for OpenSSL 1.0 we also try some fallbacks -- look up
+ // libssl.so with a hardcoded soname. The reason is QTBUG-68156: the binary
+ // builds of Qt happen (at the time of this writing) on RHEL machines,
+ // which change SHLIB_VERSION_NUMBER to a non-portable string. When running
+ // those binaries on the target systems, this code won't pick up
+ // libssl.so.MODIFIED_SHLIB_VERSION_NUMBER because it doesn't exist there.
+ // Given that the only 1.0 supported release (at the time of this writing)
+ // is 1.0.2, with soname "1.0.0", give that a try too. Note that we mandate
+ // OpenSSL >= 1.0.0 with a configure-time check, and OpenSSL has kept binary
+ // compatibility between 1.0.0 and 1.0.2.
+ //
+ // It is important, however, to try the canonical name and the unversioned name
+ // without going through the loop. By not specifying a path, we let the system
+ // dlopen(3) function determine it for us. This will include any DT_RUNPATH or
+ // DT_RPATH tags on our library header as well as other system-specific search
+ // paths. See the man page for dlopen(3) on your system for more information.
+
+#ifdef Q_OS_OPENBSD
+ libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint);
+#endif
+#if defined(SHLIB_VERSION_NUMBER) && !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so
+ // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER>
+ libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER));
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER));
+ if (libcrypto->load() && libssl->load()) {
+ // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found
+ return result;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+
+#if !QT_CONFIG(opensslv11)
+ // first-and-half attempts: for OpenSSL 1.0 try to load some hardcoded sonames:
+ // - "1.0.0" is the official upstream one
+ // - "1.0.2" is found on some distributions (e.g. Debian) that patch OpenSSL
+ static const QLatin1String fallbackSonames[] = {
+ QLatin1String("1.0.0"),
+ QLatin1String("1.0.2")
+ };
+
+ for (auto fallbackSoname : fallbackSonames) {
+ libssl->setFileNameAndVersion(QLatin1String("ssl"), fallbackSoname);
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto"), fallbackSoname);
+ if (libcrypto->load() && libssl->load()) {
+ return result;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+ }
+#endif
+#endif
+
+#ifndef Q_OS_DARWIN
+ // second attempt: find the development files libssl.so and libcrypto.so
+ //
+ // disabled on macOS/iOS:
+ // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third
+ // attempt, _after_ <bundle>/Contents/Frameworks has been searched.
+ // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place.
+# if defined(Q_OS_ANDROID)
+ // OpenSSL 1.1.x must be suffixed otherwise it will use the system libcrypto.so libssl.so which on API-21 are OpenSSL 1.0 not 1.1
+ auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) {
+ auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX");
+ if (suffix.isEmpty())
+ return defaultSuffix;
+ return suffix;
+ };
+# if QT_CONFIG(opensslv11)
+ static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1"));
+# else
+ static QString suffix = QString::fromLatin1(openSSLSuffix());
+# endif
+ libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1);
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1);
+# else
+ libssl->setFileNameAndVersion(QLatin1String("ssl"), -1);
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1);
+# endif
+ if (libcrypto->load() && libssl->load()) {
+ // libssl.so.0 and libcrypto.so.0 found
+ return result;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+#endif
+
+ // third attempt: loop on the most common library paths and find libssl
+ const QStringList sslList = findAllLibSsl();
+ const QStringList cryptoList = findAllLibCrypto();
+
+ for (const QString &crypto : cryptoList) {
+ libcrypto->setFileNameAndVersion(crypto, -1);
+ if (libcrypto->load()) {
+ QFileInfo fi(crypto);
+ QString version = fi.completeSuffix();
+
+ for (const QString &ssl : sslList) {
+ if (!ssl.endsWith(version))
+ continue;
+
+ libssl->setFileNameAndVersion(ssl, -1);
+
+ if (libssl->load()) {
+ // libssl.so.x and libcrypto.so.x found
+ return result;
+ } else {
+ libssl->unload();
+ }
+ }
+ }
+ libcrypto->unload();
+ }
+
+ // failed to load anything
+ result = {};
+ return result;
+
+# else
+ // not implemented for this platform yet
+ return result;
+# endif
+}
+#endif
+
+static QBasicMutex symbolResolveMutex;
+static QBasicAtomicInt symbolsResolved = Q_BASIC_ATOMIC_INITIALIZER(false);
+static bool triedToResolveSymbols = false;
+
+bool q_resolveOpenSslSymbols()
+{
+ if (symbolsResolved.loadAcquire())
+ return true;
+ QMutexLocker locker(&symbolResolveMutex);
+ if (symbolsResolved.loadRelaxed())
+ return true;
+ if (triedToResolveSymbols)
+ return false;
+ triedToResolveSymbols = true;
+
+ LoadedOpenSsl libs = loadOpenSsl();
+ if (!libs.ssl || !libs.crypto)
+ // failed to load them
+ return false;
+
+#if QT_CONFIG(opensslv11)
+ RESOLVEFUNC(X509_REQ_get_subject_name) // v1.1.0
+ RESOLVEFUNC(OPENSSL_init_ssl)
+ RESOLVEFUNC(OPENSSL_init_crypto)
+ RESOLVEFUNC(ASN1_STRING_get0_data)
+ RESOLVEFUNC(EVP_CIPHER_CTX_reset)
+ RESOLVEFUNC(EVP_PKEY_base_id)
+ RESOLVEFUNC(RSA_bits)
+ RESOLVEFUNC(OPENSSL_sk_new_null)
+ RESOLVEFUNC(OPENSSL_sk_push)
+ RESOLVEFUNC(OPENSSL_sk_free)
+ RESOLVEFUNC(OPENSSL_sk_num)
+ RESOLVEFUNC(OPENSSL_sk_pop_free)
+ RESOLVEFUNC(OPENSSL_sk_value)
+ RESOLVEFUNC(DH_get0_pqg)
+ RESOLVEFUNC(CRYPTO_get_ex_new_index)
+ RESOLVEFUNC(X509_up_ref)
+ RESOLVEFUNC(X509_STORE_CTX_get0_chain)
+ RESOLVEFUNC(X509_getm_notBefore)
+ RESOLVEFUNC(X509_getm_notAfter)
+ RESOLVEFUNC(X509_get_version)
+ RESOLVEFUNC(X509_get_pubkey)
+ RESOLVEFUNC(X509_STORE_set_verify_cb)
+ RESOLVEFUNC(CRYPTO_free)
+ RESOLVEFUNC(OpenSSL_version_num)
+ RESOLVEFUNC(OpenSSL_version)
+ if (!_q_OpenSSL_version) {
+ // Apparently, we were built with OpenSSL 1.1 enabled but are now using
+ // a wrong library.
+ qCWarning(lcSsl, "Incompatible version of OpenSSL");
+ return false;
+ }
+
+ RESOLVEFUNC(DH_bits)
+
+ RESOLVEFUNC(BIO_set_data)
+ RESOLVEFUNC(BIO_get_data)
+ RESOLVEFUNC(BIO_set_init)
+ RESOLVEFUNC(BIO_get_shutdown)
+ RESOLVEFUNC(BIO_set_shutdown)
+ RESOLVEFUNC(EVP_PKEY_CTX_new_id)
+ RESOLVEFUNC(EVP_PKEY_CTX_free)
+ RESOLVEFUNC(EVP_PKEY_keygen_init)
+ RESOLVEFUNC(EVP_PKEY_keygen);
+ RESOLVEFUNC(RSA_pkey_ctx_ctrl);
+ RESOLVEFUNC(BIO_free_all)
+ RESOLVEFUNC(X509_REQ_new)
+ RESOLVEFUNC(X509_REQ_set_version)
+ RESOLVEFUNC(OBJ_txt2obj)
+ RESOLVEFUNC(X509_NAME_add_entry_by_OBJ)
+ RESOLVEFUNC(X509_REQ_add_extensions)
+ RESOLVEFUNC(X509_EXTENSION_free)
+ RESOLVEFUNC(X509_REQ_set_pubkey)
+ RESOLVEFUNC(X509_REQ_sign)
+ RESOLVEFUNC(PEM_write_bio_X509_REQ)
+ RESOLVEFUNC(X509_REQ_free)
+
+#else // !opensslv11
+
+ RESOLVEFUNC(ASN1_STRING_data)
+
+#ifdef SSLEAY_MACROS
+ RESOLVEFUNC(ASN1_dup)
+#endif // SSLEAY_MACROS
+ RESOLVEFUNC(BIO_new_file)
+ RESOLVEFUNC(ERR_clear_error)
+ RESOLVEFUNC(ERR_load_crypto_strings)
+ RESOLVEFUNC(CRYPTO_free)
+ RESOLVEFUNC(CRYPTO_num_locks)
+ RESOLVEFUNC(CRYPTO_set_id_callback)
+ RESOLVEFUNC(CRYPTO_set_locking_callback)
+ RESOLVEFUNC(ERR_peek_last_error)
+ RESOLVEFUNC(ERR_free_strings)
+ RESOLVEFUNC(EVP_CIPHER_CTX_cleanup)
+ RESOLVEFUNC(EVP_CIPHER_CTX_init)
+
+#ifdef SSLEAY_MACROS // ### verify
+ RESOLVEFUNC(PEM_ASN1_read_bio)
+#endif // SSLEAY_MACROS
+
+ RESOLVEFUNC(sk_new_null)
+ RESOLVEFUNC(sk_push)
+ RESOLVEFUNC(sk_free)
+ RESOLVEFUNC(sk_num)
+ RESOLVEFUNC(sk_pop_free)
+ RESOLVEFUNC(sk_value)
+ RESOLVEFUNC(X509_STORE_CTX_get_chain)
+#ifdef SSLEAY_MACROS
+ RESOLVEFUNC(i2d_RSAPrivateKey)
+ RESOLVEFUNC(d2i_RSAPrivateKey)
+#endif
+
+ RESOLVEFUNC(CONF_get1_default_config_file)
+ RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf)
+ RESOLVEFUNC(OPENSSL_add_all_algorithms_conf)
+ RESOLVEFUNC(SSLeay)
+ RESOLVEFUNC(X509_REQ_new)
+ RESOLVEFUNC(X509_REQ_free)
+ RESOLVEFUNC(X509_NAME_add_entry_by_txt)
+ RESOLVEFUNC(X509_NAME_add_entry_by_OBJ)
+ RESOLVEFUNC(OBJ_txt2obj)
+ RESOLVEFUNC(X509_REQ_add_extensions)
+
+ RESOLVEFUNC(X509_REQ_set_pubkey)
+ RESOLVEFUNC(X509_REQ_sign)
+ RESOLVEFUNC(PEM_write_bio_X509_REQ)
+ RESOLVEFUNC(PEM_write_bio_X509_REQ_NEW)
+ RESOLVEFUNC(X509_REQ_set_version)
+ RESOLVEFUNC(BIO_free_all)
+ RESOLVEFUNC(X509_EXTENSION_free)
+
+ if (!_q_SSLeay || q_SSLeay() >= 0x10100000L) {
+ // OpenSSL 1.1 has deprecated and removed SSLeay. We consider a failure to
+ // resolve this symbol as a failure to resolve symbols.
+ // The right operand of '||' above is ... a bit of paranoia.
+ qCWarning(lcSsl, "Incompatible version of OpenSSL");
+ return false;
+ }
+
+
+ RESOLVEFUNC(SSLeay_version)
+
+#ifndef OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (q_SSLeay() >= 0x10002000L)
+ RESOLVEFUNC(EC_curve_nist2nid)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif // OPENSSL_NO_EC
+
+
+#endif // !opensslv11
+
+ RESOLVEFUNC(ASN1_INTEGER_get)
+ RESOLVEFUNC(ASN1_INTEGER_cmp)
+ RESOLVEFUNC(ASN1_STRING_length)
+ RESOLVEFUNC(ASN1_STRING_to_UTF8)
+ RESOLVEFUNC(BIO_ctrl)
+ RESOLVEFUNC(BIO_free)
+ RESOLVEFUNC(BIO_new)
+ RESOLVEFUNC(BIO_new_mem_buf)
+ RESOLVEFUNC(BIO_read)
+ RESOLVEFUNC(BIO_s_mem)
+ RESOLVEFUNC(BIO_write)
+ RESOLVEFUNC(BIO_set_flags)
+ RESOLVEFUNC(BIO_clear_flags)
+ RESOLVEFUNC(BIO_set_ex_data)
+ RESOLVEFUNC(BIO_get_ex_data)
+ RESOLVEFUNC(X509V3_EXT_conf_nid)
+ RESOLVEFUNC(X509_EXTENSION_set_critical)
+
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EC_KEY_get0_group)
+ RESOLVEFUNC(EC_GROUP_get_degree)
+#endif
+ RESOLVEFUNC(BN_num_bits)
+#if QT_CONFIG(opensslv11)
+ RESOLVEFUNC(BN_is_word)
+#endif
+ RESOLVEFUNC(BN_mod_word)
+ RESOLVEFUNC(BN_set_word)
+ RESOLVEFUNC(BN_new)
+ RESOLVEFUNC(BN_free)
+ RESOLVEFUNC(BN_clear)
+ RESOLVEFUNC(BN_clear_free)
+ RESOLVEFUNC(ERR_error_string)
+ RESOLVEFUNC(ERR_error_string_n)
+ RESOLVEFUNC(ERR_get_error)
+ RESOLVEFUNC(EVP_CIPHER_CTX_new)
+ RESOLVEFUNC(EVP_CIPHER_CTX_free)
+ RESOLVEFUNC(EVP_CIPHER_CTX_ctrl)
+ RESOLVEFUNC(EVP_CIPHER_CTX_set_key_length)
+ RESOLVEFUNC(EVP_CipherInit)
+ RESOLVEFUNC(EVP_CipherInit_ex)
+ RESOLVEFUNC(EVP_CipherUpdate)
+ RESOLVEFUNC(EVP_CipherFinal)
+ RESOLVEFUNC(EVP_get_digestbyname)
+ RESOLVEFUNC(EVP_sha1)
+ RESOLVEFUNC(EVP_sha256)
+ RESOLVEFUNC(EVP_aes_256_gcm)
+ RESOLVEFUNC(EVP_aes_128_cbc)
+ RESOLVEFUNC(EVP_PKEY_assign)
+ RESOLVEFUNC(EVP_PKEY_set1_RSA)
+ RESOLVEFUNC(EVP_PKEY_set1_DH)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EVP_PKEY_set1_EC_KEY)
+#endif
+ RESOLVEFUNC(EVP_PKEY_free)
+ RESOLVEFUNC(EVP_PKEY_get1_RSA)
+ RESOLVEFUNC(EVP_PKEY_get1_DH)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EVP_PKEY_get1_EC_KEY)
+#endif
+ RESOLVEFUNC(EVP_PKEY_new)
+ RESOLVEFUNC(EVP_PKEY_type)
+ RESOLVEFUNC(OBJ_nid2sn)
+ RESOLVEFUNC(OBJ_nid2ln)
+ RESOLVEFUNC(OBJ_sn2nid)
+ RESOLVEFUNC(OBJ_ln2nid)
+ RESOLVEFUNC(i2t_ASN1_OBJECT)
+ RESOLVEFUNC(OBJ_obj2txt)
+ RESOLVEFUNC(OBJ_obj2nid)
+
+#ifndef SSLEAY_MACROS
+ RESOLVEFUNC(PEM_read_bio_PrivateKey)
+ RESOLVEFUNC(PEM_write_bio_PKCS8PrivateKey)
+ RESOLVEFUNC(PEM_read_bio_RSAPrivateKey)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_read_bio_ECPrivateKey)
+#endif
+ RESOLVEFUNC(PEM_read_bio_DHparams)
+ RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
+ RESOLVEFUNC(PEM_write_bio_PrivateKey)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_write_bio_ECPrivateKey)
+#endif
+#endif // !SSLEAY_MACROS
+
+ RESOLVEFUNC(PEM_read_bio_PUBKEY)
+ RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_read_bio_EC_PUBKEY)
+#endif
+ RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY)
+ RESOLVEFUNC(PEM_write_bio_PUBKEY)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_write_bio_EC_PUBKEY)
+#endif
+ RESOLVEFUNC(RAND_seed)
+ RESOLVEFUNC(RAND_status)
+ RESOLVEFUNC(RAND_bytes)
+ RESOLVEFUNC(RSA_new)
+ RESOLVEFUNC(RSA_generate_key_ex);
+ RESOLVEFUNC(RSA_free)
+ RESOLVEFUNC(X509_NAME_entry_count)
+ RESOLVEFUNC(X509_NAME_get_entry)
+ RESOLVEFUNC(X509_NAME_ENTRY_get_data)
+ RESOLVEFUNC(X509_NAME_ENTRY_get_object)
+ RESOLVEFUNC(X509_PUBKEY_get)
+ RESOLVEFUNC(X509_STORE_free)
+ RESOLVEFUNC(X509_STORE_new)
+ RESOLVEFUNC(X509_STORE_add_cert)
+ RESOLVEFUNC(X509_STORE_CTX_free)
+ RESOLVEFUNC(X509_STORE_CTX_init)
+ RESOLVEFUNC(X509_STORE_CTX_new)
+ RESOLVEFUNC(X509_STORE_CTX_set_purpose)
+ RESOLVEFUNC(X509_STORE_CTX_get_error)
+ RESOLVEFUNC(X509_STORE_CTX_get_error_depth)
+ RESOLVEFUNC(X509_STORE_CTX_get_current_cert)
+ RESOLVEFUNC(X509_cmp)
+ RESOLVEFUNC(X509_STORE_CTX_get_ex_data)
+
+#ifndef SSLEAY_MACROS
+ RESOLVEFUNC(X509_dup)
+#endif
+ RESOLVEFUNC(X509_print)
+ RESOLVEFUNC(X509_digest)
+ RESOLVEFUNC(X509_EXTENSION_get_object)
+ RESOLVEFUNC(X509_free)
+ RESOLVEFUNC(X509_gmtime_adj)
+ RESOLVEFUNC(ASN1_TIME_free)
+ RESOLVEFUNC(X509_get_ext)
+ RESOLVEFUNC(X509_get_ext_count)
+ RESOLVEFUNC(X509_get_ext_d2i)
+ RESOLVEFUNC(X509V3_EXT_get)
+ RESOLVEFUNC(X509V3_EXT_d2i)
+ RESOLVEFUNC(X509_EXTENSION_get_critical)
+ RESOLVEFUNC(X509_EXTENSION_get_data)
+ RESOLVEFUNC(BASIC_CONSTRAINTS_free)
+ RESOLVEFUNC(AUTHORITY_KEYID_free)
+ RESOLVEFUNC(GENERAL_NAME_free)
+ RESOLVEFUNC(ASN1_STRING_print)
+ RESOLVEFUNC(X509_check_issued)
+ RESOLVEFUNC(X509_get_issuer_name)
+ RESOLVEFUNC(X509_get_subject_name)
+ RESOLVEFUNC(X509_get_serialNumber)
+ RESOLVEFUNC(X509_verify_cert)
+ RESOLVEFUNC(d2i_X509)
+ RESOLVEFUNC(i2d_X509)
+ RESOLVEFUNC(SSL_CTX_load_verify_locations)
+ RESOLVEFUNC(i2d_SSL_SESSION)
+ RESOLVEFUNC(d2i_SSL_SESSION)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
+ RESOLVEFUNC(SSL_select_next_proto)
+ RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
+ RESOLVEFUNC(SSL_get0_next_proto_negotiated)
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ RESOLVEFUNC(SSL_set_alpn_protos)
+ RESOLVEFUNC(SSL_CTX_set_alpn_select_cb)
+ RESOLVEFUNC(SSL_get0_alpn_selected)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
+ RESOLVEFUNC(CRYPTO_malloc)
+ RESOLVEFUNC(DH_new)
+ RESOLVEFUNC(DH_free)
+ RESOLVEFUNC(d2i_DHparams)
+ RESOLVEFUNC(i2d_DHparams)
+ RESOLVEFUNC(DH_check)
+ RESOLVEFUNC(BN_bin2bn)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EC_KEY_dup)
+ RESOLVEFUNC(EC_KEY_new_by_curve_name)
+ RESOLVEFUNC(EC_KEY_free)
+ RESOLVEFUNC(EC_get_builtin_curves)
+#endif // OPENSSL_NO_EC
+ RESOLVEFUNC(PKCS12_parse)
+ RESOLVEFUNC(d2i_PKCS12_bio)
+ RESOLVEFUNC(PKCS12_free)
+ RESOLVEFUNC(X509_REQ_to_X509)
+ RESOLVEFUNC(PEM_write_bio_X509)
+ RESOLVEFUNC(X509_new)
+ RESOLVEFUNC(ASN1_INTEGER_set)
+ RESOLVEFUNC(X509_set_pubkey)
+ RESOLVEFUNC(X509_set_issuer_name)
+ RESOLVEFUNC(X509_sign)
+ RESOLVEFUNC(X509_add_ext)
+ RESOLVEFUNC(X509_set_version)
+ RESOLVEFUNC(X509_set_subject_name)
+ RESOLVEFUNC(ASN1_OCTET_STRING_new)
+ RESOLVEFUNC(X509_pubkey_digest)
+ RESOLVEFUNC(ASN1_OCTET_STRING_set)
+ RESOLVEFUNC(X509_add1_ext_i2d)
+ RESOLVEFUNC(ASN1_OCTET_STRING_free)
+ RESOLVEFUNC(ASN1_INTEGER_new)
+ RESOLVEFUNC(GENERAL_NAMES_new)
+ RESOLVEFUNC(GENERAL_NAME_new)
+ RESOLVEFUNC(X509_NAME_dup)
+ RESOLVEFUNC(X509_set_serialNumber)
+ RESOLVEFUNC(AUTHORITY_KEYID_new)
+ RESOLVEFUNC(ASN1_INTEGER_dup)
+ RESOLVEFUNC(X509_NAME_digest)
+ RESOLVEFUNC(ASN1_INTEGER_free)
+ RESOLVEFUNC(i2d_X509_REQ_bio)
+ RESOLVEFUNC(i2d_X509_bio)
+
+ symbolsResolved.storeRelease(true);
+ return true;
+}
+#endif // QT_CONFIG(library)
+
+#else // !defined QT_LINKED_OPENSSL
+
+bool q_resolveOpenSslSymbols()
+{
+#ifdef QT_NO_OPENSSL
+ return false;
+#endif
+ return true;
+}
+#endif // !defined QT_LINKED_OPENSSL
+
+//==============================================================================
+// contributed by Jay Case of Sarvega, Inc.; http://sarvega.com/
+// Based on X509_cmp_time() for intitial buffer hacking.
+//==============================================================================
+QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
+{
+ size_t lTimeLength = aTime->length;
+ char *pString = (char *) aTime->data;
+
+ if (aTime->type == V_ASN1_UTCTIME) {
+
+ char lBuffer[24];
+ char *pBuffer = lBuffer;
+
+ if ((lTimeLength < 11) || (lTimeLength > 17))
+ return QDateTime();
+
+ memcpy(pBuffer, pString, 10);
+ pBuffer += 10;
+ pString += 10;
+
+ if ((*pString == 'Z') || (*pString == '-') || (*pString == '+')) {
+ *pBuffer++ = '0';
+ *pBuffer++ = '0';
+ } else {
+ *pBuffer++ = *pString++;
+ *pBuffer++ = *pString++;
+ // Skip any fractional seconds...
+ if (*pString == '.') {
+ pString++;
+ while ((*pString >= '0') && (*pString <= '9'))
+ pString++;
+ }
+ }
+
+ *pBuffer++ = 'Z';
+ *pBuffer++ = '\0';
+
+ time_t lSecondsFromUCT;
+ if (*pString == 'Z') {
+ lSecondsFromUCT = 0;
+ } else {
+ if ((*pString != '+') && (*pString != '-'))
+ return QDateTime();
+
+ lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
+ lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0');
+ lSecondsFromUCT *= 60;
+ if (*pString == '-')
+ lSecondsFromUCT = -lSecondsFromUCT;
+ }
+
+ tm lTime;
+ lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
+ lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
+ lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
+ lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
+ lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
+ lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
+ if (lTime.tm_year < 50)
+ lTime.tm_year += 100; // RFC 2459
+
+ QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday);
+ QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
+
+ QDateTime result(resDate, resTime, Qt::UTC);
+ result = result.addSecs(lSecondsFromUCT);
+ return result;
+
+ } else if (aTime->type == V_ASN1_GENERALIZEDTIME) {
+
+ if (lTimeLength < 15)
+ return QDateTime(); // hopefully never triggered
+
+ // generalized time is always YYYYMMDDHHMMSSZ (RFC 2459, section 4.1.2.5.2)
+ tm lTime;
+ lTime.tm_sec = ((pString[12] - '0') * 10) + (pString[13] - '0');
+ lTime.tm_min = ((pString[10] - '0') * 10) + (pString[11] - '0');
+ lTime.tm_hour = ((pString[8] - '0') * 10) + (pString[9] - '0');
+ lTime.tm_mday = ((pString[6] - '0') * 10) + (pString[7] - '0');
+ lTime.tm_mon = (((pString[4] - '0') * 10) + (pString[5] - '0'));
+ lTime.tm_year = ((pString[0] - '0') * 1000) + ((pString[1] - '0') * 100) +
+ ((pString[2] - '0') * 10) + (pString[3] - '0');
+
+ QDate resDate(lTime.tm_year, lTime.tm_mon, lTime.tm_mday);
+ QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
+
+ QDateTime result(resDate, resTime, Qt::UTC);
+ return result;
+
+ } else {
+ qCWarning(lcSsl, "unsupported date format detected");
+ return QDateTime();
+ }
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/openssl_symbols_p.h b/src/opcua/x509/openssl_symbols_p.h
new file mode 100644
index 0000000..0241c51
--- /dev/null
+++ b/src/opcua/x509/openssl_symbols_p.h
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#ifndef OPENSSL_SYMBOLS_P_H
+#define OPENSSL_SYMBOLS_P_H
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/crypto.h>
+#include <openssl/tls1.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtCore/QLoggingCategory>
+
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#if defined(X509_NAME)
+#undef X509_NAME
+#endif
+#endif // Q_OS_WIN
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcSsl)
+
+#define DUMMYARG
+
+#if !defined QT_LINKED_OPENSSL
+// **************** Shared declarations ******************
+// ret func(arg)
+
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a); \
+ }
+
+// ret func(arg1, arg2)
+# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func);\
+ err; \
+ } \
+ funcret _q_##func(a, b); \
+ }
+
+// ret func(arg1, arg2, arg3)
+# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4)
+# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg5)
+# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6)
+# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
+# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f, g); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
+# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f, g, h, i); \
+ }
+// **************** Shared declarations ******************
+
+#else // !defined QT_LINKED_OPENSSL
+
+// **************** Static declarations ******************
+
+// ret func(arg)
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ ret q_##func(arg) { funcret func(a); }
+
+// ret func(arg1, arg2)
+# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
+ ret q_##func(arg1, arg2) { funcret func(a, b); }
+
+// ret func(arg1, arg2, arg3)
+# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
+ ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); }
+
+// ret func(arg1, arg2, arg3, arg4)
+# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); }
+
+// ret func(arg1, arg2, arg3, arg4, arg5)
+# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6)
+# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
+# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
+# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); }
+
+// **************** Static declarations ******************
+
+#endif // !defined QT_LINKED_OPENSSL
+
+#if QT_CONFIG(opensslv11)
+#include "qsslsocket_openssl11_symbols_p.h"
+#else
+#include "qsslsocket_opensslpre11_symbols_p.h"
+#endif // QT_CONFIG
+
+bool q_resolveOpenSslSymbols();
+long q_ASN1_INTEGER_get(ASN1_INTEGER *a);
+int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y);
+int q_ASN1_STRING_length(ASN1_STRING *a);
+int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
+long q_BIO_ctrl(BIO *a, int b, long c, void *d);
+Q_AUTOTEST_EXPORT int q_BIO_free(BIO *a);
+void q_BIO_free_all(BIO *a);
+BIO *q_BIO_new_mem_buf(void *a, int b);
+int q_BIO_read(BIO *a, void *b, int c);
+Q_AUTOTEST_EXPORT int q_BIO_write(BIO *a, const void *b, int c);
+int q_BN_num_bits(const BIGNUM *a);
+
+#if QT_CONFIG(opensslv11)
+int q_BN_is_word(BIGNUM *a, BN_ULONG w);
+#else // opensslv11
+// BN_is_word is implemented purely as a
+// macro in OpenSSL < 1.1. It doesn't
+// call any functions.
+//
+// The implementation of BN_is_word is
+// 100% the same between 1.0.0, 1.0.1
+// and 1.0.2.
+//
+// Users are required to include <openssl/bn.h>.
+#define q_BN_is_word BN_is_word
+#endif // !opensslv11
+
+BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w);
+int q_BN_set_word(const BIGNUM *a, BN_ULONG w);
+BIGNUM *q_BN_new();
+void q_BN_clear(BIGNUM *a);
+void q_BN_free(BIGNUM *a);
+void q_BN_clear_free(BIGNUM *a);
+
+#ifndef OPENSSL_NO_EC
+const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k);
+int q_EC_GROUP_get_degree(const EC_GROUP* g);
+#endif
+DSA *q_DSA_new();
+void q_DSA_free(DSA *a);
+X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
+char *q_ERR_error_string(unsigned long a, char *b);
+void q_ERR_error_string_n(unsigned long e, char *buf, size_t len);
+unsigned long q_ERR_get_error();
+EVP_CIPHER_CTX *q_EVP_CIPHER_CTX_new();
+void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a);
+int q_EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
+int q_EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);
+int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *key, const unsigned char *iv, int enc);
+int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc);
+int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
+int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
+const EVP_MD *q_EVP_get_digestbyname(const char *name);
+X509_REQ *q_X509_REQ_new();
+void q_X509_REQ_free(X509_REQ *req);
+int q_PEM_write_bio_X509_REQ(BIO *bp, X509_REQ *x);
+int q_PEM_write_bio_X509_REQ_NEW(BIO *bp, X509_REQ *x);
+int q_X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts);
+void q_X509_EXTENSION_free(X509_EXTENSION *ext);
+int q_X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit);
+ASN1_INTEGER *q_ASN1_INTEGER_dup(const ASN1_INTEGER *x);
+
+#if QT_CONFIG(opensslv11)
+X509_NAME *q_X509_REQ_get_subject_name(X509_REQ *req);
+#define q_ERR_load_crypto_strings() \
+ q_OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)
+#else // opensslv11
+void q_ERR_load_crypto_strings();
+#define q_X509_REQ_get_subject_name X509_REQ_get_subject_name
+#define q_X509_get_notBefore X509_get_notBefore
+#define q_X509_get_notAfter X509_get_notAfter
+#endif // !opensslv11
+
+
+int q_X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type, const unsigned char *bytes, int len, int loc, int set);
+int q_X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int type, const unsigned char *bytes, int len, int loc, int set);
+ASN1_OBJECT *q_OBJ_txt2obj(const char *s, int no_name);
+int q_X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
+int q_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md);
+int q_X509_REQ_set_version(X509_REQ *x, long version);
+X509_EXTENSION *q_X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, int ext_nid, char *value);
+
+#ifndef OPENSSL_NO_DES
+const EVP_CIPHER *q_EVP_des_cbc();
+const EVP_CIPHER *q_EVP_des_ede3_cbc();
+#endif
+const EVP_CIPHER *q_EVP_aes_256_gcm();
+const EVP_CIPHER *q_EVP_aes_128_cbc();
+Q_AUTOTEST_EXPORT const EVP_MD *q_EVP_sha1();
+const EVP_MD *q_EVP_sha256();
+int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c);
+Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
+int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
+int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b);
+#ifndef OPENSSL_NO_EC
+int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
+#endif
+Q_AUTOTEST_EXPORT void q_EVP_PKEY_free(EVP_PKEY *a);
+RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
+DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
+DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a);
+#ifndef OPENSSL_NO_EC
+EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a);
+#endif
+int q_EVP_PKEY_type(int a);
+Q_AUTOTEST_EXPORT EVP_PKEY *q_EVP_PKEY_new();
+int q_i2d_X509(X509 *a, unsigned char **b);
+const char *q_OBJ_nid2sn(int a);
+const char *q_OBJ_nid2ln(int a);
+int q_OBJ_sn2nid(const char *s);
+int q_OBJ_ln2nid(const char *s);
+int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj);
+int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name);
+int q_OBJ_obj2nid(const ASN1_OBJECT *a);
+#define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(a))
+#ifdef SSLEAY_MACROS
+// ### verify
+void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_password_cb *e,
+ void *f);
+// ### ditto for write
+#else
+Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PrivateKey(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
+DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d);
+RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d);
+#ifndef OPENSSL_NO_EC
+EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
+#endif
+DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d);
+int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_PKCS8PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+#ifndef OPENSSL_NO_EC
+int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+#endif
+#endif // SSLEAY_MACROS
+Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
+DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
+RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
+#ifndef OPENSSL_NO_EC
+EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
+#endif
+int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b);
+int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b);
+int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b);
+#ifndef OPENSSL_NO_EC
+int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b);
+#endif
+void q_RAND_seed(const void *a, int b);
+int q_RAND_status();
+int q_RAND_bytes(unsigned char *b, int n);
+RSA *q_RSA_new();
+void q_RSA_free(RSA *a);
+int q_RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
+void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback);
+typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+int q_SSL_write(SSL *a, const void *b, int c);
+int q_X509_cmp(X509 *a, X509 *b);
+#ifdef SSLEAY_MACROS
+void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x);
+#define q_X509_dup(x509) (X509 *)q_ASN1_dup((i2d_of_void *)q_i2d_X509, \
+ (d2i_of_void *)q_d2i_X509,(char *)x509)
+#else
+X509 *q_X509_dup(X509 *a);
+#endif
+void q_X509_print(BIO *a, X509*b);
+int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len);
+ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a);
+Q_AUTOTEST_EXPORT void q_X509_free(X509 *a);
+Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+Q_AUTOTEST_EXPORT void q_ASN1_TIME_free(ASN1_TIME *t);
+X509_EXTENSION *q_X509_get_ext(X509 *a, int b);
+int q_X509_get_ext_count(X509 *a);
+void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d);
+const X509V3_EXT_METHOD *q_X509V3_EXT_get(X509_EXTENSION *a);
+void *q_X509V3_EXT_d2i(X509_EXTENSION *a);
+int q_X509_EXTENSION_get_critical(X509_EXTENSION *a);
+ASN1_OCTET_STRING *q_X509_EXTENSION_get_data(X509_EXTENSION *a);
+void q_BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *a);
+void q_AUTHORITY_KEYID_free(AUTHORITY_KEYID *a);
+int q_ASN1_STRING_print(BIO *a, const ASN1_STRING *b);
+int q_X509_check_issued(X509 *a, X509 *b);
+X509_NAME *q_X509_get_issuer_name(X509 *a);
+X509_NAME *q_X509_get_subject_name(X509 *a);
+ASN1_INTEGER *q_X509_get_serialNumber(X509 *a);
+int q_X509_verify_cert(X509_STORE_CTX *ctx);
+int q_X509_NAME_entry_count(X509_NAME *a);
+X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b);
+ASN1_STRING *q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a);
+ASN1_OBJECT *q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a);
+EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a);
+void q_X509_STORE_free(X509_STORE *store);
+X509_STORE *q_X509_STORE_new();
+int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
+void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx);
+int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
+ X509 *x509, STACK_OF(X509) *chain);
+X509_STORE_CTX *q_X509_STORE_CTX_new();
+int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
+int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
+int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
+X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
+X509 *q_X509_REQ_to_X509(X509_REQ *r, int days, EVP_PKEY *pkey);
+int q_PEM_write_bio_X509(BIO *bp, X509 *x);
+X509 *q_X509_new();
+int q_ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
+int q_X509_set_pubkey(X509 *x, EVP_PKEY *key);
+int q_X509_set_issuer_name(X509 *x, X509_NAME *name);
+int q_X509_sign(X509 *x, EVP_PKEY *key, const EVP_MD *md);
+int q_X509_add_ext(X509 *x, X509_EXTENSION *ex, int location);
+int q_X509_set_version(X509 *x, long version);
+int q_X509_set_subject_name(X509 *x, X509_NAME *name);
+ASN1_OCTET_STRING *q_ASN1_OCTET_STRING_new();
+int q_X509_pubkey_digest(const X509 *data, const EVP_MD *type, unsigned char *md, unsigned int *len);
+int q_ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *str, const unsigned char *data, int len);
+int q_X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit, unsigned long flags);
+void q_ASN1_OCTET_STRING_free(ASN1_OCTET_STRING *a);
+int q_X509_NAME_digest(const X509_NAME *data, const EVP_MD *type, unsigned char *md, unsigned int *len);
+ASN1_INTEGER *q_ASN1_INTEGER_new();
+GENERAL_NAMES *q_GENERAL_NAMES_new();
+GENERAL_NAME *q_GENERAL_NAME_new();
+X509_NAME *q_X509_NAME_dup(X509_NAME *xn);
+int q_X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial);
+AUTHORITY_KEYID *q_AUTHORITY_KEYID_new();
+void q_ASN1_INTEGER_free(ASN1_INTEGER *a);
+int q_i2d_X509_REQ_bio(BIO *bp, X509_REQ *req);
+int q_i2d_X509_bio(BIO *bp, X509 *x509);
+
+// Diffie-Hellman support
+DH *q_DH_new();
+void q_DH_free(DH *dh);
+DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length);
+int q_i2d_DHparams(DH *a, unsigned char **p);
+int q_DH_check(DH *dh, int *codes);
+
+BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
+
+#ifndef OPENSSL_NO_EC
+// EC Diffie-Hellman support
+EC_KEY *q_EC_KEY_dup(const EC_KEY *src);
+EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
+void q_EC_KEY_free(EC_KEY *ecdh);
+
+// EC curves management
+size_t q_EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems);
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+int q_EC_curve_nist2nid(const char *name);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif // OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+#define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+// PKCS#12 support
+int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca);
+PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12);
+void q_PKCS12_free(PKCS12 *pkcs12);
+
+#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
+#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
+#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL)
+#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st))
+#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i))
+#if QT_CONFIG(opensslv11)
+#define q_sk_GENERAL_NAME_push(st, val) q_OPENSSL_sk_push((st), (val))
+#else
+#define q_sk_GENERAL_NAME_push(st, val) q_SKM_sk_push(GENERAL_NAME, (st), (val))
+#endif
+
+void q_GENERAL_NAME_free(GENERAL_NAME *a);
+
+#define q_sk_X509_num(st) q_SKM_sk_num(X509, (st))
+#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i))
+#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num(SSL_CIPHER, (st))
+#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i))
+#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \
+ q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
+ (char *)(rsa))
+#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\
+ (char *)(dsa))
+#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf()
+int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp);
+
+void q_BIO_set_flags(BIO *b, int flags);
+void q_BIO_clear_flags(BIO *b, int flags);
+void *q_BIO_get_ex_data(BIO *b, int idx);
+int q_BIO_set_ex_data(BIO *b, int idx, void *data);
+
+#define q_BIO_set_retry_read(b) q_BIO_set_flags(b, (BIO_FLAGS_READ|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_set_retry_write(b) q_BIO_set_flags(b, (BIO_FLAGS_WRITE|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_clear_retry_flags(b) q_BIO_clear_flags(b, (BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_set_app_data(s,arg) q_BIO_set_ex_data(s,0,arg)
+#define q_BIO_get_app_data(s) q_BIO_get_ex_data(s,0)
+
+// Helper function
+class QDateTime;
+QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);
+
+void *q_CRYPTO_malloc(size_t num, const char *file, int line);
+#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/opcua/x509/qopcuakeypair.cpp b/src/opcua/x509/qopcuakeypair.cpp
new file mode 100644
index 0000000..90e60fe
--- /dev/null
+++ b/src/opcua/x509/qopcuakeypair.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuakeypair.h"
+#include "qopcuakeypair_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaKeyPair
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief QOpcUaKeyPair handles private and public key pairs
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+
+ It can generate, load and store keys for asymmetric encryption.
+ Instances of this class have to be passed to functions which need a key.
+*/
+
+/*!
+ \enum QOpcUaKeyPair::RsaKeyStrength
+
+ This enum type specifies the strength of a RSA key.
+
+ \value Bits1024
+ A key strength of 1024 bits.
+ \value Bits2048
+ A key strength of 2048 bits.
+ \value Bits4096
+ A key strength of 4096 bits.
+*/
+
+/*!
+ \enum QOpcUaKeyPair::KeyType
+
+ This enum type specifies the type of a key.
+
+ \value Rsa
+ An RSA key
+ \value Empty
+ No key is available.
+ \value Unknown
+ The type of key is not handled.
+*/
+
+/*!
+ \enum QOpcUaKeyPair::Cipher
+
+ Ciphers for encryption of private keys.
+
+ \value Aes128Cbc
+ Encrypting AES128 with CBC
+ \value Unencrypted
+ The Key will not be encrypted.
+*/
+
+/*!
+ \fn QOpcUaKeyPair::passphaseNeeded()
+
+ This signal is emitted when a private key needs a passphrase for en- or decryption.
+
+ Place the passphrase in \a passphrase.
+ \a writeOperation is \c true when the passphrase is needed for exporting a key, and
+ is \c false when the passphrase is needed for importing a key.
+
+ \a maximumPassphraseSize specifies the maximum length in bytes for the passphrase.
+ All characters in \a passphrase exceeding this limit will be ignored.
+
+ In case you have use this signal crossing thread boundaries you have to connect it
+ with \c Qt::BlockingQueuedConnection.
+*/
+
+/*!
+ Creates a new empty key pair.
+*/
+QOpcUaKeyPair::QOpcUaKeyPair(QObject *parent)
+ : QObject(*(new QOpcUaKeyPairPrivate()), parent)
+{
+}
+/*!
+ Destroys the key pair.
+*/
+QOpcUaKeyPair::~QOpcUaKeyPair()
+{
+}
+
+/*!
+ Loads a key from PEM encoded data in \a data.
+ Returns \c true on success and \c false otherwise.
+
+ It detects from the PEM header if the data contains a private or
+ public key. Loading encrypted keys is possible by connecting a
+ function to the signal \c passphraseNeeded for provision of the
+ passphrase.
+*/
+bool QOpcUaKeyPair::loadFromPemData(const QByteArray &data)
+{
+ Q_D(QOpcUaKeyPair);
+ return d->loadFromPemData(data);
+}
+
+/*!
+ Loads a key from PEM encoded data in \a data.
+ Returns \c true on success and \c false otherwise.
+
+ It detects from the PEM header if the data contains a private or
+ public key. Loading encrypted keys is possible by connecting a
+ function to the signal \c passphraseNeeded for provision of the
+ passphrase.
+*/
+QByteArray QOpcUaKeyPair::publicKeyToByteArray() const
+{
+ Q_D(const QOpcUaKeyPair);
+ return d->publicKeyToByteArray();
+}
+
+/*!
+ Returns the type of the current key.
+*/
+QOpcUaKeyPair::KeyType QOpcUaKeyPair::type() const
+{
+ Q_D(const QOpcUaKeyPair);
+ return d->keyType();
+}
+
+/*!
+ Returns \c true if the current key contains a private key, otherwise \c false.
+*/
+bool QOpcUaKeyPair::hasPrivateKey() const
+{
+ Q_D(const QOpcUaKeyPair);
+ return d->hasPrivateKey();
+}
+
+/*!
+ Returns the PEM encoded private key.
+ In case there is no private key, an empty byte array is returned.
+
+ The encryption of the key has to be specified using the parameters
+ \a cipher and \a password. In order to store the key unencrypted
+ the cipher \c Cipher::Unencrypted has to be used.
+*/
+QByteArray QOpcUaKeyPair::privateKeyToByteArray(Cipher cipher, const QString &password) const
+{
+ Q_D(const QOpcUaKeyPair);
+ return d->privateKeyToByteArray(cipher, password);
+}
+
+
+/*!
+ Generates a new asymmetric RSA key pair.
+
+ The length of the key can be specified by the \c strength parameter.
+*/
+void QOpcUaKeyPair::generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength)
+{
+ Q_D(QOpcUaKeyPair);
+ d->generateRsaKey(strength);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuakeypair.h b/src/opcua/x509/qopcuakeypair.h
new file mode 100644
index 0000000..dbd1197
--- /dev/null
+++ b/src/opcua/x509/qopcuakeypair.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAKEYPAIR_H
+#define QOPCUAKEYPAIR_H
+
+#include <QObject>
+#include <QString>
+#include <QSharedPointer>
+#include <QtOpcUa/qopcuaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaKeyPairPrivate;
+
+class Q_OPCUA_EXPORT QOpcUaKeyPair : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOpcUaKeyPair)
+
+public:
+ enum class RsaKeyStrength {
+ Bits1024 = 1024,
+ Bits2048 = 2048,
+ Bits4096 = 4096
+ };
+
+ enum class KeyType {
+ Rsa,
+ Empty,
+ Unknown
+ };
+
+ enum class Cipher {
+ Aes128Cbc,
+ Unencrypted
+ };
+
+ QOpcUaKeyPair(QObject *parent = nullptr);
+
+ virtual ~QOpcUaKeyPair();
+ bool loadFromPemData(const QByteArray &data);
+ QByteArray publicKeyToByteArray() const;
+ QByteArray privateKeyToByteArray(Cipher cipher, const QString &password) const;
+ KeyType type() const;
+ bool hasPrivateKey() const;
+ void generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength);
+
+Q_SIGNALS:
+ void passphraseNeeded(QString &passphrase, int maximumLength, bool writeOperation);
+
+ friend class QOpcUaX509CertificateSigningRequestPrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAKEYPAIR_H
diff --git a/src/opcua/x509/qopcuakeypair_openssl.cpp b/src/opcua/x509/qopcuakeypair_openssl.cpp
new file mode 100644
index 0000000..66a7b7e
--- /dev/null
+++ b/src/opcua/x509/qopcuakeypair_openssl.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuakeypair_p.h"
+#include "openssl_symbols_p.h"
+#include "qopcuax509utils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QOpcUaKeyPairPrivate::QOpcUaKeyPairPrivate()
+ : QObjectPrivate()
+{
+ if (!q_resolveOpenSslSymbols())
+ qFatal("Failed to resolve symbols");
+
+ q_ERR_load_crypto_strings();
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ q_OPENSSL_add_all_algorithms_noconf();
+#endif
+}
+
+QOpcUaKeyPairPrivate::~QOpcUaKeyPairPrivate()
+{
+ if (m_keyData) {
+ q_EVP_PKEY_free(m_keyData);
+ m_keyData = nullptr;
+ }
+}
+
+static int passwordCallback(char *passphraseBuffer, int maximumPassphraseSize, int writeOperation, void *userData) {
+ QOpcUaKeyPair *source = reinterpret_cast<QOpcUaKeyPair*>(userData);
+ QString passphrase;
+ source->passphraseNeeded(passphrase, maximumPassphraseSize, writeOperation == 1);
+
+ if (passphrase.isEmpty())
+ return -1;
+
+ memcpy(passphraseBuffer, passphrase.toUtf8().constData(), qMin(maximumPassphraseSize, passphrase.size()));
+ return passphrase.size();
+}
+
+bool QOpcUaKeyPairPrivate::loadFromPemData(const QByteArray &data) {
+ Q_Q(QOpcUaKeyPair);
+
+ if (m_keyData) {
+ q_EVP_PKEY_free(m_keyData);
+ m_keyData = nullptr;
+ }
+ m_hasPrivateKey = false;
+
+ BIO *bio = q_BIO_new_mem_buf((void *)data.constData(), data.size());
+ if (!bio) {
+ qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
+ return false;
+ }
+ Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
+
+ if (data.startsWith("-----BEGIN PRIVATE KEY-----") || data.startsWith("-----BEGIN ENCRYPTED PRIVATE KEY-----")) {
+ if (!q_PEM_read_bio_PrivateKey(bio, &m_keyData, &passwordCallback, q /* userData */)) {
+ qCWarning(lcSsl) << "Failed to load private key:" << getOpenSslError();
+ return false;
+ }
+ m_hasPrivateKey = true;
+ } else {
+ if (!q_PEM_read_bio_PUBKEY(bio, &m_keyData, NULL, NULL)) {
+ qCWarning(lcSsl) << "Failed to load public key:" << getOpenSslError();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QByteArray QOpcUaKeyPairPrivate::publicKeyToByteArray() const
+{
+ if (!m_keyData) {
+ qCWarning(lcSsl) << "No public key to write";
+ return QByteArray();
+ }
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio) {
+ qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
+ return QByteArray();
+ }
+ Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
+
+ if (0 == q_PEM_write_bio_PUBKEY(bio, m_keyData)) {
+ qCWarning(lcSsl) << "Failed to write public key:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ char *buf;
+ int length = q_BIO_get_mem_data(bio, &buf);
+ QByteArray data(buf, length);
+ return data;
+}
+
+bool QOpcUaKeyPairPrivate::generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength)
+{
+ if (m_keyData) {
+ q_EVP_PKEY_free(m_keyData);
+ m_keyData = nullptr;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ EVP_PKEY_CTX *ctx = q_EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
+ if (!ctx) {
+ qCWarning(lcSsl) << "Failed to allocate context:" << getOpenSslError();
+ return false;
+ }
+ Deleter<EVP_PKEY_CTX> ctxDeleter(ctx, q_EVP_PKEY_CTX_free);
+
+ if (q_EVP_PKEY_keygen_init(ctx) <= 0) {
+ qCWarning(lcSsl) << "Failed to initialize context:" << getOpenSslError();
+ return false;
+ }
+
+ if (q_EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, static_cast<int>(strength)) <= 0) {
+ qCWarning(lcSsl) << "Failed to set context property:" << getOpenSslError();
+ return false;
+ }
+
+ if (q_EVP_PKEY_keygen(ctx, &m_keyData) <= 0) {
+ qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError();
+ return false;
+ }
+
+#else
+ RSA *rsa;
+ BIGNUM *publicExponent;
+
+ publicExponent = q_BN_new();
+ if (publicExponent == NULL) {
+ qCWarning(lcSsl) << "Failed to allocate public exponent:" << getOpenSslError();
+ return false;
+ }
+ Deleter<BIGNUM> publicExponentDeleter(publicExponent, q_BN_free);
+
+ if (q_BN_set_word(publicExponent, RSA_F4) == 0) {
+ qCWarning(lcSsl) << "Failed to set public exponent:" << getOpenSslError();
+ return false;
+ }
+
+ rsa = q_RSA_new();
+ if (rsa == NULL) {
+ qCWarning(lcSsl) << "Failed to allocate RSA:" << getOpenSslError();
+ return false;
+ }
+ Deleter<RSA> rsaDeleter(rsa, q_RSA_free);
+
+ int result = q_RSA_generate_key_ex(rsa, static_cast<int>(strength), publicExponent, nullptr /* progress callback */);
+ if (result == 0) {
+ qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError();
+ return false;
+ }
+
+ m_keyData = q_EVP_PKEY_new();
+ if (!m_keyData) {
+ qCWarning(lcSsl) << "Failed to allocate key data:" << getOpenSslError();
+ return false;
+ }
+
+ if (!q_EVP_PKEY_set1_RSA(m_keyData, rsa)) {
+ qCWarning(lcSsl) << "Failed to transfer key data:" << getOpenSslError();
+ return false;
+ }
+#endif
+ m_hasPrivateKey = true;
+ return true;
+}
+
+QOpcUaKeyPair::KeyType QOpcUaKeyPairPrivate::keyType() const
+{
+ if (!m_keyData)
+ return QOpcUaKeyPair::KeyType::Empty;
+ switch (q_EVP_PKEY_base_id(m_keyData)) {
+ case EVP_PKEY_RSA:
+ return QOpcUaKeyPair::KeyType::Rsa;
+ default:
+ return QOpcUaKeyPair::KeyType::Unknown;
+ }
+}
+
+QByteArray QOpcUaKeyPairPrivate::privateKeyToByteArray(QOpcUaKeyPair::Cipher cipher, const QString &password) const
+{
+ if (!m_keyData) {
+ qCWarning(lcSsl) << "No private key to write";
+ return QByteArray();
+ }
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio) {
+ qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
+ return QByteArray();
+ }
+ Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
+
+ const EVP_CIPHER *enc = NULL;
+ if (cipher == QOpcUaKeyPair::Cipher::Unencrypted)
+ enc = NULL;
+ else if (cipher == QOpcUaKeyPair::Cipher::Aes128Cbc)
+ enc = q_EVP_aes_128_cbc();
+ else {
+ qCWarning(lcSsl) << "Unknown cipher given";
+ return QByteArray();
+ }
+
+ if (0 == q_PEM_write_bio_PKCS8PrivateKey(bio, m_keyData, enc,
+ enc ? (unsigned char*)password.toUtf8().constData() : NULL,
+ enc ? password.length() : 0,
+ NULL /* callback */, NULL /* userdata */)) {
+ qCWarning(lcSsl) << "Failed to write private key:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ char *buf;
+ int length = q_BIO_get_mem_data(bio, &buf);
+ QByteArray data(buf, length);
+ return data;
+}
+
+bool QOpcUaKeyPairPrivate::hasPrivateKey() const
+{
+ if (!m_keyData)
+ return false;
+
+ return m_hasPrivateKey;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuakeypair_p.h b/src/opcua/x509/qopcuakeypair_p.h
new file mode 100644
index 0000000..06b5565
--- /dev/null
+++ b/src/opcua/x509/qopcuakeypair_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QOPCUAPUBLICKEYPRIVATE_H
+#define QOPCUAPUBLICKEYPRIVATE_H
+
+#include <private/qobject_p.h>
+#include "qopcuakeypair.h"
+#include <QtCore/QLoggingCategory>
+#include <openssl/rsa.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcSsl)
+
+class QOpcUaKeyPairPrivate : public QObjectPrivate
+{
+public:
+ QOpcUaKeyPairPrivate();
+ ~QOpcUaKeyPairPrivate();
+
+ bool generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength);
+ bool loadFromPemData(const QByteArray &data);
+ QByteArray publicKeyToByteArray() const;
+ QByteArray privateKeyToByteArray(QOpcUaKeyPair::Cipher cipher, const QString &password) const;
+ QOpcUaKeyPair::KeyType keyType() const;
+ bool hasPrivateKey() const;
+
+protected:
+ EVP_PKEY *m_keyData = nullptr;
+ bool m_hasPrivateKey = false;
+
+private:
+ Q_DECLARE_PUBLIC(QOpcUaKeyPair)
+
+ friend class QOpcUaX509CertificateSigningRequestPrivate;
+};
+QT_END_NAMESPACE
+
+#endif // QOPCUAPUBLICKEYPRIVATE_H
diff --git a/src/opcua/x509/qopcuax509certificatesigningrequest.cpp b/src/opcua/x509/qopcuax509certificatesigningrequest.cpp
new file mode 100644
index 0000000..7bd808f
--- /dev/null
+++ b/src/opcua/x509/qopcuax509certificatesigningrequest.cpp
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509certificatesigningrequest.h"
+#include "openssl_symbols_p.h"
+
+#include "qopcuakeypair_p.h"
+#include "qopcuax509certificatesigningrequest_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509CertificateSigningRequest
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief QOpcUaX509CertificateSigningRequest create a certificate signing request
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+
+ Before actually creating the singing request data, any extension needed for that
+ specific request has to be added. Current supported extensions are SubjectAlternativeName,
+ BasicConstrains, KeyUsage and ExtendedKeyUsage.
+
+ \code
+ // Generate key
+ QOpcUaKeyPair key;
+ key.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+
+ QOpcUaX509CertificateSigningRequest csr;
+
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CommonName, "QtOpcUaViewer");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, "DE");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, "The Qt Company");
+ csr.setSubject(dn);
+
+ QOpcUaX509ExtensionSubjectAlternativeName *san = new QOpcUaX509ExtensionSubjectAlternativeName;
+ san->addData(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "foo.com");
+ san->addData(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "foo.com");
+ san->addData(QOpcUaX509ExtensionSubjectAlternativeName::Type::URI, "urn:foo.com:The%20Qt%20Company:QtOpcUaViewer");
+ san->setCritical(true);
+ csr.addExtension(san);
+
+ QOpcUaX509ExtensionBasicConstraints *bc = new QOpcUaX509ExtensionBasicConstraints;
+ bc->setCa(false);
+ bc->setCritical(true);
+ csr.addExtension(bc);
+
+ QOpcUaX509ExtensionKeyUsage *ku = new QOpcUaX509ExtensionKeyUsage;
+ ku->setCritical(true);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DigitalSignature);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::NonRepudiation);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DataEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CertificateSigning);
+ csr.addExtension(ku);
+
+ QOpcUaX509ExtensionExtendedKeyUsage *eku = new QOpcUaX509ExtensionExtendedKeyUsage;
+ eku->setCritical(true);
+ eku->setKeyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::EmailProtection);
+ csr.addExtension(eku);
+
+ QByteArray csrData = csr.createRequest(key);
+ \endcode
+
+ \sa QOpcUaX509ExtensionSubjectAlternativeName, QOpcUaX509ExtensionBasicConstraints, QOpcUaX509ExtensionKeyUsage, QOpcUaX509ExtensionKeyUsage
+*/
+
+/*!
+ \enum QOpcUaX509CertificateSigningRequest::MessageDigest
+
+ This enum type specifies the message digest to be used.
+
+ \value SHA256
+ Using the SHA256 message digest
+*/
+
+/*!
+ \enum QOpcUaX509CertificateSigningRequest::Encoding
+
+ This enum type specifies the encoding of the generated certificate siging request.
+
+ \value PEM
+ Using PEM encoding
+ \value DER
+ Using DER encoding
+*/
+
+/*!
+ Creates an empty certificate signing request.
+*/
+QOpcUaX509CertificateSigningRequest::QOpcUaX509CertificateSigningRequest()
+ : d_ptr(new QOpcUaX509CertificateSigningRequestPrivate)
+{
+ setEncoding(Encoding::PEM);
+}
+
+/*!
+ Destroys the request and frees all extensions.
+*/
+QOpcUaX509CertificateSigningRequest::~QOpcUaX509CertificateSigningRequest()
+{
+}
+
+/*!
+ Sets the used message digest to \a digest.
+ The default message digest is SHA256.
+*/
+void QOpcUaX509CertificateSigningRequest::setMessageDigest(QOpcUaX509CertificateSigningRequest::MessageDigest digest)
+{
+ Q_D(QOpcUaX509CertificateSigningRequest);
+ d->setMessageDigest(digest);
+}
+
+/*!
+ Returns the used message digest.
+*/
+QOpcUaX509CertificateSigningRequest::MessageDigest QOpcUaX509CertificateSigningRequest::messageDigest() const
+{
+ Q_D(const QOpcUaX509CertificateSigningRequest);
+ return d->messageDigest();
+}
+
+/*!
+ Returns the used request encoding.
+*/
+QOpcUaX509CertificateSigningRequest::Encoding QOpcUaX509CertificateSigningRequest::encoding() const
+{
+ Q_D(const QOpcUaX509CertificateSigningRequest);
+ return d->encoding();
+}
+
+/*!
+ Sets the used request encoding to \a encoding.
+ The default request encoding is PEM.
+*/
+void QOpcUaX509CertificateSigningRequest::setEncoding(QOpcUaX509CertificateSigningRequest::Encoding encoding)
+{
+ Q_D(QOpcUaX509CertificateSigningRequest);
+ d->setEncoding(encoding);
+}
+
+/*!
+ Adds a certificate extension to the request.
+
+ The ownership of the extension object will be transferred to this class.
+
+ \sa QOpcUaX509ExtensionSubjectAlternativeName, QOpcUaX509ExtensionBasicConstraints, QOpcUaX509ExtensionKeyUsage, QOpcUaX509ExtensionKeyUsage
+*/
+void QOpcUaX509CertificateSigningRequest::addExtension(QOpcUaX509Extension *extension)
+{
+ Q_D(QOpcUaX509CertificateSigningRequest);
+ d->addExtension(extension);
+}
+
+/*!
+ Sets the subject for this request.
+ Without a subject it is not possible to generate the request.
+*/
+void QOpcUaX509CertificateSigningRequest::setSubject(const QOpcUaX509DistinguishedName &subject)
+{
+ Q_D(QOpcUaX509CertificateSigningRequest);
+ d->setSubject(subject);
+}
+
+/*!
+ Returns the subject of this request.
+*/
+const QOpcUaX509DistinguishedName &QOpcUaX509CertificateSigningRequest::subject() const
+{
+ Q_D(const QOpcUaX509CertificateSigningRequest);
+ return d->subject();
+}
+
+/*!
+ Creates a certificate signing request to be the to a CA for signing.
+ The private key in \a privateKey is used to sign the request.
+ The request data is returned as a byte array in the encoding set by setEncoding().
+*/
+QByteArray QOpcUaX509CertificateSigningRequest::createRequest(const QOpcUaKeyPair &privateKey)
+{
+ Q_D(QOpcUaX509CertificateSigningRequest);
+ return d->createRequest(privateKey);
+}
+
+/*!
+ Creates a self-signed certificate from this request for immediate use.
+ The private key in \a privateKey is used to sign the request.
+ A validity in days can be specified in \a validityInDays.
+ The request data is returned as a byte array in the encoding set by setEncoding().
+*/
+QByteArray QOpcUaX509CertificateSigningRequest::createSelfSignedCertificate(const QOpcUaKeyPair &privateKey, int validityInDays)
+{
+ Q_D(QOpcUaX509CertificateSigningRequest);
+ return d->createSelfSignedCertificate(privateKey, validityInDays);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509certificatesigningrequest.h b/src/opcua/x509/qopcuax509certificatesigningrequest.h
new file mode 100644
index 0000000..eb750c0
--- /dev/null
+++ b/src/opcua/x509/qopcuax509certificatesigningrequest.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509CERTIFICATESIGNINGREQUEST_H
+#define QOPCUAX509CERTIFICATESIGNINGREQUEST_H
+
+#include "QtOpcUa/qopcuakeypair.h"
+#include "QtOpcUa/qopcuax509extension.h"
+#include "QtOpcUa/qopcuax509distinguishedname.h"
+#include <QVector>
+#include <QtOpcUa/qopcuaglobal.h>
+#include <QtOpcUa/qopcuax509certificatesigningrequest.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaX509CertificateSigningRequestPrivate;
+
+class Q_OPCUA_EXPORT QOpcUaX509CertificateSigningRequest
+{
+ Q_DECLARE_PRIVATE(QOpcUaX509CertificateSigningRequest)
+
+public:
+ enum class MessageDigest {
+ SHA256
+ };
+ enum class Encoding {
+ PEM,
+ DER
+ };
+
+ QOpcUaX509CertificateSigningRequest();
+ ~QOpcUaX509CertificateSigningRequest();
+
+ void setMessageDigest(MessageDigest digest);
+ MessageDigest messageDigest() const;
+
+ void setEncoding(Encoding encoding);
+ Encoding encoding() const;
+
+ void setSubject(const QOpcUaX509DistinguishedName &subject);
+ const QOpcUaX509DistinguishedName &subject() const;
+
+ void addExtension(QOpcUaX509Extension *extension);
+ QByteArray createRequest(const QOpcUaKeyPair &privateKey);
+ QByteArray createSelfSignedCertificate(const QOpcUaKeyPair &privateKey, int validityInDays = 365);
+
+private:
+ QOpcUaX509CertificateSigningRequestPrivate *d_ptr = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509CERTIFICATESIGNINGREQUEST_H
diff --git a/src/opcua/x509/qopcuax509certificatesigningrequest_openssl.cpp b/src/opcua/x509/qopcuax509certificatesigningrequest_openssl.cpp
new file mode 100644
index 0000000..4ec4421
--- /dev/null
+++ b/src/opcua/x509/qopcuax509certificatesigningrequest_openssl.cpp
@@ -0,0 +1,509 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509certificatesigningrequest.h"
+#include "openssl_symbols_p.h"
+#include "qopcuakeypair_p.h"
+#include "qopcuax509utils_p.h"
+#include "qopcuax509distinguishedname.h"
+#include "qopcuax509extensionsubjectalternativename.h"
+#include "qopcuax509certificatesigningrequest_p.h"
+#include <QOpcUaX509ExtensionBasicConstraints>
+#include <QOpcUaX509ExtensionKeyUsage>
+#include <QOpcUaX509ExtensionExtendedKeyUsage>
+
+
+QT_BEGIN_NAMESPACE
+
+QOpcUaX509CertificateSigningRequestPrivate::QOpcUaX509CertificateSigningRequestPrivate()
+{
+
+}
+
+QOpcUaX509CertificateSigningRequestPrivate::~QOpcUaX509CertificateSigningRequestPrivate()
+{
+ qDeleteAll(m_extensions);
+}
+
+void QOpcUaX509CertificateSigningRequestPrivate::setMessageDigest(QOpcUaX509CertificateSigningRequest::MessageDigest digest)
+{
+ m_messageDigest = digest;
+}
+
+QOpcUaX509CertificateSigningRequest::MessageDigest QOpcUaX509CertificateSigningRequestPrivate::messageDigest() const
+{
+ return m_messageDigest;
+}
+
+void QOpcUaX509CertificateSigningRequestPrivate::addExtension(QOpcUaX509Extension *extension)
+{
+ m_extensions.append(extension);
+}
+
+void QOpcUaX509CertificateSigningRequestPrivate::setSubject(const QOpcUaX509DistinguishedName &subject)
+{
+ m_subject = subject;
+}
+
+QOpcUaX509CertificateSigningRequest::Encoding QOpcUaX509CertificateSigningRequestPrivate::encoding() const
+{
+ return m_encoding;
+}
+
+void QOpcUaX509CertificateSigningRequestPrivate::setEncoding(QOpcUaX509CertificateSigningRequest::Encoding encoding)
+{
+ m_encoding = encoding;
+}
+
+const QOpcUaX509DistinguishedName &QOpcUaX509CertificateSigningRequestPrivate::subject() const
+{
+ return m_subject;
+}
+
+static X509_EXTENSION *createExtension(QOpcUaX509Extension *extension)
+{
+ X509_EXTENSION *ex = nullptr;
+
+ if (const auto *san = dynamic_cast<const QOpcUaX509ExtensionSubjectAlternativeName *>(extension)) {
+ QStringList data;
+
+ for (const auto &pair : qAsConst(san->entries())) {
+ QString prefix;
+ if (pair.first == QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS)
+ prefix = QLatin1String("DNS:");
+ else if (pair.first == QOpcUaX509ExtensionSubjectAlternativeName::Type::Email)
+ prefix = QLatin1String("EMAIL:");
+ else if (pair.first == QOpcUaX509ExtensionSubjectAlternativeName::Type::IP)
+ prefix = QLatin1String("IP:");
+ else if (pair.first == QOpcUaX509ExtensionSubjectAlternativeName::Type::URI)
+ prefix = QLatin1String("URI:");
+ else {
+ qCWarning(lcSsl()) << "Invalid SubjectAlternativeName type";
+ return nullptr;
+ }
+
+ if (pair.second.isEmpty() || pair.second.contains(',')) {
+ qCWarning(lcSsl()) << "Invalid SubjectAlternativeName value";
+ return nullptr;
+ }
+
+ data.append(prefix + pair.second);
+ }
+
+ ex = q_X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, data.join(QLatin1Char(',')).toUtf8().data());
+ if (!ex) {
+ qCWarning(lcSsl()) << "Failed to create X509 extension" << data;
+ return nullptr;
+ }
+ q_X509_EXTENSION_set_critical(ex, san->critical() ? 1 : 0);
+ } else if (const auto *bc = dynamic_cast<const QOpcUaX509ExtensionBasicConstraints *>(extension)) {
+ QString data = QLatin1String("CA:") + QLatin1String(bc->ca() ? "true" : "false");
+ if (bc->ca() && bc->pathLength() >= 0)
+ data.append(QLatin1String(",pathlen:") + QString::number(bc->pathLength()));
+
+ ex = q_X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, data.toUtf8().data());
+ if (!ex) {
+ qCWarning(lcSsl()) << "Failed to create X509 extension" << data;
+ return nullptr;
+ }
+ q_X509_EXTENSION_set_critical(ex, bc->critical() ? 1 : 0);
+ } else if (const auto *ku = dynamic_cast<const QOpcUaX509ExtensionKeyUsage *>(extension)) {
+ QStringList data;
+
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DigitalSignature))
+ data.append(QLatin1String("Digital Signature"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::NonRepudiation))
+ data.append(QLatin1String("Non Repudiation"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyEncipherment))
+ data.append(QLatin1String("Key Encipherment"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DataEncipherment))
+ data.append(QLatin1String("Data Encipherment"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyAgreement))
+ data.append(QLatin1String("Key Agreement"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CertificateSigning))
+ data.append(QLatin1String("Certificate Sign"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CrlSigning))
+ data.append(QLatin1String("CRL Sign"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::EnciptherOnly))
+ data.append(QLatin1String("Encipther Only"));
+ if (ku->keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DecipherOnly))
+ data.append(QLatin1String("Decipher Only"));
+
+ ex = q_X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, data.join(QLatin1Char(',')).toUtf8().data());
+ if (!ex) {
+ qCWarning(lcSsl()) << "Failed to create X509 extension" << data;
+ return nullptr;
+ }
+ q_X509_EXTENSION_set_critical(ex, ku->critical() ? 1 : 0);
+ } else if (const auto *eku = dynamic_cast<const QOpcUaX509ExtensionExtendedKeyUsage *>(extension)) {
+ QStringList data;
+
+ if (eku->keyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::TlsWebServerAuthentication))
+ data.append(QLatin1String("SSL Server"));
+ if (eku->keyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::TlsWebClientAuthentication))
+ data.append(QLatin1String("SSL Client"));
+ if (eku->keyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::SignExecutableCode))
+ data.append(QLatin1String("Object Signing"));
+ if (eku->keyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::EmailProtection))
+ data.append(QLatin1String("S/MIME"));
+
+ // NID_ext_key_usage
+ ex = q_X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, data.join(QLatin1Char(',')).toUtf8().data());
+ if (!ex) {
+ qCWarning(lcSsl()) << "Failed to create X509 extension" << data;
+ return nullptr;
+ }
+ q_X509_EXTENSION_set_critical(ex, eku->critical() ? 1 : 0);
+ } else {
+ qCWarning(lcSsl) << "Unknown X509 extension";
+ return nullptr;
+ }
+ return ex;
+}
+
+static bool setSubjectName(X509_NAME *subject, const QOpcUaX509DistinguishedName &dn) {
+ QVector<QOpcUaX509DistinguishedName::Type> entries = {
+ QOpcUaX509DistinguishedName::Type::CommonName,
+ QOpcUaX509DistinguishedName::Type::CountryName,
+ QOpcUaX509DistinguishedName::Type::LocalityName,
+ QOpcUaX509DistinguishedName::Type::StateOrProvinceName,
+ QOpcUaX509DistinguishedName::Type::OrganizationName,
+ };
+
+ for (const auto &type : entries) {
+ const auto value = dn.entry(type);
+ if (value.isEmpty())
+ continue;
+
+ ASN1_OBJECT *obj = q_OBJ_txt2obj(QOpcUaX509DistinguishedName::typeToOid(type).toLatin1().constData(), 1 /* no names allowed */);
+ if (!obj) {
+ qCWarning(lcSsl()) << "Invalid distinguishedName type";
+ return false;
+ }
+
+ if (!q_X509_NAME_add_entry_by_OBJ(subject, obj, MBSTRING_UTF8, (const unsigned char*)(value.toUtf8().constData()), -1, -1, 0)) {
+ qCWarning(lcSsl) << "Failed to set CSR enrty:" << getOpenSslError();
+ return false;
+ }
+ }
+ return true;
+}
+
+// Creates the request and returns a PEM encoded byte array
+QByteArray QOpcUaX509CertificateSigningRequestPrivate::createRequest(const QOpcUaKeyPair &privateKey)
+{
+ if (!privateKey.hasPrivateKey()) {
+ qCWarning(lcSsl) << "Key has no private key";
+ return QByteArray();
+ }
+
+ auto keyData = privateKey.d_func()->m_keyData;
+
+ X509_REQ *req = q_X509_REQ_new();
+ if (!req) {
+ qCWarning(lcSsl) << "Failed to create CSR:" << getOpenSslError();
+ return QByteArray();
+ }
+ Deleter<X509_REQ> reqDeleter(req, q_X509_REQ_free);
+
+ if (!q_X509_REQ_set_version(req, 0 /* version */)) {
+ qCWarning(lcSsl) << "Failed to set CSR version:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ X509_NAME *subj = q_X509_REQ_get_subject_name(req);
+ if (!subj) {
+ qCWarning(lcSsl) << "Invalid subject pointer";
+ return QByteArray();
+ }
+
+ if (!setSubjectName(subj, m_subject)) {
+ qCWarning(lcSsl) << "Failed to set subject";
+ return QByteArray();
+ }
+
+ if (m_extensions.size() > 0) {
+ auto exts = q_sk_X509_EXTENSION_new_null();
+
+ for (auto extension : m_extensions) {
+ auto ex = createExtension(extension);
+ if (ex)
+ q_sk_X509_EXTENSION_push(exts, ex); // returns void
+ }
+ if (q_X509_REQ_add_extensions(req, (STACK_OF(X509_EXTENSION) *)exts) == 0) {
+ qCWarning(lcSsl) << "Failed to add X509 extensions";
+ return QByteArray();
+ }
+ q_sk_X509_EXTENSION_pop_free(exts, (void(*)(void*))q_X509_EXTENSION_free); // frees the whole stack, returns void
+ } // end of for loop
+
+ if (!q_X509_REQ_set_pubkey(req, keyData)) {
+ qCWarning(lcSsl) << "Failed to set public key:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ const EVP_MD *digest = nullptr;
+ if (m_messageDigest == QOpcUaX509CertificateSigningRequest::MessageDigest::SHA256)
+ digest = q_EVP_sha256();
+
+ if (!digest) {
+ qCWarning(lcSsl) << "Invalid message digest";
+ return QByteArray();
+ }
+
+ if (q_X509_REQ_sign(req, keyData, digest) <= 0) {
+ qCWarning(lcSsl) << "Failed to sign CSR:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio) {
+ qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
+ return QByteArray();
+ }
+ Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
+
+ int result = 0;
+
+ if (m_encoding == QOpcUaX509CertificateSigningRequest::Encoding::PEM) {
+ // Some CAs require to use q_PEM_write_bio_X509_REW_NEW
+ result = q_PEM_write_bio_X509_REQ(bio, req);
+ } else if (m_encoding == QOpcUaX509CertificateSigningRequest::Encoding::DER) {
+ result = q_i2d_X509_REQ_bio(bio, req);
+ }
+ if (result != 1) {
+ qCWarning(lcSsl) << "Failed to export certificate request";
+ return QByteArray();
+ }
+
+ char *buf;
+ int length = q_BIO_get_mem_data(bio, &buf);
+ QByteArray data(buf, length);
+ return data;
+}
+
+QByteArray QOpcUaX509CertificateSigningRequestPrivate::createSelfSignedCertificate(const QOpcUaKeyPair &privateKey, int validityInDays)
+{
+ if (!privateKey.hasPrivateKey()) {
+ qCWarning(lcSsl) << "Key has no private key";
+ return QByteArray();
+ }
+
+ auto keyData = privateKey.d_func()->m_keyData;
+
+ X509 *x509 = q_X509_new();
+ if (!x509)
+ return QByteArray();
+
+ Deleter<X509> x509Deleter(x509, q_X509_free);
+
+ if (!q_X509_set_version(x509, 2 /* version */)) {
+ qCWarning(lcSsl) << "Failed to set version";
+ return QByteArray();
+ }
+ q_X509_gmtime_adj(q_X509_getm_notBefore(x509), 0); // current time
+ q_X509_gmtime_adj(q_X509_getm_notAfter(x509), (long)60 * 60 * 24 * validityInDays);
+
+ if (!q_X509_set_pubkey(x509, keyData)) {
+ qCWarning(lcSsl) << "Failed to set public key:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ X509_NAME *subj = q_X509_get_subject_name(x509);
+ if (!subj) {
+ qCWarning(lcSsl) << "Invalid subject pointer";
+ return QByteArray();
+ }
+
+ if (!setSubjectName(subj, m_subject)) {
+ qCWarning(lcSsl) << "Failed to set subject";
+ return QByteArray();
+ }
+
+ X509_NAME *issuer = q_X509_get_issuer_name(x509);
+ if (!issuer) {
+ qCWarning(lcSsl) << "Invalid issuer pointer";
+ return QByteArray();
+ }
+
+ if (!setSubjectName(issuer, m_subject)) {
+ qCWarning(lcSsl) << "Failed to set issuer";
+ return QByteArray();
+ }
+
+ for (auto extension : m_extensions) {
+ auto ex = createExtension(extension);
+ if (ex) {
+ if (!q_X509_add_ext(x509, ex, -1)) {
+ qCWarning(lcSsl) << "Failed to add extension";
+ return QByteArray();
+ }
+ q_X509_EXTENSION_free(ex);
+ } else {
+ qCWarning(lcSsl) << "Invalid extension";
+ return QByteArray();
+ }
+ }
+
+ // Hash of public key
+ unsigned char publicKeyHash[SHA_DIGEST_LENGTH];
+ unsigned int len;
+ if (!q_X509_pubkey_digest(x509, q_EVP_sha1(), publicKeyHash, &len)) {
+ qCWarning(lcSsl) << "Failed to hash public key";
+ return QByteArray();
+ }
+
+ // Set subject key identifier
+ ASN1_OCTET_STRING *subjectKeyIdentifier = q_ASN1_OCTET_STRING_new();
+ if (!subjectKeyIdentifier) {
+ qCWarning(lcSsl) << "Failed to allocate ASN1 string";
+ return QByteArray();
+ }
+ Deleter<ASN1_OCTET_STRING> subjectKeyIdentifierDeleter(subjectKeyIdentifier, q_ASN1_OCTET_STRING_free);
+
+ if (!q_ASN1_OCTET_STRING_set(subjectKeyIdentifier, publicKeyHash, SHA_DIGEST_LENGTH)) {
+ qCWarning(lcSsl) << "Failed set ASN1 string";
+ return QByteArray();
+ }
+
+ if (!q_X509_add1_ext_i2d(x509, NID_subject_key_identifier, subjectKeyIdentifier, 0, X509V3_ADD_DEFAULT)) {
+ qCWarning(lcSsl) << "Failed to add subject key identifier extension";
+ return QByteArray();
+ }
+
+ // Set serial number
+ unsigned char subjHash[SHA_DIGEST_LENGTH];
+ unsigned char finalHash[SHA_DIGEST_LENGTH];
+
+ if (!q_X509_NAME_digest(subj, q_EVP_sha1(), subjHash, &len)) {
+ qCWarning(lcSsl) << "failed";
+ return QByteArray();
+ }
+ for (unsigned int i = 0; i < len; i++)
+ finalHash[i] = subjHash[i] ^ publicKeyHash[i];
+
+ ASN1_INTEGER *serial_num = q_ASN1_INTEGER_new();
+ if (!serial_num) {
+ qCWarning(lcSsl) << "Failed to allocate ASN1 integer";
+ return QByteArray();
+ }
+ Deleter<ASN1_OCTET_STRING> serial_numDeleter(serial_num, q_ASN1_INTEGER_free);
+
+ if (!q_ASN1_OCTET_STRING_set(serial_num, finalHash, len)) {
+ qCWarning(lcSsl) << "Failed to set ASN1 integer";
+ return QByteArray();
+ }
+ if (!q_X509_set_serialNumber(x509, serial_num)) {
+ qCWarning(lcSsl) << "Failed to set serial number";
+ return QByteArray();
+ }
+
+ // Set authority key identifier
+ AUTHORITY_KEYID *akid = q_AUTHORITY_KEYID_new();
+ if (!akid) {
+ qCWarning(lcSsl) << "Failed to allocate authority key id";
+ return QByteArray();
+ }
+ Deleter<AUTHORITY_KEYID> akidDeleter(akid, q_AUTHORITY_KEYID_free);
+
+ akid->issuer = q_GENERAL_NAMES_new();
+ if (!akid->issuer) {
+ qCWarning(lcSsl) << "Failed to set authority key id";
+ return QByteArray();
+ }
+
+ GENERAL_NAME *generalName = q_GENERAL_NAME_new();
+ if (!generalName) {
+ qCWarning(lcSsl) << "Failed to set authority key id";
+ return QByteArray();
+ }
+ generalName->type = GEN_DIRNAME;
+ generalName->d.directoryName = q_X509_NAME_dup(q_X509_get_subject_name(x509));
+
+#if QT_CONFIG(opensslv11)
+ q_sk_GENERAL_NAME_push((OPENSSL_STACK*)akid->issuer, generalName);
+#else
+ q_sk_GENERAL_NAME_push(akid->issuer, generalName);
+#endif
+ akid->keyid = (ASN1_OCTET_STRING*)q_X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL);
+ akid->serial = q_ASN1_INTEGER_dup(q_X509_get_serialNumber(x509));
+
+ if (!q_X509_add1_ext_i2d(x509, NID_authority_key_identifier, akid, 0, X509V3_ADD_DEFAULT)) {
+ qCWarning(lcSsl) << "Failed to add authority key id extension";
+ return QByteArray();
+ }
+
+ const EVP_MD *digest = nullptr;
+ if (m_messageDigest == QOpcUaX509CertificateSigningRequest::MessageDigest::SHA256)
+ digest = q_EVP_sha256();
+
+ if (!digest) {
+ qCWarning(lcSsl) << "Invalid message digest";
+ return QByteArray();
+ }
+
+ if (q_X509_sign(x509, keyData, digest) <= 0) {
+ qCWarning(lcSsl) << "Failed to sign certificate:" << getOpenSslError();
+ return QByteArray();
+ }
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio) {
+ qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
+ return QByteArray();
+ }
+ Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
+
+ int result = 0;
+
+ if (m_encoding == QOpcUaX509CertificateSigningRequest::Encoding::PEM) {
+ result = q_PEM_write_bio_X509(bio, x509);
+ } else if (m_encoding == QOpcUaX509CertificateSigningRequest::Encoding::DER) {
+ result = q_i2d_X509_bio(bio, x509);
+ }
+ if (result != 1) {
+ qCWarning(lcSsl) << "Failed to export certificate";
+ return QByteArray();
+ }
+
+ char *buf;
+ int length = q_BIO_get_mem_data(bio, &buf);
+ QByteArray data(buf, length);
+ return data;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/opcua/x509/qopcuax509certificatesigningrequest_p.h b/src/opcua/x509/qopcuax509certificatesigningrequest_p.h
new file mode 100644
index 0000000..6406d46
--- /dev/null
+++ b/src/opcua/x509/qopcuax509certificatesigningrequest_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QOPCUAX509CERTIFICATESIGNINGREQUESTPRIVATE_H
+#define QOPCUAX509CERTIFICATESIGNINGREQUESTPRIVATE_H
+
+#include "QtOpcUa/qopcuax509extension.h"
+#include "QtOpcUa/qopcuax509distinguishedname.h"
+#include "QtOpcUa/qopcuakeypair.h"
+#include <QVector>
+#include <QtOpcUa/qopcuaglobal.h>
+#include "qopcuax509certificatesigningrequest.h"
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaX509CertificateSigningRequestPrivate
+{
+public:
+ QOpcUaX509CertificateSigningRequestPrivate();
+ ~QOpcUaX509CertificateSigningRequestPrivate();
+
+ void setMessageDigest(QOpcUaX509CertificateSigningRequest::MessageDigest);
+ QOpcUaX509CertificateSigningRequest::MessageDigest messageDigest() const;
+
+ QOpcUaX509CertificateSigningRequest::Encoding encoding() const;
+ void setEncoding(QOpcUaX509CertificateSigningRequest::Encoding encoding);
+
+ const QOpcUaX509DistinguishedName& subject() const;
+ void setSubject(const QOpcUaX509DistinguishedName &subject);
+
+ void addExtension(QOpcUaX509Extension *extension);
+ QByteArray createRequest(const QOpcUaKeyPair &privateKey);
+ QByteArray createSelfSignedCertificate(const QOpcUaKeyPair &privateKey, int validityInDays);
+
+private:
+ QVector<QOpcUaX509Extension *> m_extensions;
+ QOpcUaX509CertificateSigningRequest::MessageDigest m_messageDigest = QOpcUaX509CertificateSigningRequest::MessageDigest::SHA256;
+ QOpcUaX509DistinguishedName m_subject;
+ QOpcUaX509CertificateSigningRequest::Encoding m_encoding = QOpcUaX509CertificateSigningRequest::Encoding::PEM;
+
+ Q_DECLARE_PUBLIC(QOpcUaX509CertificateSigningRequest)
+ QOpcUaX509CertificateSigningRequest *q_ptr = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509CERTIFICATESIGNINGREQUESTPRIVATE_H
diff --git a/src/opcua/x509/qopcuax509distinguishedname.cpp b/src/opcua/x509/qopcuax509distinguishedname.cpp
new file mode 100644
index 0000000..a4c39b0
--- /dev/null
+++ b/src/opcua/x509/qopcuax509distinguishedname.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509distinguishedname.h"
+#include <QtCore/QMap>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509DistinguishedName
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Information about a distinguished name item
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+
+ \code
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CommonName, "QtOpcUaViewer");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, "DE");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, "The Qt Company");
+ \endcode
+
+ \sa QOpcUaX509CertificateSigningRequest
+*/
+
+/*!
+ \enum QOpcUaX509DistinguishedName::Type
+
+ Enum with entry types for X509DistinguishedName.
+
+ \value CommonName
+ Common name
+ \value CountryName
+ Country name
+ \value LocalityName
+ Locality name
+ \value StateOrProvinceName
+ State or provice name
+ \value OrganizationName
+ Organization name
+*/
+
+class QOpcUaX509DistinguishedNameData : public QSharedData
+{
+public:
+ QMap<QOpcUaX509DistinguishedName::Type, QString> entries;
+};
+
+/*!
+ Constructs an empty X509DistinguishedName.
+*/
+QOpcUaX509DistinguishedName::QOpcUaX509DistinguishedName()
+ : data(new QOpcUaX509DistinguishedNameData)
+{
+}
+
+/*!
+ Destructs a X509DistinguishedName.
+*/
+QOpcUaX509DistinguishedName::~QOpcUaX509DistinguishedName()
+{
+}
+
+/*!
+ Constructs a X509DistinguishedName from \a rhs.
+*/
+QOpcUaX509DistinguishedName::QOpcUaX509DistinguishedName(const QOpcUaX509DistinguishedName &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this X509DistinguishedName.
+*/
+QOpcUaX509DistinguishedName &QOpcUaX509DistinguishedName::operator=(const QOpcUaX509DistinguishedName &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Returns \c true if this X509DistinguishedName has the same value as \a rhs.
+*/
+bool QOpcUaX509DistinguishedName::operator==(const QOpcUaX509DistinguishedName &rhs) const
+{
+ return data->entries == rhs.data->entries;
+}
+
+/*!
+ Sets the entry of \a type to \a value.
+ Already existing types will be overwritten.
+*/
+void QOpcUaX509DistinguishedName::setEntry(QOpcUaX509DistinguishedName::Type type, const QString &value)
+{
+ data->entries.insert(type, value);
+}
+
+/*!
+ Returns the object id string for \a type.
+*/
+QString QOpcUaX509DistinguishedName::typeToOid(QOpcUaX509DistinguishedName::Type type)
+{
+ switch (type) {
+ case Type::CommonName:
+ return QLatin1String("2.5.4.3");
+ case Type::CountryName:
+ return QLatin1String("2.5.4.6");
+ case Type::LocalityName:
+ return QLatin1String("2.5.4.7");
+ case Type::StateOrProvinceName:
+ return QLatin1String("2.5.4.8");
+ case Type::OrganizationName:
+ return QLatin1String("2.5.4.10");
+ default:
+ return QString();
+ }
+}
+
+/*!
+ Returns value for a \a type.
+*/
+QString QOpcUaX509DistinguishedName::entry(QOpcUaX509DistinguishedName::Type type) const
+{
+ return data->entries.value(type);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509distinguishedname.h b/src/opcua/x509/qopcuax509distinguishedname.h
new file mode 100644
index 0000000..f7df9f2
--- /dev/null
+++ b/src/opcua/x509/qopcuax509distinguishedname.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUADISTINGUISHEDNAME_H
+#define QOPCUADISTINGUISHEDNAME_H
+
+#include <QtOpcUa/qopcuaglobal.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaX509DistinguishedNameData;
+class Q_OPCUA_EXPORT QOpcUaX509DistinguishedName
+{
+public:
+ enum class Type {
+ CommonName, // 2.5.4.3
+ CountryName, // 2.5.4.6
+ LocalityName, // 2.5.4.7
+ StateOrProvinceName, // 2.5.4.8
+ OrganizationName, // 2.5.4.10
+ };
+
+ QOpcUaX509DistinguishedName();
+ QOpcUaX509DistinguishedName(const QOpcUaX509DistinguishedName &);
+ QOpcUaX509DistinguishedName &operator=(const QOpcUaX509DistinguishedName &);
+ bool operator==(const QOpcUaX509DistinguishedName &rhs) const;
+ ~QOpcUaX509DistinguishedName();
+ void setEntry(Type type, const QString &value);
+ QString entry(Type type) const;
+ static QString typeToOid(Type type);
+
+private:
+ QSharedDataPointer<QOpcUaX509DistinguishedNameData> data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUADISTINGUISHEDNAME_H
diff --git a/src/opcua/x509/qopcuax509extension.cpp b/src/opcua/x509/qopcuax509extension.cpp
new file mode 100644
index 0000000..566a8de
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extension.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509extension.h"
+#include "qopcuax509extension_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509Extension
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Base class for all X509 extensions
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+
+ \sa QOpcUaX509ExtensionSubjectAlternativeName, QOpcUaX509ExtensionBasicConstraints, QOpcUaX509ExtensionKeyUsage, QOpcUaX509ExtensionKeyUsage
+*/
+
+/*!
+ Constructs a default X509Extension.
+*/
+QOpcUaX509Extension::QOpcUaX509Extension()
+ : data(new QOpcUaX509ExtensionData)
+{
+}
+
+/*!
+ Constructs a X509Extension from \a rhs.
+*/
+QOpcUaX509Extension::QOpcUaX509Extension(const QOpcUaX509Extension &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Constructs a X509Extension from \a rhs.
+*/
+QOpcUaX509Extension::QOpcUaX509Extension(QSharedDataPointer<QOpcUaX509ExtensionData> rhs)
+ : data(rhs)
+{
+}
+
+/*!
+ Returns \c true if this X509Extension has the same value as \a rhs.
+*/
+bool QOpcUaX509Extension::operator==(const QOpcUaX509Extension &rhs) const
+{
+ return data->critical == rhs.data->critical;
+}
+
+/*!
+ Sets the values from \a rhs in this X509Extension.
+*/
+QOpcUaX509Extension &QOpcUaX509Extension::operator=(const QOpcUaX509Extension &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Destructs a X509Extension.
+*/
+QOpcUaX509Extension::~QOpcUaX509Extension()
+{
+}
+
+/*!
+ Sets the critical flag to \a critical.
+*/
+void QOpcUaX509Extension::setCritical(bool critical)
+{
+ data->critical = critical;
+}
+
+/*!
+ Return the state of the critical flag.
+*/
+bool QOpcUaX509Extension::critical() const
+{
+ return data->critical;
+}
+
+QOpcUaX509Extension::QOpcUaX509Extension(QOpcUaX509ExtensionData *other)
+{
+ data = other;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509extension.h b/src/opcua/x509/qopcuax509extension.h
new file mode 100644
index 0000000..6e502df
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extension.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509EXTENSION_H
+#define QOPCUAX509EXTENSION_H
+
+#include <QtOpcUa/qopcuaglobal.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaX509ExtensionData;
+class Q_OPCUA_EXPORT QOpcUaX509Extension
+{
+public:
+ QOpcUaX509Extension();
+ QOpcUaX509Extension(const QOpcUaX509Extension &);
+ QOpcUaX509Extension &operator=(const QOpcUaX509Extension &);
+ bool operator==(const QOpcUaX509Extension &rhs) const;
+ virtual ~QOpcUaX509Extension();
+ void setCritical(bool critical);
+ bool critical() const;
+
+protected:
+ QOpcUaX509Extension(QOpcUaX509ExtensionData*);
+ QOpcUaX509Extension(QSharedDataPointer<QOpcUaX509ExtensionData>);
+ QSharedDataPointer<QOpcUaX509ExtensionData> data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509EXTENSION_H
diff --git a/src/opcua/x509/qopcuax509extension_p.h b/src/opcua/x509/qopcuax509extension_p.h
new file mode 100644
index 0000000..e945ffd
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extension_p.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509EXTENSION_P_H
+#define QOPCUAX509EXTENSION_P_H
+
+#include <QtCore/qshareddata.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaX509ExtensionData : public QSharedData
+{
+public:
+ bool critical = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509EXTENSION_P_H
diff --git a/src/opcua/x509/qopcuax509extensionbasicconstraints.cpp b/src/opcua/x509/qopcuax509extensionbasicconstraints.cpp
new file mode 100644
index 0000000..366288c
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionbasicconstraints.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509extensionbasicconstraints.h"
+#include "qopcuax509extension_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509ExtensionBasicConstraints
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Class for X509 basic constraints
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+*/
+
+class QOpcUaX509ExtensionBasicConstraintsData : public QOpcUaX509ExtensionData
+{
+public:
+ bool ca = false;
+ int pathLength = -1;
+};
+
+/*!
+ Constructs a default X509ExtensionBasicConstraints.
+*/
+QOpcUaX509ExtensionBasicConstraints::QOpcUaX509ExtensionBasicConstraints()
+ : QOpcUaX509Extension(new QOpcUaX509ExtensionBasicConstraintsData)
+{
+}
+
+/*!
+ Constructs a X509ExtensionBasicConstraints from \a rhs.
+*/
+QOpcUaX509ExtensionBasicConstraints::QOpcUaX509ExtensionBasicConstraints(const QOpcUaX509ExtensionBasicConstraints &rhs)
+ : QOpcUaX509Extension(rhs.data)
+{
+}
+
+/*!
+ Returns \c true if this X509ExtensionBasicConstraints has the same value as \a rhs.
+*/
+bool QOpcUaX509ExtensionBasicConstraints::operator==(const QOpcUaX509ExtensionBasicConstraints &rhs) const
+{
+ return data->critical == rhs.data->critical;
+}
+
+/*!
+ Destructs a X509ExtensionBasicConstraints.
+*/
+QOpcUaX509ExtensionBasicConstraints::~QOpcUaX509ExtensionBasicConstraints()
+{
+}
+
+/*!
+ Sets the values from \a rhs in this X509ExtensionBasicConstraints.
+*/
+QOpcUaX509ExtensionBasicConstraints &QOpcUaX509ExtensionBasicConstraints::operator=(const QOpcUaX509ExtensionBasicConstraints &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Sets the flag, if the certificate's subject is a CA to \a value.
+*/
+void QOpcUaX509ExtensionBasicConstraints::setCa(bool value)
+{
+ QOpcUaX509ExtensionBasicConstraintsData *d = static_cast<QOpcUaX509ExtensionBasicConstraintsData*>(data.data());
+ d->ca = value;
+}
+
+/*!
+ Returns the flag, if the certificate's subject is a CA.
+*/
+bool QOpcUaX509ExtensionBasicConstraints::ca() const
+{
+ const QOpcUaX509ExtensionBasicConstraintsData *d = static_cast<const QOpcUaX509ExtensionBasicConstraintsData*>(data.data());
+ return d->ca;
+}
+
+/*!
+ Sets the validation path length of the certificate to \a length.
+*/
+void QOpcUaX509ExtensionBasicConstraints::setPathLength(int length)
+{
+ QOpcUaX509ExtensionBasicConstraintsData *d = static_cast<QOpcUaX509ExtensionBasicConstraintsData*>(data.data());
+ d->pathLength = length;
+}
+
+/*!
+ Returns the validation path length of the certificate.
+*/
+int QOpcUaX509ExtensionBasicConstraints::pathLength() const
+{
+ const QOpcUaX509ExtensionBasicConstraintsData *d = static_cast<const QOpcUaX509ExtensionBasicConstraintsData*>(data.data());
+ return d->pathLength;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509extensionbasicconstraints.h b/src/opcua/x509/qopcuax509extensionbasicconstraints.h
new file mode 100644
index 0000000..eccbcd8
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionbasicconstraints.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509EXTENSIONBASICCONSTRAINTS_H
+#define QOPCUAX509EXTENSIONBASICCONSTRAINTS_H
+
+#include "QtOpcUa/qopcuax509extension.h"
+#include <QtOpcUa/qopcuaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaX509ExtensionBasicConstraintsData;
+
+class Q_OPCUA_EXPORT QOpcUaX509ExtensionBasicConstraints : public QOpcUaX509Extension
+{
+public:
+ QOpcUaX509ExtensionBasicConstraints();
+ QOpcUaX509ExtensionBasicConstraints(const QOpcUaX509ExtensionBasicConstraints &);
+ QOpcUaX509ExtensionBasicConstraints &operator=(const QOpcUaX509ExtensionBasicConstraints &);
+ bool operator==(const QOpcUaX509ExtensionBasicConstraints &rhs) const;
+ ~QOpcUaX509ExtensionBasicConstraints();
+
+ void setCa(bool value);
+ bool ca() const;
+
+ void setPathLength(int length);
+ int pathLength() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509EXTENSIONBASICCONSTRAINTS_H
diff --git a/src/opcua/x509/qopcuax509extensionextendedkeyusage.cpp b/src/opcua/x509/qopcuax509extensionextendedkeyusage.cpp
new file mode 100644
index 0000000..cbe85d4
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionextendedkeyusage.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509extensionextendedkeyusage.h"
+#include "qopcuax509extension_p.h"
+#include <QSet>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509ExtensionExtendedKeyUsage
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Class for X509 extended key usage
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+*/
+
+/*!
+ \enum QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage
+
+ Enum with entry types for X509ExtensionExtendedKeyUsage.
+
+ \value TlsWebServerAuthentication
+ Permits TLS webserver Authentication
+ \value TlsWebClientAuthentication
+ Permits TLS client authentication
+ \value SignExecutableCode
+ Permits signature of executable code
+ \value EmailProtection
+ Permits signing emails
+*/
+
+class QOpcUaX509ExtensionExtendedKeyUsageData : public QOpcUaX509ExtensionData
+{
+public:
+ QSet<QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage> keyUsage;
+};
+
+/*!
+ Constructs a X509ExtensionExtendedKeyUsage.
+*/
+QOpcUaX509ExtensionExtendedKeyUsage::QOpcUaX509ExtensionExtendedKeyUsage()
+ : QOpcUaX509Extension(new QOpcUaX509ExtensionExtendedKeyUsageData)
+{
+
+}
+
+/*!
+ Constructs a X509ExtensionExtendedKeyUsage from \a rhs.
+*/
+QOpcUaX509ExtensionExtendedKeyUsage::QOpcUaX509ExtensionExtendedKeyUsage(const QOpcUaX509ExtensionExtendedKeyUsage &rhs)
+ : QOpcUaX509Extension(rhs.data)
+{
+}
+
+/*!
+ Returns \c true if this X509ExtensionExtendedKeyUsage has the same value as \a rhs.
+*/
+bool QOpcUaX509ExtensionExtendedKeyUsage::operator==(const QOpcUaX509ExtensionExtendedKeyUsage &rhs) const
+{
+ return data->critical == rhs.data->critical;
+}
+
+/*!
+ Destructs a X509ExtensionExtendedKeyUsage.
+*/
+QOpcUaX509ExtensionExtendedKeyUsage::~QOpcUaX509ExtensionExtendedKeyUsage()
+{
+}
+
+/*!
+ Sets the values from \a rhs in this X509ExtensionExtendedKeyUsage.
+*/
+QOpcUaX509ExtensionExtendedKeyUsage &QOpcUaX509ExtensionExtendedKeyUsage::operator=(const QOpcUaX509ExtensionExtendedKeyUsage &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Sets the key usage flag in \a keyUsage to \a enable.
+*/
+void QOpcUaX509ExtensionExtendedKeyUsage::setKeyUsage(KeyUsage keyUsage, bool enable)
+{
+ QOpcUaX509ExtensionExtendedKeyUsageData *d = static_cast<QOpcUaX509ExtensionExtendedKeyUsageData*>(data.data());
+
+ if (enable)
+ d->keyUsage.insert(keyUsage);
+ else
+ d->keyUsage.remove(keyUsage);
+}
+
+/*!
+ Returns the key usage flag for \a keyUsage.
+*/
+bool QOpcUaX509ExtensionExtendedKeyUsage::keyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage keyUsage) const
+{
+ const QOpcUaX509ExtensionExtendedKeyUsageData *d = static_cast<const QOpcUaX509ExtensionExtendedKeyUsageData*>(data.data());
+ return d->keyUsage.contains(keyUsage);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509extensionextendedkeyusage.h b/src/opcua/x509/qopcuax509extensionextendedkeyusage.h
new file mode 100644
index 0000000..7fc7ce2
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionextendedkeyusage.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509EXTENSIONEXTENDEDKEYUSAGE_H
+#define QOPCUAX509EXTENSIONEXTENDEDKEYUSAGE_H
+
+#include "QtOpcUa/qopcuax509extension.h"
+#include <QtOpcUa/qopcuaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_OPCUA_EXPORT QOpcUaX509ExtensionExtendedKeyUsage : public QOpcUaX509Extension
+{
+public:
+ enum class KeyUsage : uint {
+ TlsWebServerAuthentication,
+ TlsWebClientAuthentication,
+ SignExecutableCode,
+ EmailProtection,
+ };
+
+ QOpcUaX509ExtensionExtendedKeyUsage();
+ QOpcUaX509ExtensionExtendedKeyUsage(const QOpcUaX509ExtensionExtendedKeyUsage &);
+ QOpcUaX509ExtensionExtendedKeyUsage &operator=(const QOpcUaX509ExtensionExtendedKeyUsage &);
+ bool operator==(const QOpcUaX509ExtensionExtendedKeyUsage &rhs) const;
+ ~QOpcUaX509ExtensionExtendedKeyUsage();
+
+ void setKeyUsage(KeyUsage keyUsage, bool enable = true);
+ bool keyUsage(KeyUsage) const;
+};
+
+inline uint qHash(const QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage &key)
+{
+ return ::qHash(static_cast<uint>(key));
+}
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509EXTENSIONEXTENDEDKEYUSAGE_H
diff --git a/src/opcua/x509/qopcuax509extensionkeyusage.cpp b/src/opcua/x509/qopcuax509extensionkeyusage.cpp
new file mode 100644
index 0000000..f270899
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionkeyusage.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509extensionkeyusage.h"
+#include "qopcuax509extension_p.h"
+#include <QSet>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509ExtensionKeyUsage
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Class for X509 extended key usage
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+*/
+
+/*!
+ \enum QOpcUaX509ExtensionKeyUsage::KeyUsage
+
+ Enum with entry types for X509ExtensionKeyUsage.
+
+ \value DigitalSignature
+ Permits digital signatures
+ \value NonRepudiation
+ Permits non repudiation
+ \value KeyEncipherment
+ Permits key encipherment
+ \value DataEncipherment
+ Permits data encipherment
+ \value KeyAgreement
+ Permits key agreement
+ \value CertificateSigning
+ Permits certificate signing
+ \value CrlSigning
+ Permits CRL signing
+ \value EnciptherOnly
+ Restricts to encipherment only
+ \value DecipherOnly
+ Restricts to decipher only
+*/
+
+class QOpcUaX509ExtensionKeyUsageData : public QOpcUaX509ExtensionData
+{
+public:
+ QSet<QOpcUaX509ExtensionKeyUsage::KeyUsage> keyUsage;
+};
+
+/*!
+ Constructs a X509ExtensionKeyUsage.
+*/
+QOpcUaX509ExtensionKeyUsage::QOpcUaX509ExtensionKeyUsage()
+ : QOpcUaX509Extension(new QOpcUaX509ExtensionKeyUsageData)
+{
+}
+
+/*!
+ Constructs a X509ExtensionKeyUsage from \a rhs.
+*/
+QOpcUaX509ExtensionKeyUsage::QOpcUaX509ExtensionKeyUsage(const QOpcUaX509ExtensionKeyUsage &rhs)
+ : QOpcUaX509Extension(rhs.data)
+{
+}
+
+/*!
+ Returns \c true if this X509ExtensionKeyUsage has the same value as \a rhs.
+*/
+bool QOpcUaX509ExtensionKeyUsage::operator==(const QOpcUaX509ExtensionKeyUsage &rhs) const
+{
+ return data->critical == rhs.data->critical;
+}
+
+/*!
+ Destructs a X509ExtensionKeyUsage.
+*/
+QOpcUaX509ExtensionKeyUsage::~QOpcUaX509ExtensionKeyUsage()
+{
+}
+
+/*!
+ Sets the values from \a rhs in this X509ExtensionKeyUsage.
+*/
+QOpcUaX509ExtensionKeyUsage &QOpcUaX509ExtensionKeyUsage::operator=(const QOpcUaX509ExtensionKeyUsage &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Sets the key usage flag in \a keyUsage to \a enable.
+*/
+void QOpcUaX509ExtensionKeyUsage::setKeyUsage(KeyUsage keyUsage, bool enable)
+{
+ QOpcUaX509ExtensionKeyUsageData *d = static_cast<QOpcUaX509ExtensionKeyUsageData*>(data.data());
+
+ if (enable)
+ d->keyUsage.insert(keyUsage);
+ else
+ d->keyUsage.remove(keyUsage);
+}
+
+/*!
+ Returns the key usage flag for \a keyUsage.
+*/
+bool QOpcUaX509ExtensionKeyUsage::keyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage keyUsage) const
+{
+ const QOpcUaX509ExtensionKeyUsageData *d = static_cast<const QOpcUaX509ExtensionKeyUsageData*>(data.data());
+ return d->keyUsage.contains(keyUsage);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509extensionkeyusage.h b/src/opcua/x509/qopcuax509extensionkeyusage.h
new file mode 100644
index 0000000..7e0586c
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionkeyusage.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509EXTENSIONKEYUSAGE_H
+#define QOPCUAX509EXTENSIONKEYUSAGE_H
+
+#include "QtOpcUa/qopcuax509extension.h"
+#include <QtOpcUa/qopcuaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_OPCUA_EXPORT QOpcUaX509ExtensionKeyUsage : public QOpcUaX509Extension
+{
+public:
+ enum class KeyUsage : uint {
+ DigitalSignature,
+ NonRepudiation,
+ KeyEncipherment,
+ DataEncipherment,
+ KeyAgreement,
+ CertificateSigning,
+ CrlSigning,
+ EnciptherOnly,
+ DecipherOnly
+ };
+
+ QOpcUaX509ExtensionKeyUsage();
+ QOpcUaX509ExtensionKeyUsage(const QOpcUaX509ExtensionKeyUsage &);
+ QOpcUaX509ExtensionKeyUsage &operator=(const QOpcUaX509ExtensionKeyUsage &);
+ bool operator==(const QOpcUaX509ExtensionKeyUsage &rhs) const;
+ ~QOpcUaX509ExtensionKeyUsage();
+
+ void setKeyUsage(KeyUsage keyUsage, bool enable = true);
+ bool keyUsage(KeyUsage) const;
+};
+
+inline uint qHash(const QOpcUaX509ExtensionKeyUsage::KeyUsage &key)
+{
+ return ::qHash(static_cast<uint>(key));
+}
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509EXTENSIONKEYUSAGE_H
diff --git a/src/opcua/x509/qopcuax509extensionsubjectalternativename.cpp b/src/opcua/x509/qopcuax509extensionsubjectalternativename.cpp
new file mode 100644
index 0000000..86bad2e
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionsubjectalternativename.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopcuax509extensionsubjectalternativename.h"
+#include "qopcuax509extension_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpcUaX509ExtensionSubjectAlternativeName
+ \inmodule QtOpcUa
+ \since 5.14
+
+ \brief Class for an X509 subject alternative name
+
+ This class is currently available as a Technology Preview, and therefore the API
+ and functionality provided by the class may be subject to change at any time without
+ prior notice.
+*/
+
+/*!
+ \enum QOpcUaX509ExtensionSubjectAlternativeName::Type
+
+ Enum with entry types for subject alternative name.
+
+ \value Email
+ Entry type for an email address
+ \value URI
+ Entry type for an URI
+ \value DNS
+ Entry type for DNS
+ \value IP
+ Entry type for an IP address
+*/
+
+class QOpcUaX509ExtensionSubjectAlternativeNameData : public QOpcUaX509ExtensionData
+{
+public:
+ QVector <QPair<QOpcUaX509ExtensionSubjectAlternativeName::Type, QString>> entries;
+};
+
+/*!
+ Constructs a X509ExtensionSubjectAlternativeName.
+*/
+QOpcUaX509ExtensionSubjectAlternativeName::QOpcUaX509ExtensionSubjectAlternativeName()
+ : QOpcUaX509Extension(new QOpcUaX509ExtensionSubjectAlternativeNameData)
+{
+}
+
+/*!
+ Constructs a X509ExtensionSubjectAlternativeName from \a rhs.
+*/
+QOpcUaX509ExtensionSubjectAlternativeName::QOpcUaX509ExtensionSubjectAlternativeName(const QOpcUaX509ExtensionSubjectAlternativeName &rhs)
+ : QOpcUaX509Extension(rhs.data)
+{
+}
+
+/*!
+ Returns \c true if this X509ExtensionSubjectAlternativeName has the same value as \a rhs.
+*/
+bool QOpcUaX509ExtensionSubjectAlternativeName::operator==(const QOpcUaX509ExtensionSubjectAlternativeName &rhs) const
+{
+ return data->critical == rhs.data->critical;
+}
+
+/*!
+ Destructs a X509ExtensionSubjectAlternativeName.
+*/
+QOpcUaX509ExtensionSubjectAlternativeName::~QOpcUaX509ExtensionSubjectAlternativeName()
+{
+}
+
+/*!
+ Sets the values from \a rhs in this X509ExtensionSubjectAlternativeName.
+*/
+QOpcUaX509ExtensionSubjectAlternativeName &QOpcUaX509ExtensionSubjectAlternativeName::operator=(const QOpcUaX509ExtensionSubjectAlternativeName &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Adds an entry of type \a type with content \a value.
+*/
+void QOpcUaX509ExtensionSubjectAlternativeName::addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type type, const QString &value)
+{
+ QOpcUaX509ExtensionSubjectAlternativeNameData *d = static_cast<QOpcUaX509ExtensionSubjectAlternativeNameData*>(data.data());
+ d->entries.append(qMakePair(type, value));
+}
+
+/*!
+ Returns the vector of entries.
+*/
+const QVector<QPair<QOpcUaX509ExtensionSubjectAlternativeName::Type, QString>> &QOpcUaX509ExtensionSubjectAlternativeName::entries() const
+{
+ const QOpcUaX509ExtensionSubjectAlternativeNameData *d = static_cast<const QOpcUaX509ExtensionSubjectAlternativeNameData*>(data.data());
+ return d->entries;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/x509/qopcuax509extensionsubjectalternativename.h b/src/opcua/x509/qopcuax509extensionsubjectalternativename.h
new file mode 100644
index 0000000..4f13117
--- /dev/null
+++ b/src/opcua/x509/qopcuax509extensionsubjectalternativename.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPCUAX509EXTENSIONSUBJECTALTERNATIVENAME_H
+#define QOPCUAX509EXTENSIONSUBJECTALTERNATIVENAME_H
+
+#include "QtOpcUa/qopcuax509extension.h"
+#include <QtOpcUa/qopcuaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+// OID 2.5.29.17
+class Q_OPCUA_EXPORT QOpcUaX509ExtensionSubjectAlternativeName : public QOpcUaX509Extension
+{
+public:
+ enum class Type {
+ Email,
+ URI,
+ DNS,
+ IP
+ };
+
+ QOpcUaX509ExtensionSubjectAlternativeName();
+ QOpcUaX509ExtensionSubjectAlternativeName(const QOpcUaX509ExtensionSubjectAlternativeName &);
+ QOpcUaX509ExtensionSubjectAlternativeName &operator=(const QOpcUaX509ExtensionSubjectAlternativeName &);
+ bool operator==(const QOpcUaX509ExtensionSubjectAlternativeName &rhs) const;
+ ~QOpcUaX509ExtensionSubjectAlternativeName();
+
+ void addEntry(Type type, const QString &value);
+ const QVector<QPair<Type, QString>> &entries() const;
+};
+
+QT_END_NAMESPACE
+#endif // QOPCUAX509EXTENSIONSUBJECTALTERNATIVENAME_H
diff --git a/src/opcua/x509/qopcuax509utils.cpp b/src/opcua/x509/qopcuax509utils.cpp
new file mode 100644
index 0000000..74eb5d4
--- /dev/null
+++ b/src/opcua/x509/qopcuax509utils.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qopcuax509utils_p.h"
+#include "openssl_symbols_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QString getOpenSslError()
+{
+ char errorBuf[1024];
+ errorBuf[0] = 0;
+ q_ERR_error_string_n(q_ERR_get_error(), errorBuf, sizeof(errorBuf));
+ return QString::fromLatin1(errorBuf);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/opcua/x509/qopcuax509utils_p.h b/src/opcua/x509/qopcuax509utils_p.h
new file mode 100644
index 0000000..4f9f00e
--- /dev/null
+++ b/src/opcua/x509/qopcuax509utils_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QOPCUAX509UTILS_H
+#define QOPCUAX509UTILS_H
+
+#include <functional>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T>
+class Deleter
+{
+public:
+ Deleter(T *data, std::function<void(T *value)> f)
+ : m_data(data)
+ , m_function(f)
+ {
+ }
+ ~Deleter()
+ {
+ if (m_data)
+ m_function(m_data);
+ }
+ void release()
+ {
+ m_data = nullptr;
+ m_function = nullptr;
+ }
+private:
+ T *m_data {nullptr};
+ std::function<void(T *attribute)> m_function;
+};
+
+QString getOpenSslError();
+
+QT_END_NAMESPACE
+
+#endif // QOPCUAX509UTILS_H
diff --git a/src/opcua/x509/qsslsocket_openssl11_symbols_p.h b/src/opcua/x509/qsslsocket_openssl11_symbols_p.h
new file mode 100644
index 0000000..cac3999
--- /dev/null
+++ b/src/opcua/x509/qsslsocket_openssl11_symbols_p.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#ifndef QSSLSOCKET_OPENSSL11_SYMBOLS_P_H
+#define QSSLSOCKET_OPENSSL11_SYMBOLS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Note: this file does not have QT_BEGIN_NAMESPACE/QT_END_NAMESPACE, it's done
+// in qsslsocket_openssl_symbols_p.h.
+
+#ifndef OPENSSL_SYMBOLS_P_H
+#error "You are not supposed to use this header file, include openssl_symbols_p.h instead"
+#endif
+
+#include <openssl/x509.h>
+
+const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x);
+
+Q_AUTOTEST_EXPORT BIO *q_BIO_new(const BIO_METHOD *a);
+Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem();
+
+int q_DSA_bits(DSA *a);
+int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
+int q_EVP_PKEY_base_id(EVP_PKEY *a);
+int q_RSA_bits(RSA *a);
+Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a);
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
+Q_AUTOTEST_EXPORT OPENSSL_STACK *q_OPENSSL_sk_new_null();
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a);
+Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
+int q_SSL_session_reused(SSL *a);
+unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op);
+int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
+size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen);
+size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen);
+int q_CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+const SSL_METHOD *q_TLS_method();
+const SSL_METHOD *q_TLS_client_method();
+const SSL_METHOD *q_TLS_server_method();
+ASN1_TIME *q_X509_getm_notBefore(X509 *a);
+ASN1_TIME *q_X509_getm_notAfter(X509 *a);
+
+Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a);
+long q_X509_get_version(X509 *a);
+EVP_PKEY *q_X509_get_pubkey(X509 *a);
+void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb);
+STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
+void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
+int q_DH_bits(DH *dh);
+
+# define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \
+ | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)
+
+#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_OPENSSL_sk_num)(st)
+#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_OPENSSL_sk_value)(st, i)
+
+#define q_OPENSSL_add_all_algorithms_conf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
+ | OPENSSL_INIT_ADD_ALL_DIGESTS \
+ | OPENSSL_INIT_LOAD_CONFIG, NULL)
+#define q_OPENSSL_add_all_algorithms_noconf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
+ | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL)
+
+// We resolve q_sk_ functions, but use q_OPENSSL_sk_ macros in code to reduce
+// the amount of #ifdefs and for confusing developers.
+OPENSSL_STACK *q_sk_new_null();
+#define q_OPENSSL_sk_new_null() q_sk_new_null()
+#define q_sk_X509_EXTENSION_new_null() \
+ ((OPENSSL_STACK *)q_sk_new_null())
+
+void q_sk_push(OPENSSL_STACK *st, void *data);
+
+#define q_sk_X509_EXTENSION_push(st, val) \
+ q_OPENSSL_sk_push((st), (val))
+#define q_sk_X509_EXTENSION_pop_free(st, free_func) \
+ q_OPENSSL_sk_pop_free((st), (free_func))
+
+
+int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
+void q_CRYPTO_free(void *str, const char *file, int line);
+
+long q_OpenSSL_version_num();
+const char *q_OpenSSL_version(int type);
+
+unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
+unsigned long q_SSL_set_options(SSL *s, unsigned long op);
+
+#ifdef TLS1_3_VERSION
+int q_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str);
+#endif
+
+void q_BIO_set_data(BIO *a, void *ptr);
+void *q_BIO_get_data(BIO *a);
+void q_BIO_set_init(BIO *a, int init);
+int q_BIO_get_shutdown(BIO *a);
+void q_BIO_set_shutdown(BIO *a, int shut);
+
+EVP_PKEY_CTX* q_EVP_PKEY_CTX_new_id(int id, ENGINE *e);
+void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
+int q_EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx);
+
+int q_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void *p2);
+#define q_EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
+ q_RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, \
+ EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
+
+int q_EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **pkey);
+
+#define q_SSL_CTX_set_min_proto_version(ctx, version) \
+ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr)
+
+#define q_SSL_CTX_set_max_proto_version(ctx, version) \
+ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr)
+
+#endif
diff --git a/src/opcua/x509/qsslsocket_opensslpre11_symbols_p.h b/src/opcua/x509/qsslsocket_opensslpre11_symbols_p.h
new file mode 100644
index 0000000..ee41ad9
--- /dev/null
+++ b/src/opcua/x509/qsslsocket_opensslpre11_symbols_p.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+
+#ifndef QSSLSOCKET_OPENSSLPRE11_SYMBOLS_P_H
+#define QSSLSOCKET_OPENSSLPRE11_SYMBOLS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Note: this file does not have QT_BEGIN_NAMESPACE/QT_END_NAMESPACE, it's done
+// in qsslsocket_openssl_symbols_p.h.
+
+#ifndef OPENSSL_SYMBOLS_P_H
+#error "You are not supposed to use this header file, include openssl_symbols_p.h instead"
+#endif
+
+unsigned char * q_ASN1_STRING_data(ASN1_STRING *a);
+BIO *q_BIO_new_file(const char *filename, const char *mode);
+void q_ERR_clear_error();
+Q_AUTOTEST_EXPORT BIO *q_BIO_new(BIO_METHOD *a);
+Q_AUTOTEST_EXPORT BIO_METHOD *q_BIO_s_mem();
+int q_CRYPTO_num_locks();
+void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int));
+void q_CRYPTO_set_id_callback(unsigned long (*a)());
+void q_CRYPTO_free(void *a);
+unsigned long q_ERR_peek_last_error();
+void q_ERR_free_strings();
+void q_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);
+void q_EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a);
+
+typedef _STACK STACK;
+
+// The typedef we use to make our pre 1.1 code look more like 1.1 (less ifdefs).
+typedef STACK OPENSSL_STACK;
+
+// We resolve q_sk_ functions, but use q_OPENSSL_sk_ macros in code to reduce
+// the amount of #ifdefs and for confusing developers.
+int q_sk_num(STACK *a);
+#define q_OPENSSL_sk_num(a) q_sk_num(a)
+void q_sk_pop_free(STACK *a, void (*b)(void *));
+#define q_OPENSSL_sk_pop_free(a, b) (a, b)
+STACK *q_sk_new_null();
+#define q_OPENSSL_sk_new_null() q_sk_new_null()
+
+#define q_sk_X509_EXTENSION_new_null() \
+ ((STACK_OF(X509_EXTENSION) *)q_sk_new_null())
+
+#define q_SKM_sk_push(type, st, val) \
+ q_OPENSSL_sk_push(CHECKED_STACK_OF(type, st), CHECKED_PTR_OF(type, val))
+
+#define q_sk_X509_EXTENSION_push(st, val) \
+ q_SKM_sk_push(X509_EXTENSION, (st), (val))
+#define q_sk_X509_EXTENSION_pop_free(st, free_func) \
+ q_OPENSSL_sk_pop_free(CHECKED_STACK_OF(X509_EXTENSION, st), (free_func))
+
+void q_sk_free(STACK *a);
+
+// Just a name alias (not a function call expression) since in code we take an
+// address of this:
+#define q_OPENSSL_sk_free q_sk_free
+
+void *q_sk_value(STACK *a, int b);
+void q_sk_push(STACK *st, void *data);
+
+#define q_OPENSSL_sk_value(a, b) q_sk_value(a, b)
+#define q_OPENSSL_sk_push(st, data) q_sk_push(st, data)
+
+SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
+
+int q_SSL_library_init();
+void q_SSL_load_error_strings();
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+#endif
+
+const SSL_METHOD *q_SSLv23_client_method();
+const SSL_METHOD *q_TLSv1_client_method();
+const SSL_METHOD *q_TLSv1_1_client_method();
+const SSL_METHOD *q_TLSv1_2_client_method();
+const SSL_METHOD *q_SSLv23_server_method();
+const SSL_METHOD *q_TLSv1_server_method();
+const SSL_METHOD *q_TLSv1_1_server_method();
+const SSL_METHOD *q_TLSv1_2_server_method();
+
+STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
+
+#ifdef SSLEAY_MACROS
+int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp);
+int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp);
+RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length);
+DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
+#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \
+ (RSA *)q_PEM_ASN1_read_bio( \
+ (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u)
+#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \
+ (DSA *)q_PEM_ASN1_read_bio( \
+ (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u)
+#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
+#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
+#define q_PEM_read_bio_DHparams(bp, dh, cb, u) \
+ (DH *)q_PEM_ASN1_read_bio( \
+ (void *(*)(void**, const unsigned char**, long int))q_d2i_DHparams, PEM_STRING_DHPARAMS, bp, (void **)x, cb, u)
+#endif // SSLEAY_MACROS
+
+#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
+#define q_SSL_set_options(ssl,op) q_SSL_ctrl((ssl),SSL_CTRL_OPTIONS,(op),nullptr)
+#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st)
+#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i)
+#define q_X509_getm_notAfter(x) X509_get_notAfter(x)
+#define q_X509_getm_notBefore(x) X509_get_notBefore(x)
+
+// "Forward compatibility" with OpenSSL 1.1 (to save on #if-ery elsewhere):
+#define q_X509_get_version(x509) q_ASN1_INTEGER_get((x509)->cert_info->version)
+#define q_ASN1_STRING_get0_data(x) q_ASN1_STRING_data(x)
+#define q_EVP_PKEY_base_id(pkey) ((pkey)->type)
+#define q_X509_get_pubkey(x509) q_X509_PUBKEY_get((x509)->cert_info->key)
+#define q_SSL_SESSION_get_ticket_lifetime_hint(s) ((s)->tlsext_tick_lifetime_hint)
+#define q_RSA_bits(rsa) q_BN_num_bits((rsa)->n)
+#define q_DSA_bits(dsa) q_BN_num_bits((dsa)->p)
+#define q_DH_bits(dsa) q_BN_num_bits((dh)->p)
+#define q_X509_STORE_set_verify_cb(s,c) X509_STORE_set_verify_cb_func((s),(c))
+
+char *q_CONF_get1_default_config_file();
+void q_OPENSSL_add_all_algorithms_noconf();
+void q_OPENSSL_add_all_algorithms_conf();
+
+long q_SSLeay();
+const char *q_SSLeay_version(int type);
+
+#endif // QSSLSOCKET_OPENSSL_PRE11_SYMBOLS_P_H
diff --git a/src/opcua/x509/x509.pri b/src/opcua/x509/x509.pri
new file mode 100644
index 0000000..efcb13c
--- /dev/null
+++ b/src/opcua/x509/x509.pri
@@ -0,0 +1,33 @@
+PUBLIC_HEADERS += \
+ $$PWD/qopcuakeypair.h \
+ $$PWD/qopcuax509certificatesigningrequest.h \
+ $$PWD/qopcuax509distinguishedname.h \
+ $$PWD/qopcuax509extensionextendedkeyusage.h \
+ $$PWD/qopcuax509extension.h \
+ $$PWD/qopcuax509extensionkeyusage.h \
+ $$PWD/qopcuax509extensionsubjectalternativename.h \
+ $$PWD/qopcuax509extensionbasicconstraints.h \
+
+PRIVATE_HEADERS += \
+ $$PWD/qopcuakeypair_p.h \
+ $$PWD/openssl_symbols_p.h \
+ $$PWD/qsslsocket_openssl11_symbols_p.h \
+ $$PWD/qsslsocket_opensslpre11_symbols_p.h \
+ $$PWD/qopcuax509certificatesigningrequest_p.h \
+ $$PWD/qopcuax509utils_p.h \
+ $$PWD/qopcuax509extension_p.h \
+
+SOURCES += \
+ $$PWD/qopcuax509certificatesigningrequest.cpp \
+ $$PWD/qopcuax509distinguishedname.cpp \
+ $$PWD/qopcuax509extensionextendedkeyusage.cpp \
+ $$PWD/qopcuax509extension.cpp \
+ $$PWD/qopcuax509extensionkeyusage.cpp \
+ $$PWD/qopcuax509extensionsubjectalternativename.cpp \
+ $$PWD/qopcuax509extensionbasicconstraints.cpp \
+ $$PWD/openssl_symbols.cpp \
+ $$PWD/qopcuakeypair.cpp \
+ $$PWD/qopcuakeypair_openssl.cpp \
+ $$PWD/qopcuax509certificatesigningrequest_openssl.cpp \
+ $$PWD/qopcuax509utils.cpp \
+
diff --git a/src/plugins/opcua/open62541/qopen62541utils.h b/src/plugins/opcua/open62541/qopen62541utils.h
index fd13219..6990912 100644
--- a/src/plugins/opcua/open62541/qopen62541utils.h
+++ b/src/plugins/opcua/open62541/qopen62541utils.h
@@ -39,7 +39,7 @@
#include "qopen62541.h"
-#include <QtCore/qstring.h>
+#include <QString>
#include <functional>
diff --git a/src/plugins/opcua/uacpp/quacppbackend.cpp b/src/plugins/opcua/uacpp/quacppbackend.cpp
index 1681438..627cceb 100644
--- a/src/plugins/opcua/uacpp/quacppbackend.cpp
+++ b/src/plugins/opcua/uacpp/quacppbackend.cpp
@@ -630,7 +630,7 @@ void UACppAsyncBackend::callMethod(quint64 handle, const UaNodeId &objectId, con
UaStatus status = m_nativeSession->call(settings, in, out);
if (status.isBad())
- qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Calling method failed";
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Calling method" << methodId.toFullString().toUtf8() << "failed";
if (out.callResult.isBad())
qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Method call returned a failure";
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 9d8b7db..82a44df 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,10 +1,11 @@
TEMPLATE = subdirs
SUBDIRS += qopcuaclient connection clientSetupInCpp security
-QT_FOR_CONFIG += opcua-private
+QT_FOR_CONFIG += opcua-private core-private
# only build declarative tests if at least one backend was built
qtHaveModule(qmltest):qtConfig(open62541)|qtConfig(uacpp) {
SUBDIRS += declarative
}
+qtConfig(ssl):!darwin:!winrt: SUBDIRS += x509
diff --git a/tests/auto/x509/tst_x509.cpp b/tests/auto/x509/tst_x509.cpp
new file mode 100644
index 0000000..d00c3d9
--- /dev/null
+++ b/tests/auto/x509/tst_x509.cpp
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpcUa/QOpcUaProvider>
+#include <QtOpcUa/QOpcUaKeyPair>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QScopedPointer>
+#include <QOpcUaX509CertificateSigningRequest>
+#include <QOpcUaX509ExtensionSubjectAlternativeName>
+#include <QOpcUaX509ExtensionBasicConstraints>
+#include <QOpcUaX509ExtensionKeyUsage>
+#include <QOpcUaX509ExtensionExtendedKeyUsage>
+
+#include <QtTest/QSignalSpy>
+#include <QtTest/QtTest>
+
+#define defineDataMethod(name) void name()\
+{\
+ QTest::addColumn<QString>("backend");\
+ for (auto backend : m_backends) {\
+ const QString rowName = QString("%1").arg(backend); \
+ QTest::newRow(rowName.toLatin1().constData()) << backend ; \
+ }\
+}
+
+class Tst_QOpcUaSecurity: public QObject
+{
+ Q_OBJECT
+
+public:
+ Tst_QOpcUaSecurity();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ defineDataMethod(keyPairs_data)
+ void keyPairs();
+
+ defineDataMethod(certificateSigningRequest_data)
+ void certificateSigningRequest();
+
+private:
+ QStringList m_backends;
+ QOpcUaProvider m_opcUa;
+};
+
+QByteArray textifyCertificateRequest(const QByteArray &data)
+{
+ QProcess p;
+ p.start("openssl", QStringList {"req", "-text", "-noout"});
+ p.waitForStarted();
+ p.write(data);
+ p.closeWriteChannel();
+ p.waitForFinished();
+ return p.readAllStandardOutput();
+}
+
+QByteArray textifyCertificate(const QByteArray &data)
+{
+ QProcess p;
+ p.start("openssl", QStringList {"x509", "-text", "-noout"});
+ p.waitForStarted();
+ p.write(data);
+ p.closeWriteChannel();
+ p.waitForFinished();
+ return p.readAllStandardOutput();
+}
+
+QByteArray asn1dump(const QByteArray &data)
+{
+ QProcess p;
+ p.start("openssl", QStringList {"asn1parse", "-inform","PEM"});
+ p.waitForStarted();
+ p.write(data);
+ p.closeWriteChannel();
+ p.waitForFinished();
+ return p.readAllStandardOutput();
+}
+
+Tst_QOpcUaSecurity::Tst_QOpcUaSecurity()
+{
+ m_backends = QOpcUaProvider::availableBackends();
+}
+
+void Tst_QOpcUaSecurity::initTestCase()
+{
+}
+
+void Tst_QOpcUaSecurity::keyPairs()
+{
+ QFETCH(QString, backend);
+
+ QOpcUaKeyPair key;
+ QOpcUaKeyPair loadedKey;
+ QByteArray byteArray;
+
+ QVERIFY(key.hasPrivateKey() == false);
+
+ // Generate key
+ key.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+ QVERIFY(key.hasPrivateKey() == true);
+
+ // Export public key
+ byteArray = key.publicKeyToByteArray();
+ QVERIFY(byteArray.startsWith("-----BEGIN PUBLIC KEY-----\n"));
+ QVERIFY(byteArray.endsWith("-----END PUBLIC KEY-----\n"));
+
+ // Load public key
+ QVERIFY(loadedKey.loadFromPemData(byteArray));
+ QVERIFY(loadedKey.hasPrivateKey() == false);
+
+ // Check unencrypted PEM export
+ byteArray = key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString());
+ QVERIFY(byteArray.startsWith("-----BEGIN PRIVATE KEY-----\n"));
+ QVERIFY(byteArray.endsWith("-----END PRIVATE KEY-----\n"));
+
+ // Load private key from PEM data
+ QSignalSpy passwordSpy(&loadedKey, SIGNAL(passphraseNeeded(QString&,int,bool)));
+
+ QVERIFY(loadedKey.loadFromPemData(byteArray));
+ QVERIFY(loadedKey.hasPrivateKey() == true);
+ QCOMPARE(passwordSpy.count(), 0);
+ QCOMPARE(loadedKey.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString()), byteArray);
+
+ // Check encrypted PEM export
+ byteArray = key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Aes128Cbc, QString("password"));
+ QVERIFY(byteArray.startsWith("-----BEGIN ENCRYPTED PRIVATE KEY-----\n"));
+ QVERIFY(byteArray.endsWith("-----END ENCRYPTED PRIVATE KEY-----\n"));
+ QCOMPARE(passwordSpy.count(), 0);
+
+ // Setup password callback
+ QString passphraseToReturn;
+ connect(&loadedKey, &QOpcUaKeyPair::passphraseNeeded, this, [&passphraseToReturn](QString &passphrase, int maximumLength, bool writeOperation){
+ Q_UNUSED(maximumLength);
+ qDebug() << "Requested a passphrase for" << (writeOperation ? "write":"read") << "operation";
+ passphrase = passphraseToReturn;
+ });
+
+ // Load key with wrong password
+ qDebug() << "Trying to decrypt with wrong password; will cause an error";
+ passphraseToReturn = "WrongPassword";
+ QVERIFY(!loadedKey.loadFromPemData(byteArray));
+ QCOMPARE(passwordSpy.count(), 1);
+ QVERIFY(loadedKey.hasPrivateKey() == false);
+
+ // Load key with right password
+ qDebug() << "Trying to decrypt with right password; will cause no error";
+ passphraseToReturn = "password";
+ QVERIFY(loadedKey.loadFromPemData(byteArray));
+ QCOMPARE(passwordSpy.count(), 2);
+ QCOMPARE(loadedKey.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString()),
+ key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString()));
+ QVERIFY(loadedKey.hasPrivateKey() == true);
+}
+
+void Tst_QOpcUaSecurity::certificateSigningRequest()
+{
+ QFETCH(QString, backend);
+
+ QOpcUaKeyPair key;
+
+ // Generate key
+ key.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+ QVERIFY(key.hasPrivateKey() == true);
+
+ QOpcUaX509CertificateSigningRequest csr;
+
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CommonName, "QtOpcUaViewer");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, "DE");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, "The Qt Company");
+ csr.setSubject(dn);
+
+ QOpcUaX509ExtensionSubjectAlternativeName *san = new QOpcUaX509ExtensionSubjectAlternativeName;
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "foo.com");
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "bla.com");
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::URI, "urn:foo.com:The%20Qt%20Company:QtOpcUaViewer");
+ san->setCritical(true);
+ csr.addExtension(san);
+
+ QOpcUaX509ExtensionBasicConstraints *bc = new QOpcUaX509ExtensionBasicConstraints;
+ bc->setCa(false);
+ bc->setCritical(true);
+ csr.addExtension(bc);
+
+ QOpcUaX509ExtensionKeyUsage *ku = new QOpcUaX509ExtensionKeyUsage;
+ ku->setCritical(true);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DigitalSignature);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::NonRepudiation);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DataEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CertificateSigning);
+ csr.addExtension(ku);
+
+ QOpcUaX509ExtensionExtendedKeyUsage *eku = new QOpcUaX509ExtensionExtendedKeyUsage;
+ eku->setCritical(true);
+ eku->setKeyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::EmailProtection);
+ csr.addExtension(eku);
+
+ QByteArray csrData = csr.createRequest(key);
+ qDebug() << csrData;
+ QVERIFY(csrData.startsWith("-----BEGIN CERTIFICATE REQUEST-----\n"));
+ QVERIFY(csrData.endsWith("\n-----END CERTIFICATE REQUEST-----\n"));
+ qDebug().noquote() << textifyCertificateRequest(csrData);
+ qDebug().noquote() << asn1dump(csrData);
+
+ QByteArray certData = csr.createSelfSignedCertificate(key);
+ qDebug() << certData;
+ QVERIFY(certData.startsWith("-----BEGIN CERTIFICATE-----\n"));
+ QVERIFY(certData.endsWith("\n-----END CERTIFICATE-----\n"));
+ qDebug().noquote() << textifyCertificate(certData);
+ qDebug().noquote() << asn1dump(certData);
+}
+
+void Tst_QOpcUaSecurity::cleanupTestCase()
+{
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QTEST_SET_MAIN_SOURCE_PATH
+
+ // run tests for all available backends
+ QStringList availableBackends = QOpcUaProvider::availableBackends();
+ if (availableBackends.empty()) {
+ qDebug("No OPCUA backends found, skipping tests.");
+ return EXIT_SUCCESS;
+ }
+
+ Tst_QOpcUaSecurity tc;
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_x509.moc"
+
diff --git a/tests/auto/x509/x509.pro b/tests/auto/x509/x509.pro
new file mode 100644
index 0000000..b1f1fc1
--- /dev/null
+++ b/tests/auto/x509/x509.pro
@@ -0,0 +1,8 @@
+TARGET = tst_x509
+
+QT += testlib opcua network
+QT -= gui
+CONFIG += testcase
+
+SOURCES += \
+ tst_x509.cpp
diff --git a/tests/manual/gds/gds.pro b/tests/manual/gds/gds.pro
new file mode 100644
index 0000000..525595f
--- /dev/null
+++ b/tests/manual/gds/gds.pro
@@ -0,0 +1,8 @@
+TARGET = tst_gds
+
+QT += testlib opcua network
+QT -= gui
+CONFIG += testcase
+
+SOURCES += \
+ tst_gds.cpp
diff --git a/tests/manual/gds/tst_gds.cpp b/tests/manual/gds/tst_gds.cpp
new file mode 100644
index 0000000..9e57238
--- /dev/null
+++ b/tests/manual/gds/tst_gds.cpp
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpcUa/QOpcUaProvider>
+#include <QtOpcUa/QOpcUaGdsClient>
+#include <QtOpcUa/QOpcUaApplicationIdentity>
+#include <QtOpcUa/QOpcUaPkiConfiguration>
+#include <QtOpcUa/QOpcUaAuthenticationInformation>
+#include <QtOpcUa/QOpcUaEndpointDescription>
+#include <QtOpcUa/QOpcUaApplicationRecordDataType>
+#include <QtOpcUa/QOpcUaClient>
+#include <QtOpcUa/QOpcUaX509DistinguishedName>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QScopedPointer>
+#include <QHostInfo>
+
+#include <QtTest/QSignalSpy>
+#include <QtTest/QtTest>
+
+#define defineDataMethod(name) void name()\
+{\
+ QTest::addColumn<QString>("backend");\
+ for (auto backend : m_backends) {\
+ const QString rowName = QString("%1").arg(backend); \
+ QTest::newRow(rowName.toLatin1().constData()) << backend ; \
+ QVERIFY(!backend.isEmpty()); \
+ }\
+}
+
+static QOpcUaPkiConfiguration getPkiConfig()
+{
+ QOpcUaPkiConfiguration pkiConfig;
+ const QString pkiDir = QCoreApplication::applicationDirPath() + "/pki";
+
+ pkiConfig.setClientCertificateFile(pkiDir + "/own/certs/application.der");
+ pkiConfig.setPrivateKeyFile(pkiDir + "/own/private/application.pem");
+ pkiConfig.setTrustListDirectory(pkiDir + "/trusted/certs");
+ pkiConfig.setRevocationListDirectory(pkiDir + "/trusted/crl");
+ pkiConfig.setIssuerListDirectory(pkiDir + "/issuers/certs");
+ pkiConfig.setIssuerRevocationListDirectory(pkiDir + "/issuers/crl");
+ return pkiConfig;
+}
+
+static QOpcUaApplicationIdentity getAppIdentity()
+{
+ QOpcUaApplicationIdentity identity;
+
+ const QString applicationUri = QStringLiteral("urn:%1:%2:%3")
+ .arg(QHostInfo::localHostName())
+ .arg(QCoreApplication::organizationName())
+ .arg(QCoreApplication::applicationName());
+ const QString productUri = QStringLiteral("urn:%1:%2")
+ .arg(QCoreApplication::organizationName())
+ .arg(QCoreApplication::applicationName());
+
+ identity.setProductUri(productUri);
+ identity.setApplicationUri(applicationUri);
+ identity.setApplicationName(QCoreApplication::applicationName());
+ identity.setApplicationType(QOpcUaApplicationDescription::Client);
+ return identity;
+}
+
+static bool removeSettingsFile()
+{
+ QSettings settings;
+ QFile file(settings.fileName());
+ if (file.exists())
+ return file.remove();
+ return true;
+}
+
+static void provideCredentials(QOpcUaAuthenticationInformation &authInfo)
+{
+ qDebug() << "Authentication credentials provided";
+ authInfo.setUsernameAuthentication("root", "secret");
+}
+
+static void commonGdsClientSetup(QOpcUaGdsClient &gc, const QString &backend, const QOpcUaEndpointDescription endpoint)
+{
+ QObject::connect(&gc, &QOpcUaGdsClient::authenticationRequired, provideCredentials);
+
+ gc.setBackend(backend);
+ gc.setEndpoint(endpoint);
+ gc.setApplicationIdentity(getAppIdentity());
+ gc.setPkiConfiguration(getPkiConfig());
+
+ QOpcUaApplicationRecordDataType ar = gc.applicationRecord();
+ ar.setApplicationNames(QVector<QOpcUaLocalizedText>{QOpcUaLocalizedText("en", gc.applicationIdentity().applicationName())});
+ ar.setApplicationType(gc.applicationIdentity().applicationType());
+ ar.setApplicationUri(gc.applicationIdentity().applicationUri());
+ ar.setProductUri(gc.applicationIdentity().productUri());
+ ar.setDiscoveryUrls(QVector<QString>{QLatin1String("opc.tcp://localhost")});
+ gc.setApplicationRecord(ar);
+
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, QLatin1String("DE"));
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, QLatin1String("Berlin"));
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, QLatin1String("Berlin"));
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, QLatin1String("The Qt Company"));
+ gc.setCertificateSigningRequestPresets(dn, QLatin1String("foo.com"));
+}
+
+// The tests have are depending on each other and have to be executed in order.
+
+class Tst_QOpcUaGds: public QObject
+{
+ Q_OBJECT
+
+public:
+ Tst_QOpcUaGds();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ defineDataMethod(registerApplication_data)
+ void registerApplication();
+ defineDataMethod(reuseApplicationId_data)
+ void reuseApplicationId();
+ defineDataMethod(reuseRegisteredUri_data)
+ void reuseRegisteredUri();
+ defineDataMethod(serverForgotRegistration_data)
+ void serverForgotRegistration();
+private:
+ QStringList m_backends;
+ QOpcUaEndpointDescription m_endpoint;
+};
+
+Tst_QOpcUaGds::Tst_QOpcUaGds()
+{
+ m_backends = QOpcUaProvider::availableBackends();
+}
+
+void Tst_QOpcUaGds::initTestCase()
+{
+ QOpcUaEndpointDescription endpoint;
+ QOpcUaProvider provider;
+
+ QScopedPointer<QOpcUaClient> client(provider.createClient("open62541"));
+ QVERIFY(!client.isNull());
+ QSignalSpy endpointSpy(client.data(), &QOpcUaClient::endpointsRequestFinished);
+ client->requestEndpoints(QUrl("opc.tcp://172.17.0.1:48060"));
+ endpointSpy.wait(2000);
+ QCOMPARE(endpointSpy.size(), 1);
+
+ const QVector<QOpcUaEndpointDescription> desc = endpointSpy.at(0).at(0).value<QVector<QOpcUaEndpointDescription>>();
+ QVERIFY(desc.size() > 0);
+
+ for (const auto &i : qAsConst(desc)) {
+ if (i.securityPolicy().endsWith("#Basic256Sha256")
+ && i.securityMode() == QOpcUaEndpointDescription::MessageSecurityMode::SignAndEncrypt)
+ m_endpoint = i;
+ }
+
+ QVERIFY(m_endpoint.securityPolicy().endsWith("#Basic256Sha256"));
+}
+
+void Tst_QOpcUaGds::registerApplication()
+{
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ QVERIFY(removeSettingsFile());
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+ QSignalSpy certificateStatusSpy(&gc, &QOpcUaGdsClient::certificateUpdateRequired);
+ QSignalSpy certificateUpdatedSpy(&gc, &QOpcUaGdsClient::certificateUpdated);
+ QSignalSpy trustListUpdatedSpy(&gc, &QOpcUaGdsClient::trustListUpdated);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+
+ if (certificateStatusSpy.isEmpty())
+ certificateStatusSpy.wait();
+
+ if (certificateUpdatedSpy.isEmpty())
+ certificateUpdatedSpy.wait();
+
+ if (trustListUpdatedSpy.isEmpty())
+ trustListUpdatedSpy.wait();
+
+ QVERIFY(!certificateStatusSpy.isEmpty());
+ QVERIFY(!certificateUpdatedSpy.isEmpty());
+ QVERIFY(!trustListUpdatedSpy.isEmpty());
+}
+
+void Tst_QOpcUaGds::reuseApplicationId()
+{
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ // Keep settings file in order to reuse the existing registration
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+}
+
+void Tst_QOpcUaGds::reuseRegisteredUri()
+{
+ // This test assumes that a registratin at the server is still present.
+ // It will remove the information locally and has to reuse the
+ // registration from the server.
+
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ // Remove the registration data locally; the server will keep it.
+ QSettings settings("Unknown Organization", "tst_gds");
+
+ QString applicationId = settings.value(QLatin1String("gds/applicationId")).toString();
+ settings.remove(QLatin1String("gds/applicationId"));
+ settings.sync();
+
+ QVERIFY(removeSettingsFile());
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+ QVERIFY(gc.applicationId().size() > 10);
+ if (!applicationId.isEmpty())
+ QVERIFY(gc.applicationId() == applicationId);
+}
+
+void Tst_QOpcUaGds::serverForgotRegistration()
+{
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ // Setting an invalid application ID simulates a previous registration which has been
+ // forgotten by the server
+ QSettings settings("Unknown Organization", "tst_gds");
+ settings.setValue(QLatin1String("gds/applicationId"), "ns=42;i=1111111");
+ settings.sync();
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+}
+
+void Tst_QOpcUaGds::cleanupTestCase()
+{
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QTEST_SET_MAIN_SOURCE_PATH
+
+ if (QOpcUaProvider::availableBackends().empty()) {
+ qDebug("No OPCUA backends found, skipping tests.");
+ return EXIT_SUCCESS;
+ }
+
+ Tst_QOpcUaGds tc;
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_gds.moc"
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 3ee3e86..6007738 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -1,3 +1,6 @@
TEMPLATE = subdirs
SUBDIRS += eventsubscription
+
+QT_FOR_CONFIG += opcua-private core-private
+qtConfig(ssl):!darwin:!winrt: SUBDIRS += gds
diff --git a/tests/tests.pro b/tests/tests.pro
index e7b0667..5719b55 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -1,6 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS += auto \
- manual
+SUBDIRS += auto
QT_FOR_CONFIG += opcua-private