diff options
-rw-r--r-- | src/SelfSignedCertificate.cpp | 280 | ||||
-rw-r--r-- | src/SelfSignedCertificate.h | 22 | ||||
-rw-r--r-- | src/mumble.pri | 4 | ||||
-rw-r--r-- | src/mumble/Cert.cpp | 60 | ||||
-rw-r--r-- | src/murmur/Cert.cpp | 226 | ||||
-rw-r--r-- | src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.cpp | 44 | ||||
-rw-r--r-- | src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.pro | 13 | ||||
-rw-r--r-- | src/tests/tests.pro | 3 |
8 files changed, 371 insertions, 281 deletions
diff --git a/src/SelfSignedCertificate.cpp b/src/SelfSignedCertificate.cpp new file mode 100644 index 000000000..c6fa9c3cd --- /dev/null +++ b/src/SelfSignedCertificate.cpp @@ -0,0 +1,280 @@ +// Copyright 2005-2017 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at <https://www.mumble.info/LICENSE>. + +#include "murmur_pch.h" + +#include "SelfSignedCertificate.h" + +#define SSL_STRING(x) QString::fromLatin1(x).toUtf8().data() + +static int add_ext(X509 * crt, int nid, char *value) { + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0); + + X509_EXTENSION *ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); + if (ex == NULL) { + return 0; + } + + if (X509_add_ext(crt, ex, -1) == 0) { + X509_EXTENSION_free(ex); + return 0; + } + + X509_EXTENSION_free(ex); + return 1; +} + +bool SelfSignedCertificate::generate(QString clientCertName, QString clientCertEmail, QSslCertificate &qscCert, QSslKey &qskKey) { + bool ok = true; + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + BIGNUM *e = NULL; + X509_NAME *name = NULL; + ASN1_INTEGER *serialNumber = NULL; + ASN1_TIME *notBefore = NULL; + ASN1_TIME *notAfter = NULL; + QString commonName; + bool isServerCert = clientCertName.isEmpty() && clientCertEmail.isEmpty(); + + if (CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON) == -1) { + ok = false; + goto out; + } + + x509 = X509_new(); + if (x509 == NULL) { + ok = false; + goto out; + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + ok = false; + goto out; + } + + rsa = RSA_new(); + if (rsa == NULL) { + ok = false; + goto out; + } + + e = BN_new(); + if (e == NULL) { + ok = false; + goto out; + } + if (BN_set_word(e, 65537) == 0) { + ok = false; + goto out; + } + + if (RSA_generate_key_ex(rsa, 2048, e, NULL) == 0) { + ok = false; + goto out; + } + + if (EVP_PKEY_assign_RSA(pkey, rsa) == 0) { + ok = false; + goto out; + } + + if (X509_set_version(x509, 2) == 0) { + ok = false; + goto out; + } + + serialNumber = X509_get_serialNumber(x509); + if (serialNumber == NULL) { + ok = false; + goto out; + } + if (ASN1_INTEGER_set(serialNumber, 1) == 0) { + ok = false; + goto out; + } + + notBefore = X509_get_notBefore(x509); + if (notBefore == NULL) { + ok = false; + goto out; + } + if (X509_gmtime_adj(notBefore, 0) == NULL) { + ok = false; + goto out; + } + + notAfter = X509_get_notAfter(x509); + if (notAfter == NULL) { + ok = false; + goto out; + } + if (X509_gmtime_adj(notAfter, 60*60*24*365*20) == NULL) { + ok = false; + goto out; + } + + if (X509_set_pubkey(x509, pkey) == 0) { + ok = false; + goto out; + } + + name = X509_get_subject_name(x509); + if (name == NULL) { + ok = false; + goto out; + } + + if (isServerCert) { + commonName = QLatin1String("Murmur Autogenerated Certificate v2"); + } else { + if (!clientCertName.isEmpty()) { + commonName = clientCertName; + } else { + commonName = QLatin1String("Mumble User"); + } + } + + if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast<unsigned char *>(commonName.toUtf8().data()), -1, -1, 0) == 0) { + ok = false; + goto out; + } + + if (X509_set_issuer_name(x509, name) == 0) { + ok = false; + goto out; + } + + if (add_ext(x509, NID_basic_constraints, SSL_STRING("critical,CA:FALSE")) == 0) { + ok = false; + goto out; + } + + if (isServerCert) { + if (add_ext(x509, NID_ext_key_usage, SSL_STRING("serverAuth,clientAuth")) == 0) { + ok = false; + goto out; + } + } else { + if (add_ext(x509, NID_ext_key_usage, SSL_STRING("clientAuth")) == 0) { + ok = false; + goto out; + } + } + + if (add_ext(x509, NID_subject_key_identifier, SSL_STRING("hash")) == 0) { + ok = false; + goto out; + } + + if (isServerCert) { + if (add_ext(x509, NID_netscape_comment, SSL_STRING("Generated from murmur")) == 0) { + ok = false; + goto out; + } + } else { + if (add_ext(x509, NID_netscape_comment, SSL_STRING("Generated by Mumble")) == 0) { + ok = false; + goto out; + } + } + + if (!isServerCert) { + if (add_ext(x509, NID_subject_alt_name, QString::fromLatin1("email:%1").arg(clientCertEmail).toUtf8().data()) == 0) { + ok = false; + goto out; + } + } + + if (X509_sign(x509, pkey, EVP_sha1()) == 0) { + ok = false; + goto out; + } + + { + QByteArray crt; + int len = i2d_X509(x509, NULL); + if (len <= 0) { + ok = false; + goto out; + } + crt.resize(len); + + unsigned char *dptr = reinterpret_cast<unsigned char *>(crt.data()); + if (i2d_X509(x509, &dptr) != len) { + ok = false; + goto out; + } + + qscCert = QSslCertificate(crt, QSsl::Der); + if (qscCert.isNull()) { + ok = false; + } + } + + { + QByteArray key; + int len = i2d_PrivateKey(pkey, NULL); + if (len <= 0) { + ok = false; + goto out; + } + key.resize(len); + + unsigned char *dptr = reinterpret_cast<unsigned char *>(key.data()); + if (i2d_PrivateKey(pkey, &dptr) != len) { + ok = false; + goto out; + } + + qskKey = QSslKey(key, QSsl::Rsa, QSsl::Der); + if (qskKey.isNull()) { + ok = false; + } + } + +out: + if (e) { + BN_free(e); + } + // We only need to free the pkey pointer, + // not the RSA pointer. We have assigned + // our RSA key to pkey, and it will be freed + // once we free pkey. + if (pkey) { + EVP_PKEY_free(pkey); + } + if (x509) { + X509_free(x509); + } + + if (!ok) { + qscCert = QSslCertificate(); + qskKey = QSslKey(); + } + + return ok; +} + +bool SelfSignedCertificate::generateMumbleCertificate(QString name, QString email, QSslCertificate &qscCert, QSslKey &qskKey) { + if (name.trimmed().isEmpty()) { + qscCert = QSslCertificate(); + qskKey = QSslKey(); + return false; + } + if (email.trimmed().isEmpty()) { + qscCert = QSslCertificate(); + qskKey = QSslKey(); + return false; + } + return SelfSignedCertificate::generate(name, email, qscCert, qskKey); +} + +bool SelfSignedCertificate::generateMurmurV2Certificate(QSslCertificate &qscCert, QSslKey &qskKey) { + return SelfSignedCertificate::generate(QString(), QString(), qscCert, qskKey); +}
\ No newline at end of file diff --git a/src/SelfSignedCertificate.h b/src/SelfSignedCertificate.h new file mode 100644 index 000000000..a62cf472e --- /dev/null +++ b/src/SelfSignedCertificate.h @@ -0,0 +1,22 @@ +// Copyright 2005-2017 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at <https://www.mumble.info/LICENSE>. + +#ifndef MUMBLE_SELFSIGNEDCERTIFICATE_H_ +#define MUMBLE_SELFSIGNEDCERTIFICATE_H_ + +#include <QtCore/QString> +#include <QtNetwork/QSslCertificate> +#include <QtNetwork/QSslKey> + +class SelfSignedCertificate { +private: + static bool generate(QString clientCertName, QString clientCertEmail, QSslCertificate &qscCert, QSslKey &qskKey); + +public: + static bool generateMumbleCertificate(QString name, QString email, QSslCertificate &qscCert, QSslKey &qskKey); + static bool generateMurmurV2Certificate(QSslCertificate &qscCert, QSslKey &qskKey); +}; + +#endif diff --git a/src/mumble.pri b/src/mumble.pri index f0c96319e..4ee2f9a3a 100644 --- a/src/mumble.pri +++ b/src/mumble.pri @@ -14,8 +14,8 @@ CONFIG += qt thread debug_and_release warn_on DEFINES *= MUMBLE_VERSION_STRING=$$VERSION INCLUDEPATH += $$PWD . ../mumble_proto VPATH += $$PWD -HEADERS *= ACL.h Channel.h CryptState.h Connection.h Group.h HTMLFilter.h User.h Net.h OSInfo.h Timer.h SSL.h Version.h SSLCipherInfo.h SSLCipherInfoTable.h licenses.h License.h LogEmitter.h CryptographicHash.h CryptographicRandom.h PasswordGenerator.h ByteSwap.h HostAddress.cpp Ban.h EnvUtils.h UnresolvedServerAddress.h ServerAddress.h ServerResolver.h ServerResolverRecord.h -SOURCES *= ACL.cpp Group.cpp Channel.cpp Connection.cpp HTMLFilter.cpp User.cpp Timer.cpp CryptState.cpp OSInfo.cpp SSL.cpp Version.cpp SSLCipherInfo.cpp License.cpp LogEmitter.cpp CryptographicHash.cpp CryptographicRandom.cpp PasswordGenerator.cpp HostAddress.cpp Ban.cpp EnvUtils.cpp UnresolvedServerAddress.cpp ServerAddress.cpp ServerResolver_qt5.cpp ServerResolverRecord.cpp +HEADERS *= ACL.h Channel.h CryptState.h Connection.h Group.h HTMLFilter.h User.h Net.h OSInfo.h Timer.h SSL.h Version.h SSLCipherInfo.h SSLCipherInfoTable.h licenses.h License.h LogEmitter.h CryptographicHash.h CryptographicRandom.h PasswordGenerator.h ByteSwap.h HostAddress.cpp Ban.h EnvUtils.h UnresolvedServerAddress.h ServerAddress.h ServerResolver.h ServerResolverRecord.h SelfSignedCertificate.h +SOURCES *= ACL.cpp Group.cpp Channel.cpp Connection.cpp HTMLFilter.cpp User.cpp Timer.cpp CryptState.cpp OSInfo.cpp SSL.cpp Version.cpp SSLCipherInfo.cpp License.cpp LogEmitter.cpp CryptographicHash.cpp CryptographicRandom.cpp PasswordGenerator.cpp HostAddress.cpp Ban.cpp EnvUtils.cpp UnresolvedServerAddress.cpp ServerAddress.cpp ServerResolver_qt5.cpp ServerResolverRecord.cpp SelfSignedCertificate.cpp LIBS *= -lmumble_proto equals(QT_MAJOR_VERSION, 4) { diff --git a/src/mumble/Cert.cpp b/src/mumble/Cert.cpp index 30e1abdc1..2c5ecd313 100644 --- a/src/mumble/Cert.cpp +++ b/src/mumble/Cert.cpp @@ -16,6 +16,7 @@ #include "Cert.h" #include "Global.h" +#include "SelfSignedCertificate.h" #define SSL_STRING(x) QString::fromLatin1(x).toUtf8().data() @@ -351,20 +352,6 @@ void CertWizard::on_qlIntroText_linkActivated(const QString &url) { QDesktopServices::openUrl(QUrl(url)); } -static int add_ext(X509 * crt, int nid, char *value) { - X509_EXTENSION *ex; - X509V3_CTX ctx; - X509V3_set_ctx_nodb(&ctx); - X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0); - ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); - if (!ex) - return 0; - - X509_add_ext(crt, ex, -1); - X509_EXTENSION_free(ex); - return 1; -} - bool CertWizard::validateCert(const Settings::KeyPair &kp) { bool valid = ! kp.second.isNull() && ! kp.first.isEmpty(); foreach(const QSslCertificate &cert, kp.first) @@ -373,47 +360,12 @@ bool CertWizard::validateCert(const Settings::KeyPair &kp) { } Settings::KeyPair CertWizard::generateNewCert(QString qsname, const QString &qsemail) { - CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); - - X509 *x509 = X509_new(); - EVP_PKEY *pkey = EVP_PKEY_new(); - RSA *rsa = RSA_generate_key(2048,RSA_F4,NULL,NULL); - EVP_PKEY_assign_RSA(pkey, rsa); - - X509_set_version(x509, 2); - ASN1_INTEGER_set(X509_get_serialNumber(x509),1); - X509_gmtime_adj(X509_get_notBefore(x509),0); - X509_gmtime_adj(X509_get_notAfter(x509),60*60*24*365*20); - X509_set_pubkey(x509, pkey); - - X509_NAME *name=X509_get_subject_name(x509); - - if (qsname.isEmpty()) - qsname = tr("Mumble User"); - - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast<unsigned char *>(qsname.toUtf8().data()), -1, -1, 0); - X509_set_issuer_name(x509, name); - add_ext(x509, NID_basic_constraints, SSL_STRING("critical,CA:FALSE")); - add_ext(x509, NID_ext_key_usage, SSL_STRING("clientAuth")); - add_ext(x509, NID_subject_key_identifier, SSL_STRING("hash")); - add_ext(x509, NID_netscape_comment, SSL_STRING("Generated by Mumble")); - add_ext(x509, NID_subject_alt_name, QString::fromLatin1("email:%1").arg(qsemail).toUtf8().data()); - - X509_sign(x509, pkey, EVP_sha1()); - - QByteArray crt, key; - - crt.resize(i2d_X509(x509, NULL)); - unsigned char *dptr=reinterpret_cast<unsigned char *>(crt.data()); - i2d_X509(x509, &dptr); - - QSslCertificate qscCert = QSslCertificate(crt, QSsl::Der); - - key.resize(i2d_PrivateKey(pkey, NULL)); - dptr=reinterpret_cast<unsigned char *>(key.data()); - i2d_PrivateKey(pkey, &dptr); + QSslCertificate qscCert; + QSslKey qskKey; - QSslKey qskKey = QSslKey(key, QSsl::Rsa, QSsl::Der); + // Ignore return value. + // The method sets qscCert and qskKey to null values if it fails. + SelfSignedCertificate::generateMumbleCertificate(qsname, qsemail, qscCert, qskKey); QList<QSslCertificate> qlCert; qlCert << qscCert; diff --git a/src/murmur/Cert.cpp b/src/murmur/Cert.cpp index d860c1caa..167013627 100644 --- a/src/murmur/Cert.cpp +++ b/src/murmur/Cert.cpp @@ -7,229 +7,7 @@ #include "Meta.h" #include "Server.h" - -#define SSL_STRING(x) QString::fromLatin1(x).toUtf8().data() - -static int add_ext(X509 * crt, int nid, char *value) { - X509V3_CTX ctx; - X509V3_set_ctx_nodb(&ctx); - X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0); - - X509_EXTENSION *ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); - if (ex == NULL) { - return 0; - } - - if (X509_add_ext(crt, ex, -1) == 0) { - X509_EXTENSION_free(ex); - return 0; - } - - X509_EXTENSION_free(ex); - return 1; -} - -static bool selfSignedServerCert_SHA1_RSA_2048(QSslCertificate &qscCert, QSslKey &qskKey) { - bool ok = true; - X509 *x509 = NULL; - EVP_PKEY *pkey = NULL; - RSA *rsa = NULL; - BIGNUM *e = NULL; - X509_NAME *name = NULL; - ASN1_INTEGER *serialNumber = NULL; - ASN1_TIME *notBefore = NULL; - ASN1_TIME *notAfter = NULL; - unsigned char *commonName = NULL; - - if (CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON) == -1) { - ok = false; - goto out; - } - - x509 = X509_new(); - if (x509 == NULL) { - ok = false; - goto out; - } - - pkey = EVP_PKEY_new(); - if (pkey == NULL) { - ok = false; - goto out; - } - - rsa = RSA_new(); - if (rsa == NULL) { - ok = false; - goto out; - } - - e = BN_new(); - if (e == NULL) { - ok = false; - goto out; - } - if (BN_set_word(e, 65537) == 0) { - ok = false; - goto out; - } - - if (RSA_generate_key_ex(rsa, 2048, e, NULL) == 0) { - ok = false; - goto out; - } - - if (EVP_PKEY_assign_RSA(pkey, rsa) == 0) { - ok = false; - goto out; - } - - if (X509_set_version(x509, 2) == 0) { - ok = false; - goto out; - } - - serialNumber = X509_get_serialNumber(x509); - if (serialNumber == NULL) { - ok = false; - goto out; - } - if (ASN1_INTEGER_set(serialNumber, 1) == 0) { - ok = false; - goto out; - } - - notBefore = X509_get_notBefore(x509); - if (notBefore == NULL) { - ok = false; - goto out; - } - if (X509_gmtime_adj(notBefore, 0) == NULL) { - ok = false; - goto out; - } - - notAfter = X509_get_notAfter(x509); - if (notAfter == NULL) { - ok = false; - goto out; - } - if (X509_gmtime_adj(notAfter, 60*60*24*365*20) == NULL) { - ok = false; - goto out; - } - - if (X509_set_pubkey(x509, pkey) == 0) { - ok = false; - goto out; - } - - name = X509_get_subject_name(x509); - if (name == NULL) { - ok = false; - goto out; - } - - commonName = reinterpret_cast<unsigned char *>(const_cast<char *>("Murmur Autogenerated Certificate v2")); - if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, commonName, -1, -1, 0) == 0) { - ok = false; - goto out; - } - - if (X509_set_issuer_name(x509, name) == 0) { - ok = false; - goto out; - } - - if (add_ext(x509, NID_basic_constraints, SSL_STRING("critical,CA:FALSE")) == 0) { - ok = false; - goto out; - } - - if (add_ext(x509, NID_ext_key_usage, SSL_STRING("serverAuth,clientAuth")) == 0) { - ok = false; - goto out; - } - - if (add_ext(x509, NID_subject_key_identifier, SSL_STRING("hash")) == 0) { - ok = false; - goto out; - } - - if (add_ext(x509, NID_netscape_comment, SSL_STRING("Generated from murmur")) == 0) { - ok = false; - goto out; - } - - if (X509_sign(x509, pkey, EVP_sha1()) == 0) { - ok = false; - goto out; - } - - { - QByteArray crt; - int len = i2d_X509(x509, NULL); - if (len <= 0) { - ok = false; - goto out; - } - crt.resize(len); - - unsigned char *dptr = reinterpret_cast<unsigned char *>(crt.data()); - if (i2d_X509(x509, &dptr) != len) { - ok = false; - goto out; - } - - qscCert = QSslCertificate(crt, QSsl::Der); - if (qscCert.isNull()) { - ok = false; - } - } - - { - QByteArray key; - int len = i2d_PrivateKey(pkey, NULL); - if (len <= 0) { - ok = false; - goto out; - } - key.resize(len); - - unsigned char *dptr = reinterpret_cast<unsigned char *>(key.data()); - if (i2d_PrivateKey(pkey, &dptr) != len) { - ok = false; - goto out; - } - - qskKey = QSslKey(key, QSsl::Rsa, QSsl::Der); - if (qskKey.isNull()) { - ok = false; - } - } - -out: - if (e) { - BN_free(e); - } - // We only need to free the pkey pointer, - // not the RSA pointer. We have assigned - // our RSA key to pkey, and it will be freed - // once we free pkey. - if (pkey) { - EVP_PKEY_free(pkey); - } - if (x509) { - X509_free(x509); - } - - if (!ok) { - qscCert = QSslCertificate(); - qskKey = QSslKey(); - } - - return ok; -} +#include "SelfSignedCertificate.h" #if defined(USE_QSSLDIFFIEHELLMANPARAMETERS) static BN_GENCB *mumble_BN_GENCB_new() { @@ -415,7 +193,7 @@ void Server::initializeCert() { if (qscCert.isNull() || qskKey.isNull()) { log("Generating new server certificate."); - if (!selfSignedServerCert_SHA1_RSA_2048(qscCert, qskKey)) { + if (!SelfSignedCertificate::generateMurmurV2Certificate(qscCert, qskKey)) { log("Certificate or key generation failed"); } diff --git a/src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.cpp b/src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.cpp new file mode 100644 index 000000000..814474366 --- /dev/null +++ b/src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.cpp @@ -0,0 +1,44 @@ +// Copyright 2005-2017 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at <https://www.mumble.info/LICENSE>. + +#include <QtCore> +#include <QtTest> + +#include "SelfSignedCertificate.h" + +class TestSelfSignedCertificate : public QObject { + Q_OBJECT + private slots: + void exerciseClientCert(); + void exerciseServerCert(); +}; + +void TestSelfSignedCertificate::exerciseClientCert() { + QSslCertificate cert; + QSslKey key; + + bool ok = SelfSignedCertificate::generateMumbleCertificate(QLatin1String("Test"), QLatin1String("test@test.test"), cert, key); + QCOMPARE(ok, true); + QCOMPARE(cert.isNull(), false); + QCOMPARE(cert.isNull(), false); + + ok = SelfSignedCertificate::generateMumbleCertificate(QString(), QString(), cert, key); + QCOMPARE(ok, false); + QCOMPARE(cert.isNull(), true); + QCOMPARE(cert.isNull(), true); +} + +void TestSelfSignedCertificate::exerciseServerCert() { + QSslCertificate cert; + QSslKey key; + + bool ok = SelfSignedCertificate::generateMurmurV2Certificate(cert, key); + QCOMPARE(ok, true); + QCOMPARE(cert.isNull(), false); + QCOMPARE(cert.isNull(), false); +} + +QTEST_MAIN(TestSelfSignedCertificate) +#include "TestSelfSignedCertificate.moc" diff --git a/src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.pro b/src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.pro new file mode 100644 index 000000000..476765249 --- /dev/null +++ b/src/tests/TestSelfSignedCertificate/TestSelfSignedCertificate.pro @@ -0,0 +1,13 @@ +# Copyright 2005-2017 The Mumble Developers. All rights reserved. +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file at the root of the +# Mumble source tree or at <https://www.mumble.info/LICENSE>. + +include(../test.pri) +include(../../../qmake/qt.pri) + +QT *= network + +TARGET = TestSelfSignedCertificate +SOURCES = TestSelfSignedCertificate.cpp SelfSignedCertificate.cpp +HEADERS = SelfSignedCertificate.h diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 3ed9a767b..2b608453b 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -15,4 +15,5 @@ SUBDIRS += \ TestXMLTools \ TestUnresolvedServerAddress \ TestServerAddress \ - TestServerResolver + TestServerResolver \ + TestSelfSignedCertificate |