summaryrefslogtreecommitdiffstats
path: root/tests/auto/x509/tst_x509.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/x509/tst_x509.cpp')
-rw-r--r--tests/auto/x509/tst_x509.cpp274
1 files changed, 274 insertions, 0 deletions
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"
+