Unify QSslServer from QtWebSockets and QtHttpServer into QtNetwork
Both QtWeSockets and QtHttpServer has a QSslServer class that is useful elsewhere. They are different though, so the new class has features from both versions. [ChangeLog][QtNetwork] Unify QSslServer from QtWebSockets and QtHttpServer into QtNetwork Task-number: QTBUG-100823 Change-Id: I523f04db39297ceb9b258f673eb12deecfc6886c Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
782fbe0f63
commit
d631e581c0
@ -330,6 +330,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
|
|||||||
ssl/qsslkey.h ssl/qsslkey_p.cpp ssl/qsslkey_p.h
|
ssl/qsslkey.h ssl/qsslkey_p.cpp ssl/qsslkey_p.h
|
||||||
ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
|
ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
|
||||||
ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h
|
ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h
|
||||||
|
ssl/qsslserver.cpp ssl/qsslserver.h ssl/qsslserver_p.h
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl
|
qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl
|
||||||
|
287
src/network/ssl/qsslserver.cpp
Normal file
287
src/network/ssl/qsslserver.cpp
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSslServer
|
||||||
|
|
||||||
|
\ingroup network
|
||||||
|
\ingroup ssl
|
||||||
|
\inmodule QtNetwork
|
||||||
|
\since 6.4
|
||||||
|
|
||||||
|
\brief Implements an encrypted, secure TCP server over TLS.
|
||||||
|
|
||||||
|
Class to use in place of QTcpServer to implement TCP server using
|
||||||
|
Transport Layer Security (TLS).
|
||||||
|
|
||||||
|
To configure the secure handshake settings, use the applicable setter
|
||||||
|
functions on a QSslConfiguration object, and then use it as a argument
|
||||||
|
to the setSslConfiguration() function. All following incoming
|
||||||
|
connections handled will use these settings.
|
||||||
|
|
||||||
|
To start listening to incoming connections use the listen() function
|
||||||
|
inherited from QTcpServer. Other settings can be configured by using the
|
||||||
|
setter functions inherited from the QTcpServer class.
|
||||||
|
|
||||||
|
Connect to the signals of this class to respond to the incoming connection
|
||||||
|
attempts. They are the same as the signals on QSslSocket, but also
|
||||||
|
passes a pointer to the socket in question.
|
||||||
|
|
||||||
|
When responding to the pendingConnectionAvailable() signal, use the
|
||||||
|
nextPendingConnection() function to fetch the next incoming connection and
|
||||||
|
take it out of the pending connection queue. The QSslSocket is a child of
|
||||||
|
the QSslServer and will be deleted when the QSslServer is deleted. It is
|
||||||
|
still a good a good idea to destroy the object explicitly when you are done
|
||||||
|
with it, to avoid wasting memory.
|
||||||
|
|
||||||
|
\sa QTcpServer, QSslConfiguration, QSslSocket
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::peerVerifyError(QSslSocket *socket, const QSslError &error)
|
||||||
|
|
||||||
|
QSslServer can emit this signal several times during the SSL handshake,
|
||||||
|
before encryption has been established, to indicate that an error has
|
||||||
|
occurred while establishing the identity of the peer. The \a error is
|
||||||
|
usually an indication that \a socket is unable to securely identify the
|
||||||
|
peer.
|
||||||
|
|
||||||
|
This signal provides you with an early indication when something's wrong.
|
||||||
|
By connecting to this signal, you can manually choose to tear down the
|
||||||
|
connection from inside the connected slot before the handshake has
|
||||||
|
completed. If no action is taken, QSslServer will proceed to emitting
|
||||||
|
sslErrors().
|
||||||
|
|
||||||
|
\sa sslErrors()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::sslErrors(QSslSocket *socket, const QList<QSslError> &errors);
|
||||||
|
|
||||||
|
QSslServer emits this signal after the SSL handshake to indicate that one
|
||||||
|
or more errors have occurred while establishing the identity of the
|
||||||
|
peer. The errors are usually an indication that \a socket is unable to
|
||||||
|
securely identify the peer. Unless any action is taken, the connection
|
||||||
|
will be dropped after this signal has been emitted.
|
||||||
|
|
||||||
|
If you want to continue connecting despite the errors that have occurred,
|
||||||
|
you must call QSslSocket::ignoreSslErrors() from inside a slot connected to
|
||||||
|
this signal. If you need to access the error list at a later point, you
|
||||||
|
can call sslHandshakeErrors().
|
||||||
|
|
||||||
|
\a errors contains one or more errors that prevent QSslSocket from
|
||||||
|
verifying the identity of the peer.
|
||||||
|
|
||||||
|
\note You cannot use Qt::QueuedConnection when connecting to this signal,
|
||||||
|
or calling QSslSocket::ignoreSslErrors() will have no effect.
|
||||||
|
|
||||||
|
\sa peerVerifyError()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError socketError)
|
||||||
|
|
||||||
|
This signal is emitted after an error occurred during handshake. The
|
||||||
|
\a socketError parameter describes the type of error that occurred.
|
||||||
|
|
||||||
|
The \a socket is automatically deleted after this signal is emitted if the
|
||||||
|
socket handshake has not reached encrypted state. But if the \a socket is
|
||||||
|
successfully encrypted, it is inserted into the QSslServer's pending
|
||||||
|
connections queue. When the user has called
|
||||||
|
QTcpServer::nextPendingConnection() it is the user's responsibility to
|
||||||
|
destroy the \a socket or the \a socket will not be destroyed until the
|
||||||
|
QSslServer object is destroyed. If an error occurs on a \a socket after
|
||||||
|
it has been inserted into the pending connections queue, this signal
|
||||||
|
will not be emitted, and the \a socket will not be removed or destroyed.
|
||||||
|
|
||||||
|
\note You cannot use Qt::QueuedConnection when connecting to this signal,
|
||||||
|
or the \a socket will have been already destroyed when the signal is
|
||||||
|
handled.
|
||||||
|
|
||||||
|
\sa QSslSocket::error(), errorString()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::preSharedKeyAuthenticationRequired(QSslSocket *socket,
|
||||||
|
QSslPreSharedKeyAuthenticator *authenticator)
|
||||||
|
|
||||||
|
QSslServer emits this signal when \a socket negotiates a PSK ciphersuite,
|
||||||
|
and therefore PSK authentication is then required.
|
||||||
|
|
||||||
|
When using PSK, the server must supply a valid identity and a valid pre
|
||||||
|
shared key, in order for the SSL handshake to continue.
|
||||||
|
Applications can provide this information in a slot connected to this
|
||||||
|
signal, by filling in the passed \a authenticator object according to their
|
||||||
|
needs.
|
||||||
|
|
||||||
|
\note Ignoring this signal, or failing to provide the required credentials,
|
||||||
|
will cause the handshake to fail, and therefore the connection to be aborted.
|
||||||
|
|
||||||
|
\note The \a authenticator object is owned by the \a socket and must not be
|
||||||
|
deleted by the application.
|
||||||
|
|
||||||
|
\sa QSslPreSharedKeyAuthenticator
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::alertSent(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType type,
|
||||||
|
const QString &description)
|
||||||
|
|
||||||
|
QSslServer emits this signal if an alert message was sent from \a socket
|
||||||
|
to a peer. \a level describes if it was a warning or a fatal error.
|
||||||
|
\a type gives the code of the alert message. When a textual description
|
||||||
|
of the alert message is available, it is supplied in \a description.
|
||||||
|
|
||||||
|
\note This signal is mostly informational and can be used for debugging
|
||||||
|
purposes, normally it does not require any actions from the application.
|
||||||
|
\note Not all backends support this functionality.
|
||||||
|
|
||||||
|
\sa alertReceived(), QSsl::AlertLevel, QSsl::AlertType
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::alertReceived(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType
|
||||||
|
type, const QString &description)
|
||||||
|
|
||||||
|
QSslServer emits this signal if an alert message was received by the
|
||||||
|
\a socket from a peer. \a level tells if the alert was fatal or it was a
|
||||||
|
warning. \a type is the code explaining why the alert was sent.
|
||||||
|
When a textual description of the alert message is available, it is
|
||||||
|
supplied in \a description.
|
||||||
|
|
||||||
|
\note The signal is mostly for informational and debugging purposes and does not
|
||||||
|
require any handling in the application. If the alert was fatal, underlying
|
||||||
|
backend will handle it and close the connection.
|
||||||
|
\note Not all backends support this functionality.
|
||||||
|
|
||||||
|
\sa alertSent(), QSsl::AlertLevel, QSsl::AlertType
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSslServer::handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error)
|
||||||
|
|
||||||
|
QSslServer emits this signal if a certificate verification error was found
|
||||||
|
by \a socket and if early error reporting was enabled in QSslConfiguration.
|
||||||
|
An application is expected to inspect the \a error and decide if it wants
|
||||||
|
to continue the handshake, or abort it and send an alert message to the
|
||||||
|
peer. The signal-slot connection must be direct.
|
||||||
|
|
||||||
|
\sa QSslSocket::continueInterruptedHandshake(), sslErrors(),
|
||||||
|
QSslConfiguration::setHandshakeMustInterruptOnError()
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qsslserver.h"
|
||||||
|
#include "qsslserver_p.h"
|
||||||
|
|
||||||
|
#include <QtNetwork/QSslSocket>
|
||||||
|
#include <QtNetwork/QSslCipher>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
*/
|
||||||
|
QSslServerPrivate::QSslServerPrivate() :
|
||||||
|
sslConfiguration(QSslConfiguration::defaultConfiguration())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Constructs a new QSslServer with the given \a parent.
|
||||||
|
*/
|
||||||
|
QSslServer::QSslServer(QObject *parent) :
|
||||||
|
QTcpServer(QAbstractSocket::TcpSocket, *new QSslServerPrivate, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Destroys the QSslServer.
|
||||||
|
|
||||||
|
All open connections are closed.
|
||||||
|
*/
|
||||||
|
QSslServer::~QSslServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the \a sslConfiguration to use for all following incoming connections.
|
||||||
|
|
||||||
|
This must be called before listen() to ensure that the desired
|
||||||
|
configuration was in use during all handshakes.
|
||||||
|
|
||||||
|
\sa QSslSocket::setSslConfiguration()
|
||||||
|
*/
|
||||||
|
void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration)
|
||||||
|
{
|
||||||
|
Q_D(QSslServer);
|
||||||
|
d->sslConfiguration = sslConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the current ssl configuration.
|
||||||
|
*/
|
||||||
|
QSslConfiguration QSslServer::sslConfiguration() const
|
||||||
|
{
|
||||||
|
const Q_D(QSslServer);
|
||||||
|
return d->sslConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Called when a new connection is established.
|
||||||
|
|
||||||
|
Converts \a socket to a QSslSocket.
|
||||||
|
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
void QSslServer::incomingConnection(qintptr socket)
|
||||||
|
{
|
||||||
|
QSslSocket *pSslSocket = new QSslSocket(this);
|
||||||
|
|
||||||
|
pSslSocket->setSslConfiguration(sslConfiguration());
|
||||||
|
|
||||||
|
if (Q_LIKELY(pSslSocket->setSocketDescriptor(socket))) {
|
||||||
|
connect(pSslSocket, &QSslSocket::peerVerifyError,
|
||||||
|
[this, pSslSocket](const QSslError &error) {
|
||||||
|
Q_EMIT peerVerifyError(pSslSocket, error);
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QSslSocket::sslErrors,
|
||||||
|
[this, pSslSocket](const QList<QSslError> &errors) {
|
||||||
|
Q_EMIT sslErrors(pSslSocket, errors);
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QAbstractSocket::errorOccurred,
|
||||||
|
[this, pSslSocket](QAbstractSocket::SocketError error) {
|
||||||
|
Q_EMIT errorOccurred(pSslSocket, error);
|
||||||
|
if (!pSslSocket->isEncrypted())
|
||||||
|
pSslSocket->deleteLater();
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QSslSocket::encrypted, [this, pSslSocket]() {
|
||||||
|
pSslSocket->disconnect();
|
||||||
|
addPendingConnection(pSslSocket);
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QSslSocket::preSharedKeyAuthenticationRequired,
|
||||||
|
[this, pSslSocket](QSslPreSharedKeyAuthenticator *authenticator) {
|
||||||
|
Q_EMIT preSharedKeyAuthenticationRequired(pSslSocket, authenticator);
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QSslSocket::alertSent,
|
||||||
|
[this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type,
|
||||||
|
const QString &description) {
|
||||||
|
Q_EMIT alertSent(pSslSocket, level, type, description);
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QSslSocket::alertReceived,
|
||||||
|
[this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type,
|
||||||
|
const QString &description) {
|
||||||
|
Q_EMIT alertReceived(pSslSocket, level, type, description);
|
||||||
|
});
|
||||||
|
connect(pSslSocket, &QSslSocket::handshakeInterruptedOnError,
|
||||||
|
[this, pSslSocket](const QSslError &error) {
|
||||||
|
Q_EMIT handshakeInterruptedOnError(pSslSocket, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
Q_EMIT startedEncryptionHandshake(pSslSocket);
|
||||||
|
|
||||||
|
pSslSocket->startServerEncryption();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
58
src/network/ssl/qsslserver.h
Normal file
58
src/network/ssl/qsslserver.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef QSSLSERVER_H
|
||||||
|
#define QSSLSERVER_H
|
||||||
|
|
||||||
|
#include <QtNetwork/QTcpServer>
|
||||||
|
|
||||||
|
QT_REQUIRE_CONFIG(ssl);
|
||||||
|
|
||||||
|
#include <QtNetwork/QSslError>
|
||||||
|
#include <QtNetwork/QSslConfiguration>
|
||||||
|
#include <QtNetwork/QSslPreSharedKeyAuthenticator>
|
||||||
|
#include <QtNetwork/QSslSocket>
|
||||||
|
|
||||||
|
#include <QtCore/QList>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QSslSocket;
|
||||||
|
class QSslServerPrivate;
|
||||||
|
|
||||||
|
class Q_NETWORK_EXPORT QSslServer : public QTcpServer
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(QSslServer)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QSslServer(QObject *parent = nullptr);
|
||||||
|
~QSslServer() override;
|
||||||
|
|
||||||
|
void setSslConfiguration(const QSslConfiguration &sslConfiguration);
|
||||||
|
QSslConfiguration sslConfiguration() const;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void sslErrors(QSslSocket *socket, const QList<QSslError> &errors);
|
||||||
|
void peerVerifyError(QSslSocket *socket, const QSslError &error);
|
||||||
|
void errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError error);
|
||||||
|
void preSharedKeyAuthenticationRequired(QSslSocket *socket,
|
||||||
|
QSslPreSharedKeyAuthenticator *authenticator);
|
||||||
|
void alertSent(QSslSocket *socket, QSsl::AlertLevel level,
|
||||||
|
QSsl::AlertType type, const QString &description);
|
||||||
|
void alertReceived(QSslSocket *socket, QSsl::AlertLevel level,
|
||||||
|
QSsl::AlertType type, const QString &description);
|
||||||
|
void handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error);
|
||||||
|
void startedEncryptionHandshake(QSslSocket *socket);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void incomingConnection(qintptr socket) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DECLARE_PRIVATE(QSslServer)
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QSSLSERVER_H
|
37
src/network/ssl/qsslserver_p.h
Normal file
37
src/network/ssl/qsslserver_p.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef QSSLSERVER_P_H
|
||||||
|
#define QSSLSERVER_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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtNetwork/QSslConfiguration>
|
||||||
|
#include <QtNetwork/private/qtcpserver_p.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class Q_NETWORK_EXPORT QSslServerPrivate : public QTcpServerPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Q_DECLARE_PUBLIC(QSslServer)
|
||||||
|
|
||||||
|
QSslServerPrivate();
|
||||||
|
|
||||||
|
QSslConfiguration sslConfiguration;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QSSLSERVER_P_H
|
@ -14,6 +14,7 @@ if(QT_FEATURE_private_tests AND QT_FEATURE_ssl)
|
|||||||
add_subdirectory(qsslsocket_onDemandCertificates_static)
|
add_subdirectory(qsslsocket_onDemandCertificates_static)
|
||||||
# add_subdirectory(qasn1element)
|
# add_subdirectory(qasn1element)
|
||||||
add_subdirectory(qssldiffiehellmanparameters)
|
add_subdirectory(qssldiffiehellmanparameters)
|
||||||
|
add_subdirectory(qsslserver)
|
||||||
endif()
|
endif()
|
||||||
if(QT_FEATURE_dtls AND QT_FEATURE_private_tests AND QT_FEATURE_ssl)
|
if(QT_FEATURE_dtls AND QT_FEATURE_private_tests AND QT_FEATURE_ssl)
|
||||||
add_subdirectory(qdtlscookie)
|
add_subdirectory(qdtlscookie)
|
||||||
|
19
tests/auto/network/ssl/qsslserver/CMakeLists.txt
Normal file
19
tests/auto/network/ssl/qsslserver/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
if(NOT QT_FEATURE_private_tests)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
## tst_qsslserver Test:
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
# Collect test data
|
||||||
|
list(APPEND test_data "certs")
|
||||||
|
|
||||||
|
qt_internal_add_test(tst_qsslserver
|
||||||
|
SOURCES
|
||||||
|
tst_qsslserver.cpp
|
||||||
|
PUBLIC_LIBRARIES
|
||||||
|
Qt::CorePrivate
|
||||||
|
Qt::NetworkPrivate
|
||||||
|
TESTDATA ${test_data}
|
||||||
|
)
|
@ -0,0 +1,18 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC6TCCAdECCC/r9KvmbWTKMA0GCSqGSIb3DQEBCwUAMDUxFDASBgNVBAMMC0F1
|
||||||
|
c3dlaXNBcHAyMR0wGwYDVQQFExQxODIzNTE0MTY0NzI5NDg5NDM3MTAiGA8xOTcw
|
||||||
|
MDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjA1MRQwEgYDVQQDDAtBdXN3ZWlz
|
||||||
|
QXBwMjEdMBsGA1UEBRMUMTgyMzUxNDE2NDcyOTQ4OTQzNzEwggEiMA0GCSqGSIb3
|
||||||
|
DQEBAQUAA4IBDwAwggEKAoIBAQCahBpcZyr+PJBCpolzQeFVvDKABwlpdRKGZ8qq
|
||||||
|
jD4sq2L7VlBJslgJGv5vsB5oJbnX1FFEu4Uw2kYb/LhnFCEXEFtGKRpWOEZOOqWb
|
||||||
|
4l4q2MCa82ZCoIDt8yoAt0sSShbtR6pjW+l0lwAOEpfGvMaMVo5JUyspRxhl1dSu
|
||||||
|
sS2Wf65zliqF5VSM2r4xMfJ6LVytxDZsGfTe/HFT2OYYrF+UQZg0mNL39rYWOK4R
|
||||||
|
xoOz8eLl3K5hKuHNfn5zPt5QtMhaIvebijBg23xJpl+BeoS37WzaK1f+NyWZKPFb
|
||||||
|
rttvSnFxpkyRHqJJ5piNGH6pkQ1+zhd7uh7eOIwxktjYBOFzAgMBAAEwDQYJKoZI
|
||||||
|
hvcNAQELBQADggEBADw3MYPft+X78OK/2HAltzsKjfxv/D5qVizm9hcyG1GYe5pS
|
||||||
|
qgFn0trCyJopYdbRr+hP7CuHwMmv62CZiHSog3CBPoUh19JENUDGbHXxTEFleB0i
|
||||||
|
Fd8I2+WvRjbQ+ehaeTJPx88v5kkJnB2tZUNZuhEws8emCwr1G0TQv1tRYCR1Lp9i
|
||||||
|
8/I3FSFpL1zyk47WfM/THa279MPw9WtrFGA6oi36gH9mYxek7n/zQTVi54xDx9GT
|
||||||
|
KigBYqavjFdNXryjLTCCtJpMTDePgP66NAUnxn0D/amI2vSbIN++PSTsBm+n4Ti5
|
||||||
|
QW/ShFQDNb4bDiwjtTKCeKwvAp2/6GSHVkYy28M=
|
||||||
|
-----END CERTIFICATE-----
|
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAmoQaXGcq/jyQQqaJc0HhVbwygAcJaXUShmfKqow+LKti+1ZQ
|
||||||
|
SbJYCRr+b7AeaCW519RRRLuFMNpGG/y4ZxQhFxBbRikaVjhGTjqlm+JeKtjAmvNm
|
||||||
|
QqCA7fMqALdLEkoW7UeqY1vpdJcADhKXxrzGjFaOSVMrKUcYZdXUrrEtln+uc5Yq
|
||||||
|
heVUjNq+MTHyei1crcQ2bBn03vxxU9jmGKxflEGYNJjS9/a2FjiuEcaDs/Hi5dyu
|
||||||
|
YSrhzX5+cz7eULTIWiL3m4owYNt8SaZfgXqEt+1s2itX/jclmSjxW67bb0pxcaZM
|
||||||
|
kR6iSeaYjRh+qZENfs4Xe7oe3jiMMZLY2AThcwIDAQABAoIBAFjgvc0C5t8AdTZx
|
||||||
|
VsS+U2Aedang4lAPsE0xbIj3TFgjaTcLKfmKJUtvhIU39/WOJbz4+pofhvhXxVYZ
|
||||||
|
4vQfxvzeQrIzuFt52S7sWxA0gFgC/57hfKO8cQzt/u4UgJEPnupze5XVa47NwJFX
|
||||||
|
rof5U/erXgLdXQlMRMNm4QRvE7rp58E2MkSYNur0Xgy9L7cRcUQJ8iuMaxBpOzhS
|
||||||
|
fbNFi5zT7RCGcQSIDcb1JFlgs5tMUs6jzLoDSVD2+vvsN4i4LAAPkJSGTGed5vY1
|
||||||
|
xn4G8KPR4HHrnBYEb0SGu4ZTznOnQ+JSKhQrbnvEzXM4RTfjqn0YvF8x70+pWSMi
|
||||||
|
Fb4mlBECgYEAzW82O79HAlMm8LD7J4byPfVc/1M5/JOnE9H+RR5Vt4jZQGyjCmJu
|
||||||
|
cj4UeZyVim0xg30sSYrJ2Urd27CtHp+sMgHkvJt3/ZgcfMZJbMKNGq/OUtV8s/cA
|
||||||
|
nkU++/LgeW8r7wpaDjT7bfnOdcf16mYoXrmk0rTJvRqGXCBvCxtt5bsCgYEAwIxu
|
||||||
|
vZjPV4Vu/VX6sH2d31D9EFZuZKjGhqukFVtRqLbeosqT9mA+LhQ/wP5qoR2gLQbe
|
||||||
|
EwxJLJwGFjUhyhbHNlo6oHv3fWkzmHIMPwDRRI3Ktwi/50SwNSnyERUQcLaiwqKx
|
||||||
|
BqaxPYNnspUt0nKE0LFZsSlrfEyxajqAlUEgm6kCgYAV+uQumFScpxDvh8AXhpS8
|
||||||
|
lFgS6XC22YVy1XEDLC+3p2i3P+hh4A45IvNF378QRIabrvTiGXtnSF9cdhbPw/3E
|
||||||
|
i/dRRsEb3P6PSxfoDxjR1iWZL0Zcav0h8f6/LkleNMralJz2EC0moye36mEhZzTC
|
||||||
|
jdJYyQccuI3PpZi7839aqQKBgGezOnEiO4kHdB88jyc+gCglliWWZx4PR9x/1H8s
|
||||||
|
D26uDnneYJHwg4yNm0h1vTfInNujNzdLBp3f6edL9kbAvcmoDqsgGMqSPhd8VNwZ
|
||||||
|
tJsXQnYRYElN1RjM3nIUxiXuNvpcZLsQS6S1gMPNVEBjLOS4n3WquRjYtTRhDZ9U
|
||||||
|
1BsBAoGAUFrIatOLFhcgaqENHyUbMx5uSx0lIfF6Xd5KIAgi+btdmugHe+NK8Cd2
|
||||||
|
Rc2bQLQ9K1SvKFX6nFuEsGxnXkKuyhL/j0Kgm8nZin4uAcrtFnNdFumvCL6YgYSc
|
||||||
|
IvvM+uVfGEdbqm4pTuiLBfzOXIIy3kVlLGo402QG1pBzOtmsRMs=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,18 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5TCCAc0CCAO22gNi0v20MA0GCSqGSIb3DQEBCwUAMDMxFDASBgNVBAMMC0F1
|
||||||
|
c3dlaXNBcHAyMRswGQYDVQQFExIyNTIxMTE1NjY3NjM2MjExODgwIhgPMTk3MDAx
|
||||||
|
MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowMzEUMBIGA1UEAwwLQXVzd2Vpc0Fw
|
||||||
|
cDIxGzAZBgNVBAUTEjI1MjExMTU2Njc2MzYyMTE4ODCCASIwDQYJKoZIhvcNAQEB
|
||||||
|
BQADggEPADCCAQoCggEBAL+Fl6v5dcU7qk7vbINclWOhvCe/uklKnXV2QU382x7g
|
||||||
|
qpbYxJiJvz24C6tgDMmE0pwEz6PiCbh1dkc8+9cdp37eBcFLCOXYQb27gqVVyVtu
|
||||||
|
xO0LLVXPCv48bGSwljOz0FRC3FolzWxzrZogM/i2b/lmehHJ3D4ejmINmIgtFJ9P
|
||||||
|
JNNCH4Oh5YEbaFFlNf2m7lCoSuQkOlLZcGeLoipK2XvhZJff6c1uxValh/Mx5dNB
|
||||||
|
5Mgd5cOZSSEhwf7mcE8C3SHVfjeNfZGIqlkwdY8lvAOjirAtj6Yl88sJOUID/Q/N
|
||||||
|
hU9D8IZy6+Bk2cJQwI/Gzr590VYvlSTI+6lXr//oBBECAwEAATANBgkqhkiG9w0B
|
||||||
|
AQsFAAOCAQEArSMO88AYT+9tPCl5lXtSRa0OycqKNlW58GujxIDuR8WX1eFmGSHQ
|
||||||
|
uijo5KPYUnqydZzAewGC8NvC9WcLwFltNZ9igXikUHiAHc1JLfW7+7SgKpwOUb02
|
||||||
|
rJkUkpPA/SmwkLSKYiR1prt5wgSulU1HPBESep05DfR8MCU5+KHkLyXDqtrbudJ4
|
||||||
|
lQd9dSKJFn+cSjUC5JNxCPHoIISe7hfGFMLkd0/tVfSIXLVOAZG4K6zExUdjyPi8
|
||||||
|
qEuPq6QCRyIJbYQc5HfnARgwK6GXHqkyLWlqK946Yz8VOba7Nan5uQ6xCjUMHw8Z
|
||||||
|
z/673o/3DCaQ9N6dWahNQ09a9ZH8U1X4iA==
|
||||||
|
-----END CERTIFICATE-----
|
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAv4WXq/l1xTuqTu9sg1yVY6G8J7+6SUqddXZBTfzbHuCqltjE
|
||||||
|
mIm/PbgLq2AMyYTSnATPo+IJuHV2Rzz71x2nft4FwUsI5dhBvbuCpVXJW27E7Qst
|
||||||
|
Vc8K/jxsZLCWM7PQVELcWiXNbHOtmiAz+LZv+WZ6EcncPh6OYg2YiC0Un08k00If
|
||||||
|
g6HlgRtoUWU1/abuUKhK5CQ6UtlwZ4uiKkrZe+Fkl9/pzW7FVqWH8zHl00HkyB3l
|
||||||
|
w5lJISHB/uZwTwLdIdV+N419kYiqWTB1jyW8A6OKsC2PpiXzywk5QgP9D82FT0Pw
|
||||||
|
hnLr4GTZwlDAj8bOvn3RVi+VJMj7qVev/+gEEQIDAQABAoIBADdoXsjSEtBMwqiz
|
||||||
|
e6FFV7LLR7P4M9ygSY2B+MKnNH1qYe/iJn4626jvZfDeiNSEKKoaejffXRCQaveR
|
||||||
|
HQrO+XYqpV+WZayZM+vAI7vRZb+d/DrX0PXSQEvtDy7SJ6Itk0fNUBKEfTmy/bZp
|
||||||
|
Op/pp9tvWkFrNNyD2o1jgY1j/WNY8g605m0oURJ9WQsMUu/Kzu+NMoaKTIoQGb3d
|
||||||
|
dP71F4KaTXHYxj3B0c+y0NedKbrvnBsP6XbEpgJBaXjtD9z+z/aMF6dmuvpkx7uY
|
||||||
|
qzwPMRw05QPyJ9x+1V/v4TytY5f596NgW2niVj77BunkZasTYIEX7bjByrlTeLdx
|
||||||
|
xvPRpAECgYEA5KkM/ORbhN1oaw9+tQxA48oG2DFqChBr+vc4NU4j5SNFn9ks5nHI
|
||||||
|
xdJNZ9k+bjVUkBP4m88Wd07SW9zXCL8Q5lczb+p5SWl/Pp7ltqaxpH17uzamsaIv
|
||||||
|
KIBkeJTOU5TuWdXiV5FY+ofK9ojyEaqX1tmylWnoVe4bIMRWXE5bMSkCgYEA1mvJ
|
||||||
|
snkNzPFG0RK7ikjsNxrhzE07+7RSnoM9WeW8y2lvQ9MjdR6eOgqnnlcdk2A7OVbf
|
||||||
|
culNgLc0qx/PxZ4BV+8yLLb1EBBGvuVG+x4a6H2mLHdFCJekByZHaQNs9ogVLvdv
|
||||||
|
3z8D59KknBUjtj9dCw90Z41yMM4kpWMG9yfSEKkCgYEAvuCvytwF2d/JrrV8nD3i
|
||||||
|
XUTkecymLEiRGysMbNMR+9F56XotlSEe7KQloa8kAnPaZ3uEaOxyYJ4X1D+B8fct
|
||||||
|
cFsSwTYGkVXTtr6GG/cDC8EEbL+uX1J382Nae54croEAh1WYYGkg0eJRd4PSLxUt
|
||||||
|
M1j/TuLd4/2j/7JmNR/j2CECgYBdB3MBHghgzKXe+/OmMbFazyz8SN4nfLsDzwkF
|
||||||
|
QenBj0MY+DhADkK0B/9lcYKBeJT5cbmMz7AykkolnK22nbETh9ILGG4GxCkNlchQ
|
||||||
|
F2WxTSKV1EF9Ut11xKPi6fuSksQuFmjRQTPelsOYfIt7/M3PiKsGapYKmsXHg8l3
|
||||||
|
3i0D0QKBgQCi+HNOaYqduxwjrj8h4eUbiwjID8DCNJ+jXsuGVa6jcsfFpdpivx2c
|
||||||
|
ytYSXuTXLRq0I3c1ChUOGQQeztJ5GtCPnXjLHHMf3f6yr7Pk56AUmUsaIlR1Q2Zo
|
||||||
|
gqpFD8zYD5UFc2KM7Y38YTh4j82uDzDvHBBFpli7dEmSn2WpcmzFag==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
441
tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp
Normal file
441
tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <QtNetwork/QSslServer>
|
||||||
|
#include <QtNetwork/QSslKey>
|
||||||
|
#include "private/qtlsbackend_p.h"
|
||||||
|
|
||||||
|
class tst_QSslServer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void testOneSuccessfulConnection();
|
||||||
|
void testSelfSignedCertificateRejectedByServer();
|
||||||
|
void testSelfSignedCertificateRejectedByClient();
|
||||||
|
#if QT_CONFIG(openssl)
|
||||||
|
void testHandshakeInterruptedOnError();
|
||||||
|
void testPreSharedKeyAuthenticationRequired();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString testDataDir;
|
||||||
|
bool isTestingOpenSsl = false;
|
||||||
|
QSslConfiguration selfSignedClientQSslConfiguration();
|
||||||
|
QSslConfiguration selfSignedServerQSslConfiguration();
|
||||||
|
QSslConfiguration createQSslConfiguration(QString keyFileName, QString certificateFileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SslServerSpy : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SslServerSpy(QSslConfiguration &configuration);
|
||||||
|
|
||||||
|
QSslServer server;
|
||||||
|
QSignalSpy sslErrorsSpy;
|
||||||
|
QSignalSpy peerVerifyErrorSpy;
|
||||||
|
QSignalSpy errorOccurredSpy;
|
||||||
|
QSignalSpy pendingConnectionAvailableSpy;
|
||||||
|
QSignalSpy preSharedKeyAuthenticationRequiredSpy;
|
||||||
|
QSignalSpy alertSentSpy;
|
||||||
|
QSignalSpy alertReceivedSpy;
|
||||||
|
QSignalSpy handshakeInterruptedOnErrorSpy;
|
||||||
|
QSignalSpy startedEncryptionHandshakeSpy;
|
||||||
|
};
|
||||||
|
|
||||||
|
SslServerSpy::SslServerSpy(QSslConfiguration &configuration)
|
||||||
|
: server(),
|
||||||
|
sslErrorsSpy(&server, &QSslServer::sslErrors),
|
||||||
|
peerVerifyErrorSpy(&server, &QSslServer::peerVerifyError),
|
||||||
|
errorOccurredSpy(&server, &QSslServer::errorOccurred),
|
||||||
|
pendingConnectionAvailableSpy(&server, &QSslServer::pendingConnectionAvailable),
|
||||||
|
preSharedKeyAuthenticationRequiredSpy(&server,
|
||||||
|
&QSslServer::preSharedKeyAuthenticationRequired),
|
||||||
|
alertSentSpy(&server, &QSslServer::alertSent),
|
||||||
|
alertReceivedSpy(&server, &QSslServer::alertReceived),
|
||||||
|
handshakeInterruptedOnErrorSpy(&server, &QSslServer::handshakeInterruptedOnError),
|
||||||
|
startedEncryptionHandshakeSpy(&server, &QSslServer::startedEncryptionHandshake)
|
||||||
|
{
|
||||||
|
server.setSslConfiguration(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSslServer::initTestCase()
|
||||||
|
{
|
||||||
|
testDataDir = QFileInfo(QFINDTESTDATA("certs")).absolutePath();
|
||||||
|
if (testDataDir.isEmpty())
|
||||||
|
testDataDir = QCoreApplication::applicationDirPath();
|
||||||
|
if (!testDataDir.endsWith(QLatin1String("/")))
|
||||||
|
testDataDir += QLatin1String("/");
|
||||||
|
|
||||||
|
const QString openSslBackend = QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexOpenSSL];
|
||||||
|
const auto &tlsBackends = QSslSocket::availableBackends();
|
||||||
|
if (tlsBackends.contains(openSslBackend)) {
|
||||||
|
isTestingOpenSsl = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSslConfiguration tst_QSslServer::selfSignedClientQSslConfiguration()
|
||||||
|
{
|
||||||
|
return createQSslConfiguration(testDataDir + "certs/selfsigned-client.key",
|
||||||
|
testDataDir + "certs/selfsigned-client.crt");
|
||||||
|
}
|
||||||
|
|
||||||
|
QSslConfiguration tst_QSslServer::selfSignedServerQSslConfiguration()
|
||||||
|
{
|
||||||
|
return createQSslConfiguration(testDataDir + "certs/selfsigned-server.key",
|
||||||
|
testDataDir + "certs/selfsigned-server.crt");
|
||||||
|
}
|
||||||
|
|
||||||
|
QSslConfiguration tst_QSslServer::createQSslConfiguration(QString keyFileName,
|
||||||
|
QString certificateFileName)
|
||||||
|
{
|
||||||
|
QSslConfiguration configuration(QSslConfiguration::defaultConfiguration());
|
||||||
|
|
||||||
|
QFile keyFile(keyFileName);
|
||||||
|
if (keyFile.open(QIODevice::ReadOnly)) {
|
||||||
|
QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||||
|
if (!key.isNull()) {
|
||||||
|
configuration.setPrivateKey(key);
|
||||||
|
} else {
|
||||||
|
qCritical() << "Could not parse key: " << keyFileName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCritical() << "Could not find key: " << keyFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QSslCertificate> localCert = QSslCertificate::fromPath(certificateFileName);
|
||||||
|
if (!localCert.isEmpty() && !localCert.first().isNull()) {
|
||||||
|
configuration.setLocalCertificate(localCert.first());
|
||||||
|
} else {
|
||||||
|
qCritical() << "Could not find certificate: " << certificateFileName;
|
||||||
|
}
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSslServer::testOneSuccessfulConnection()
|
||||||
|
{
|
||||||
|
// Setup server
|
||||||
|
QSslConfiguration serverConfiguration = selfSignedServerQSslConfiguration();
|
||||||
|
SslServerSpy server(serverConfiguration);
|
||||||
|
QVERIFY(server.server.listen());
|
||||||
|
|
||||||
|
// Check that all signal spys are valid
|
||||||
|
QVERIFY(server.sslErrorsSpy.isValid());
|
||||||
|
QVERIFY(server.peerVerifyErrorSpy.isValid());
|
||||||
|
QVERIFY(server.errorOccurredSpy.isValid());
|
||||||
|
QVERIFY(server.pendingConnectionAvailableSpy.isValid());
|
||||||
|
QVERIFY(server.preSharedKeyAuthenticationRequiredSpy.isValid());
|
||||||
|
QVERIFY(server.alertSentSpy.isValid());
|
||||||
|
QVERIFY(server.alertReceivedSpy.isValid());
|
||||||
|
QVERIFY(server.handshakeInterruptedOnErrorSpy.isValid());
|
||||||
|
QVERIFY(server.startedEncryptionHandshakeSpy.isValid());
|
||||||
|
|
||||||
|
// Check that no connections has occurred
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 0);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 0);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(), 0);
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 0);
|
||||||
|
|
||||||
|
// Connect client
|
||||||
|
QSslSocket client;
|
||||||
|
QSslConfiguration clientConfiguration = QSslConfiguration::defaultConfiguration();
|
||||||
|
client.setSslConfiguration(clientConfiguration);
|
||||||
|
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(),
|
||||||
|
server.server.serverPort());
|
||||||
|
|
||||||
|
// Type of certificate error to expect
|
||||||
|
const auto certificateError =
|
||||||
|
isTestingOpenSsl ? QSslError::SelfSignedCertificate : QSslError::CertificateUntrusted;
|
||||||
|
// Expected errors
|
||||||
|
connect(&client, &QSslSocket::sslErrors,
|
||||||
|
[&certificateError, &client](const QList<QSslError> &errors) {
|
||||||
|
QCOMPARE(errors.size(), 2);
|
||||||
|
for (auto error : errors) {
|
||||||
|
QVERIFY(error.error() == certificateError
|
||||||
|
|| error.error() == QSslError::HostNameMismatch);
|
||||||
|
}
|
||||||
|
client.ignoreSslErrors();
|
||||||
|
});
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
int waitFor = 2;
|
||||||
|
connect(&client, &QSslSocket::encrypted, [&loop, &waitFor]() {
|
||||||
|
if (!--waitFor)
|
||||||
|
loop.quit();
|
||||||
|
});
|
||||||
|
connect(&server.server, &QTcpServer::pendingConnectionAvailable, [&loop, &waitFor]() {
|
||||||
|
if (!--waitFor)
|
||||||
|
loop.quit();
|
||||||
|
});
|
||||||
|
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
// Check that one encrypted connection has occurred without error
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 0);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 1);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(), 0);
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 1);
|
||||||
|
|
||||||
|
// Check client socket
|
||||||
|
QVERIFY(client.isEncrypted());
|
||||||
|
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSslServer::testSelfSignedCertificateRejectedByServer()
|
||||||
|
{
|
||||||
|
// Set up server that verifies client
|
||||||
|
QSslConfiguration serverConfiguration = selfSignedServerQSslConfiguration();
|
||||||
|
serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||||
|
SslServerSpy server(serverConfiguration);
|
||||||
|
QVERIFY(server.server.listen());
|
||||||
|
|
||||||
|
// Connect client
|
||||||
|
QSslSocket client;
|
||||||
|
QSslConfiguration clientConfiguration = selfSignedClientQSslConfiguration();
|
||||||
|
clientConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
client.setSslConfiguration(clientConfiguration);
|
||||||
|
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(),
|
||||||
|
server.server.serverPort());
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(&client, SIGNAL(disconnected()), &loop, SLOT(quit()));
|
||||||
|
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
// Check that one encrypted connection has failed
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 1);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 1);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 1);
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 0);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(),
|
||||||
|
isTestingOpenSsl ? 1 : 0); // OpenSSL only signal
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(), 0);
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 1);
|
||||||
|
|
||||||
|
// Type of certificate error to expect
|
||||||
|
const auto certificateError =
|
||||||
|
isTestingOpenSsl ? QSslError::SelfSignedCertificate : QSslError::CertificateUntrusted;
|
||||||
|
|
||||||
|
// Check the sslErrorsSpy
|
||||||
|
const auto sslErrorsSpyErrors =
|
||||||
|
qvariant_cast<QList<QSslError>>(qAsConst(server.sslErrorsSpy).first()[1]);
|
||||||
|
QCOMPARE(sslErrorsSpyErrors.size(), 1);
|
||||||
|
QCOMPARE(sslErrorsSpyErrors.first().error(), certificateError);
|
||||||
|
|
||||||
|
// Check the peerVerifyErrorSpy
|
||||||
|
const auto peerVerifyErrorSpyError =
|
||||||
|
qvariant_cast<QSslError>(qAsConst(server.peerVerifyErrorSpy).first()[1]);
|
||||||
|
QCOMPARE(peerVerifyErrorSpyError.error(), certificateError);
|
||||||
|
|
||||||
|
// Check client socket
|
||||||
|
QVERIFY(!client.isEncrypted());
|
||||||
|
QCOMPARE(client.state(), QAbstractSocket::UnconnectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSslServer::testSelfSignedCertificateRejectedByClient()
|
||||||
|
{
|
||||||
|
// Set up server without verification of client
|
||||||
|
QSslConfiguration serverConfiguration = selfSignedServerQSslConfiguration();
|
||||||
|
SslServerSpy server(serverConfiguration);
|
||||||
|
QVERIFY(server.server.listen());
|
||||||
|
|
||||||
|
// Connect client that authenticates server
|
||||||
|
QSslSocket client;
|
||||||
|
QSslConfiguration clientConfiguration = selfSignedClientQSslConfiguration();
|
||||||
|
if (isTestingOpenSsl) {
|
||||||
|
clientConfiguration.setHandshakeMustInterruptOnError(true);
|
||||||
|
QVERIFY(clientConfiguration.handshakeMustInterruptOnError());
|
||||||
|
}
|
||||||
|
client.setSslConfiguration(clientConfiguration);
|
||||||
|
QSignalSpy clientConnectedSpy(&client, SIGNAL(connected()));
|
||||||
|
QSignalSpy clientHostFoundSpy(&client, SIGNAL(hostFound()));
|
||||||
|
QSignalSpy clientDisconnectedSpy(&client, SIGNAL(disconnected()));
|
||||||
|
QSignalSpy clientConnectionEncryptedSpy(&client, SIGNAL(encrypted()));
|
||||||
|
QSignalSpy clientSslErrorsSpy(&client, SIGNAL(sslErrors(QList<QSslError>)));
|
||||||
|
QSignalSpy clientErrorOccurredSpy(&client, SIGNAL(errorOccurred(QAbstractSocket::SocketError)));
|
||||||
|
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(),
|
||||||
|
server.server.serverPort());
|
||||||
|
QEventLoop loop;
|
||||||
|
QTimer::singleShot(1000, &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
// Type of socket error to expect
|
||||||
|
const auto socketError = isTestingOpenSsl
|
||||||
|
? QAbstractSocket::SocketError::SslHandshakeFailedError
|
||||||
|
: QAbstractSocket::SocketError::RemoteHostClosedError;
|
||||||
|
|
||||||
|
QTcpSocket *connection = server.server.nextPendingConnection();
|
||||||
|
if (connection == nullptr) {
|
||||||
|
// Client disconnected before connection accepted by server
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 0);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 1); // Client rejected first
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 0);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(),
|
||||||
|
isTestingOpenSsl ? 1 : 0); // OpenSSL only signal
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 1);
|
||||||
|
|
||||||
|
const auto errrOccuredSpyError = qvariant_cast<QAbstractSocket::SocketError>(
|
||||||
|
qAsConst(server.errorOccurredSpy).first()[1]);
|
||||||
|
QCOMPARE(errrOccuredSpyError, socketError);
|
||||||
|
} else {
|
||||||
|
// Client disconnected after connection accepted by server
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 0);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 0); // Server accepted first
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 1);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(),
|
||||||
|
isTestingOpenSsl ? 1 : 0); // OpenSSL only signal
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 1);
|
||||||
|
|
||||||
|
QCOMPARE(connection->state(), QAbstractSocket::UnconnectedState);
|
||||||
|
QCOMPARE(connection->error(), socketError);
|
||||||
|
auto sslConnection = qobject_cast<QSslSocket *>(connection);
|
||||||
|
QVERIFY(sslConnection);
|
||||||
|
QVERIFY(!sslConnection->isEncrypted());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that client has rejected server
|
||||||
|
QCOMPARE(clientConnectedSpy.count(), 1);
|
||||||
|
QCOMPARE(clientHostFoundSpy.count(), 1);
|
||||||
|
QCOMPARE(clientDisconnectedSpy.count(), 1);
|
||||||
|
QCOMPARE(clientConnectionEncryptedSpy.count(), 0);
|
||||||
|
QCOMPARE(clientSslErrorsSpy.count(), isTestingOpenSsl ? 0 : 1);
|
||||||
|
QCOMPARE(clientErrorOccurredSpy.count(), 1);
|
||||||
|
|
||||||
|
// Check client socket
|
||||||
|
QVERIFY(!client.isEncrypted());
|
||||||
|
QCOMPARE(client.state(), QAbstractSocket::UnconnectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QT_CONFIG(openssl)
|
||||||
|
|
||||||
|
void tst_QSslServer::testHandshakeInterruptedOnError()
|
||||||
|
{
|
||||||
|
if (!isTestingOpenSsl)
|
||||||
|
QSKIP("This test requires OpenSSL as the active TLS backend");
|
||||||
|
|
||||||
|
auto serverConfiguration = selfSignedServerQSslConfiguration();
|
||||||
|
serverConfiguration.setHandshakeMustInterruptOnError(true);
|
||||||
|
QVERIFY(serverConfiguration.handshakeMustInterruptOnError());
|
||||||
|
serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||||
|
SslServerSpy server(serverConfiguration);
|
||||||
|
server.server.listen();
|
||||||
|
|
||||||
|
QSslSocket client;
|
||||||
|
auto clientConfiguration = selfSignedClientQSslConfiguration();
|
||||||
|
clientConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
client.setSslConfiguration(clientConfiguration);
|
||||||
|
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(),
|
||||||
|
server.server.serverPort());
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(&client, SIGNAL(disconnected()), &loop, SLOT(quit()));
|
||||||
|
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
// Check that client certificate causes handshake interrupted signal to be emitted
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 0);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 1);
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 0);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(), 1);
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(), 0);
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 1);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSslServer::testPreSharedKeyAuthenticationRequired()
|
||||||
|
{
|
||||||
|
if (!isTestingOpenSsl)
|
||||||
|
QSKIP("This test requires OpenSSL as the active TLS backend");
|
||||||
|
|
||||||
|
auto serverConfiguration = QSslConfiguration::defaultConfiguration();
|
||||||
|
serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||||
|
serverConfiguration.setProtocol(QSsl::TlsV1_2);
|
||||||
|
serverConfiguration.setCiphers({ QSslCipher("PSK-AES256-CBC-SHA") });
|
||||||
|
serverConfiguration.setPreSharedKeyIdentityHint("Server Y");
|
||||||
|
SslServerSpy server(serverConfiguration);
|
||||||
|
connect(&server.server, &QSslServer::preSharedKeyAuthenticationRequired,
|
||||||
|
[](QSslSocket *, QSslPreSharedKeyAuthenticator *authenticator) {
|
||||||
|
QCOMPARE(authenticator->identity(), QByteArray("Client X"));
|
||||||
|
authenticator->setPreSharedKey("123456");
|
||||||
|
});
|
||||||
|
server.server.listen();
|
||||||
|
|
||||||
|
QSslSocket client;
|
||||||
|
auto clientConfiguration = QSslConfiguration::defaultConfiguration();
|
||||||
|
clientConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
clientConfiguration.setProtocol(QSsl::TlsV1_2);
|
||||||
|
clientConfiguration.setCiphers({ QSslCipher("PSK-AES256-CBC-SHA") });
|
||||||
|
client.setSslConfiguration(clientConfiguration);
|
||||||
|
connect(&client, &QSslSocket::preSharedKeyAuthenticationRequired,
|
||||||
|
[](QSslPreSharedKeyAuthenticator *authenticator) {
|
||||||
|
QCOMPARE(authenticator->identityHint(), QByteArray("Server Y"));
|
||||||
|
authenticator->setPreSharedKey("123456");
|
||||||
|
authenticator->setIdentity("Client X");
|
||||||
|
});
|
||||||
|
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(),
|
||||||
|
server.server.serverPort());
|
||||||
|
|
||||||
|
connect(&server.server, &QSslServer::sslErrors,
|
||||||
|
[](QSslSocket *socket, const QList<QSslError> &errors) {
|
||||||
|
for (auto error : errors) {
|
||||||
|
QCOMPARE(error.error(), QSslError::NoPeerCertificate);
|
||||||
|
}
|
||||||
|
socket->ignoreSslErrors();
|
||||||
|
});
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(&client, SIGNAL(encrypted()), &loop, SLOT(quit()));
|
||||||
|
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
// Check that server is connected
|
||||||
|
QCOMPARE(server.sslErrorsSpy.count(), 1);
|
||||||
|
QCOMPARE(server.peerVerifyErrorSpy.count(), 1);
|
||||||
|
QCOMPARE(server.errorOccurredSpy.count(), 0);
|
||||||
|
QCOMPARE(server.pendingConnectionAvailableSpy.count(), 1);
|
||||||
|
QCOMPARE(server.preSharedKeyAuthenticationRequiredSpy.count(), 1);
|
||||||
|
QCOMPARE(server.alertSentSpy.count(), 0);
|
||||||
|
QCOMPARE(server.alertReceivedSpy.count(), 0);
|
||||||
|
QCOMPARE(server.handshakeInterruptedOnErrorSpy.count(), 0);
|
||||||
|
QCOMPARE(server.startedEncryptionHandshakeSpy.count(), 1);
|
||||||
|
|
||||||
|
// Check client socket
|
||||||
|
QVERIFY(client.isEncrypted());
|
||||||
|
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QSslServer)
|
||||||
|
|
||||||
|
#include "tst_qsslserver.moc"
|
Loading…
x
Reference in New Issue
Block a user