Класс QDtls
Этот класс обеспечивает шифрование для UDP-сокет. Подробнее...
| Заголовок: | #include <QDtls> |
| CMake: | find_package(Qt6 COMPONENTS Network REQUIRED) target_link_libraries(mytarget PRIVATE Qt6::Network) |
| qmake: | QT += network |
| С версии: | Qt 5.12 |
| Наследует: | QObject |
Общие типы
| GeneratorParameters | |
| Перечисление | HandshakeState { HandshakeNotStarted, HandshakeInProgress, PeerVerificationFailed, HandshakeComplete } |
Общие функции
| QDtls(QSslSocket::SslMode mode, QObject *parent = nullptr) | |
| виртуальный | ~QDtls() |
| bool | abortHandshake(QUdpSocket *socket) |
| QDtls::GeneratorParameters | cookieGeneratorParameters() const |
| QByteArray | decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) |
| bool | doHandshake(QUdpSocket *socket, const QByteArray &dgram = {}) |
| QSslConfiguration | dtlsConfiguration() const |
| QDtlsError | dtlsError() const |
| QString | dtlsErrorString() const |
| bool | handleTimeout(QUdpSocket *socket) |
| QDtls::HandshakeState | handshakeState() const |
| void | ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) |
| bool | isConnectionEncrypted() const |
| quint16 | mtuHint() const |
| QHostAddress | peerAddress() const |
| quint16 | peerPort() const |
| QList<QSslError> | peerVerificationErrors() const |
| QString | peerVerificationName() const |
| bool | resumeHandshake(QUdpSocket *socket) |
| QSslCipher | sessionCipher() const |
| QSsl::SslProtocol | sessionProtocol() const |
| bool | setCookieGeneratorParameters(const QDtls::GeneratorParameters ¶ms) |
| bool | setDtlsConfiguration(const QSslConfiguration &configuration) |
| void | setMtuHint(quint16 mtuHint) |
| bool | setPeer(const QHostAddress &address, quint16 port, const QString &verificationName = {}) |
| bool | setPeerVerificationName(const QString &name) |
| bool | shutdown(QUdpSocket *socket) |
| QSslSocket::SslMode | sslMode() const |
| qint64 | writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) |
Сигналы
| void | handshakeTimeout() |
| void | pskRequired(QSslPreSharedKeyAuthenticator *authenticator) |
Связанные нечлены
| Перечисление | QDtlsError { NoError, InvalidInputParameters, InvalidOperation, UnderlyingSocketError, RemoteClosedConnectionError, …, TlsNonFatalError } |
Подробное описание
Класс QDtls можно использовать для установления защищенного соединения с сетевым узлом с использованием протокола User Datagram Protocol (UDP). Соединение DTLS по сути бессоединительному протоколу UDP означает, что два узла сначала должны успешно завершить рукопожатие TLS, вызвав doHandshake(). После завершения рукопожатия зашифрованные дейтаграммы могут быть отправлены узлу с помощью writeDatagramEncrypted(). Зашифрованные дейтаграммы, поступающие от узла, могут быть расшифрованы с помощью decryptDatagram().
QDtls разработан для работы с QUdpSocket. Поскольку QUdpSocket может получать дейтаграммы, поступающие от разных узлов, приложение должно реализовать демультиплексирование, перенаправляя дейтаграммы, поступающие от разных узлов, соответствующим экземплярам QDtls. Связь между сетевым узлом и его объектом QDtls может быть установлена с помощью адреса и номера порта узла. Перед началом рукопожатия приложение должно установить адрес и номер порта узла с помощью setPeer().
QDtls не считывает дейтаграммы из QUdpSocket, это ожидается от приложения, например, в слоте, подключенном к сигналу QUdpSocket::readyRead(). Затем эти дейтаграммы должны быть обработаны QDtls.
Примечание: QDtls не берет на себя владение объектом QUdpSocket.
Обычно во время фазы рукопожатия оба узла должны получать и отправлять несколько дейтаграмм. При чтении дейтаграмм сервер и клиент должны передавать эти дейтаграммы в doHandshake() до тех пор, пока не будет обнаружена какая-либо ошибка или handshakeState() не вернет HandshakeComplete:
// A client initiates a handshake:
QUdpSocket clientSocket;
QDtls clientDtls;
clientDtls.setPeer(address, port, peerName);
clientDtls.doHandshake(&clientSocket);
// A server accepting an incoming connection; address, port, clientHello are
// read by QUdpSocket::readDatagram():
QByteArray clientHello(serverSocket.pendingDatagramSize(), Qt::Uninitialized);
QHostAddress address;
quin16 port = {};
serverSocket.readDatagram(clientHello.data(), clientHello.size(), &address, &port);
QDtls serverDtls;
serverDtls.setPeer(address, port);
serverDtls.doHandshake(&serverSocket, clientHello);
// Handshake completion, both for server and client:
void DtlsConnection::continueHandshake(const QByteArray &datagram)
{
if (dtls.doHandshake(&udpSocket, datagram)) {
// Check handshake status:
if (dtls.handshakeStatus() == QDlts::HandshakeComplete) {
// Secure DTLS connection is now established.
}
} else {
// Error handling.
}
} Для сервера первый вызов doHandshake() требует непустой дейтаграммы, содержащей сообщение ClientHello. Если сервер также использует QDtlsClientVerifier, первое сообщение ClientHello ожидается как проверенное QDtlsClientVerifier.
В случае, если идентификатор узла не может быть проверен во время рукопожатия, приложение должно проверить ошибки, возвращаемые peerVerificationErrors(), а затем либо проигнорировать ошибки, вызвав ignoreVerificationErrors(), либо прервать рукопожатие, вызвав abortHandshake(). Если ошибки были проигнорированы, рукопожатие можно возобновить, вызвав resumeHandshake().
После завершения рукопожатия дейтаграммы могут быть отправлены и получены от сетевого узла безопасно:
// Sending an encrypted datagram: dtlsConnection.writeDatagramEncrypted(&clientSocket, "Hello DTLS server!"); // Decryption: QByteArray encryptedMessage(dgramSize); socket.readDatagram(encryptedMessage.data(), dgramSize); const QByteArray plainText = dtlsConnection.decryptDatagram(&socket, encryptedMessage);
Соединение DTLS может быть закрыто с помощью shutdown().
DtlsClient::~DtlsClient()
{
clientDtls.shutdown(&clientSocket);
} Предупреждение: Рекомендуется вызывать shutdown() перед уничтожением объекта QDtls клиента, если планируется повторное использование того же номера порта для подключения к серверу позже. В противном случае сервер может отбрасывать входящие сообщения ClientHello, см. RFC 6347, раздел 4.2.8 для получения более подробной информации и советов по реализации.
Если сервер не использует QDtlsClientVerifier, он обязан настроить свои объекты QDtls для отключения процедуры проверки куки:
auto config = QSslConfiguration::defaultDtlsConfiguration(); config.setDtlsCookieVerificationEnabled(false); // Some other customization ... dtlsConnection.setDtlsConfiguration(config);
Сервер, использующий проверку куки с параметрами генератора, отличными от стандартных, обязан установить те же параметры для своего объекта QDtls перед началом рукопожатия.
Примечание: Протокол DTLS оставляет обнаружение максимального размера транспортного блока (PMTU) приложению. Приложение может предоставить QDtls MTU с помощью setMtuHint(). Это подсказка влияет только на фазу рукопожатия, так как только сообщения рукопожатия могут быть фрагментированы и восставлены DTLS. Все остальные сообщения, отправленные приложением, должны умещаться в одном датаграмме.
Примечание: DTLS-специфичные заголовки добавляют некоторую избыточность к данным приложения, что ещё больше уменьшает возможный размер сообщения.
Предупреждение: Сервер, настроенный на ответ с HelloVerifyRequest, отбросит все фрагментированные сообщения ClientHello, никогда не начав рукопожатие.
Примеры сервера DTLS и клиента DTLS иллюстрируют, как использовать QDtls в приложениях.
См. также QUdpSocket, QDtlsClientVerifier, HandshakeState, QDtlsError и QSslConfiguration.
Документация по типам членов
[alias] QDtls::GeneratorParameters
[since 5.12] enum QDtls::HandshakeState
Описывает текущее состояние рукопожатия DTLS.
Этот перечисление описывает текущее состояние рукопожатия DTLS для подключения QDtls.
| Постоянная | Значение | Описание |
|---|---|---|
QDtls::HandshakeNotStarted |
0 |
Ничего ещё не сделано. |
QDtls::HandshakeInProgress |
1 |
Рукопожатие было инициировано, и пока не было обнаружено ошибок. |
QDtls::PeerVerificationFailed |
2 |
Не удалось установить идентичность партнёра. |
QDtls::HandshakeComplete |
3 |
Рукопожатие успешно завершено, и было установлено зашифрованное соединение. |
Этот перечисление был введён или изменён в Qt 5.12.
См. также QDtls::doHandshake() и QDtls::handshakeState().
Документация по функциям-членам
QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent = nullptr)
Создаёт объект QDtls, parent передаётся конструктору QObject. mode — QSslSocket::SslServerMode для подключения DTLS на стороне сервера или QSslSocket::SslClientMode для клиента.
См. также sslMode() и QSslSocket::SslMode.
[signal] void QDtls::handshakeTimeout()
Потеря пакетов может привести к таймаутам во время фазы рукопожатия. В этом случае QDtls посылает сигнал handshakeTimeout(). Вызовите handleTimeout(), чтобы повторно передать сообщения рукопожатия:
DtlsClient::DtlsClient()
{
// Some initialization code here ...
connect(&clientDtls, &QDtls::handshakeTimeout, this, &DtlsClient::handleTimeout);
}
void DtlsClient::handleTimeout()
{
clientDtls.handleTimeout(&clientSocket);
} См. также handleTimeout().
[signal] void QDtls::pskRequired(QSslPreSharedKeyAuthenticator *authenticator)
QDtls посылает этот сигнал, когда он согласует криптографический пакет PSK, и поэтому требуется аутентификация PSK.
При использовании PSK клиент должен отправить серверу действительный идентификатор и действительный предварительно согласованный ключ, чтобы продолжить TLS-рукопожатие. Приложения могут предоставить эту информацию в слоте, подключенном к этому сигналу, заполнив переданный объект authenticator в соответствии со своими потребностями.
Примечание: Игнорирование этого сигнала или не предоставление необходимых учетных данных приведёт к сбою рукопожатия, а следовательно, к прерыванию подключения.
Примечание: Объект authenticator принадлежит QDtls и не должен быть удалён приложением.
См. также QSslPreSharedKeyAuthenticator.
[virtual] QDtls::~QDtls()
Удаляет объект QDtls.
bool QDtls::abortHandshake(QUdpSocket *socket)
Прерывает текущее рукопожатие. Возвращает true, если оно происходило на socket; в противном случае устанавливает соответствующую ошибку и возвращает false.
См. также doHandshake() и resumeHandshake().
QDtls::GeneratorParameters QDtls::cookieGeneratorParameters() const
Возвращает текущий алгоритм хеширования и секрет, либо стандартные, либо ранее установленные вызовом setCookieGeneratorParameters().
Стандартный алгоритм хеширования — QCryptographicHash::Sha256, если Qt был сконфигурирован для его поддержки, в противном случае QCryptographicHash::Sha1. Стандартный секрет извлекается из криптографически стойкого псевдослучайного генератора чисел, специфичного для бэкенда.
См. также setCookieGeneratorParameters(), QDtlsClientVerifier и cookieGeneratorParameters().
QByteArray QDtls::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram)
Дешифрует dgram и возвращает его содержимое в виде простого текста. Рукопожатие должно быть завершено перед тем, как можно будет дешифровать датаграммы. В зависимости от типа TLS-сообщения подключение может записать в socket, который должен быть действительным указателем.
bool QDtls::doHandshake(QUdpSocket *socket, const QByteArray &dgram = {})
Инициализирует или продолжает DTLS-рукопожатие. socket должен быть действительным указателем. При запуске рукопожатия сервера DTLS, dgram должно содержать начальное сообщение ClientHello, прочитанное из QUdpSocket. Эта функция возвращает true если не было обнаружено ошибок. Состояние рукопожатия можно проверить с помощью handshakeState(). false означает, что произошла ошибка, используйте dtlsError() для получения более подробной информации.
Примечание: Если идентичность партнёра не может быть установлена, ошибка устанавливается в QDtlsError::PeerVerificationError. Если вы хотите проигнорировать ошибки проверки и продолжить подключение, вы должны вызвать ignoreVerificationErrors() и затем resumeHandshake(). Если ошибки нельзя проигнорировать, вы должны вызвать abortHandshake().
if (!dtls.doHandshake(&socket, dgram)) {
if (dtls.dtlsError() == QDtlsError::PeerVerificationError)
dtls.abortAfterError(&socket);
} См. также handshakeState(), dtlsError(), ignoreVerificationErrors(), resumeHandshake() и abortHandshake().
QSslConfiguration QDtls::dtlsConfiguration() const
Возвращает либо стандартную конфигурацию DTLS, либо конфигурацию, установленную предыдущим вызовом setDtlsConfiguration().
См. также setDtlsConfiguration() и QSslConfiguration::defaultDtlsConfiguration().
QDtlsError QDtls::dtlsError() const
Возвращает последнюю ошибку, возникшую при соединении, или QDtlsError::NoError.
См. также dtlsErrorString() и QDtlsError.
QString QDtls::dtlsErrorString() const
Возвращает текстовое описание последней возникшей ошибки при соединении или пустую строку.
См. также dtlsError().
bool QDtls::handleTimeout(QUdpSocket *socket)
Если во время рукопожатия происходит таймаут, посылается сигнал handshakeTimeout(). Приложение должно вызвать handleTimeout(), чтобы повторно передать сообщения рукопожатия; handleTimeout() возвращает true если таймаут произошёл, в противном случае false. socket должен быть действительным указателем.
См. также handshakeTimeout().
QDtls::HandshakeState QDtls::handshakeState() const
Возвращает текущее состояние рукопожатия для данного QDtls.
См. также doHandshake() и QDtls::HandshakeState.
void QDtls::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore)
Этот метод сообщает QDtls, что нужно игнорировать только ошибки, указанные в errorsToIgnore.
Если, например, вы хотите подключиться к серверу, использующему самозаверяющий сертификат, рассмотрите следующий фрагмент:
QList<QSslCertificate> cert = QSslCertificate::fromPath(QLatin1String("server-certificate.pem"));
QSslError error(QSslError::SelfSignedCertificate, cert.at(0));
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error);
QDtls dtls;
dtls.ignoreVerificationErrors(expectedSslErrors);
dtls.doHandshake(udpSocket); Вы также можете вызвать эту функцию после того, как doHandshake() столкнется с ошибкой QDtlsError::PeerVerificationError, а затем возобновить установление соединения, вызвав resumeHandshake().
Позже вызовы этой функции заменят список ошибок, переданных в предыдущих вызовах. Вы можете очистить список ошибок, которые вы хотите игнорировать, вызвав эту функцию со пустым списком.
См. также doHandshake(), resumeHandshake() и QSslError.
bool QDtls::isConnectionEncrypted() const
Возвращает true если рукопожатие DTLS успешно завершено.
См. также doHandshake() и handshakeState().
quint16 QDtls::mtuHint() const
Возвращает значение, ранее заданное с помощью setMtuHint(). Значение по умолчанию равно 0.
См. также setMtuHint().
QHostAddress QDtls::peerAddress() const
Возвращает адрес удаленного узла, заданный с помощью setPeer(), или QHostAddress::Null.
См. также setPeer().
quint16 QDtls::peerPort() const
Возвращает номер порта удаленного узла, заданный с помощью setPeer(), или 0.
См. также setPeer().
QList<QSslError> QDtls::peerVerificationErrors() const
Возвращает ошибки, обнаруженные при установлении личности удаленного узла.
Если вы хотите продолжить подключение, несмотря на возникшие ошибки, вы должны вызвать ignoreVerificationErrors().
QString QDtls::peerVerificationName() const
Возвращает имя хоста, заданное с помощью setPeer() или setPeerVerificationName(). Значение по умолчанию — пустая строка.
См. также setPeerVerificationName() и setPeer().
bool QDtls::resumeHandshake(QUdpSocket *socket)
Если ошибки проверки подлинности удаленного узла были проигнорированы во время рукопожатия, resumeHandshake() возобновляет и завершает рукопожатие и возвращает true. socket должен быть допустимым указателем. Возвращает false если рукопожатие не удалось возобновить.
См. также doHandshake(), abortHandshake(), peerVerificationErrors() и ignoreVerificationErrors().
QSslCipher QDtls::sessionCipher() const
Возвращает криптографический шифр, используемый этим соединением, или нулевой шифр, если соединение не зашифровано. Шифр для сеанса выбирается во время фазы рукопожатия. Шифр используется для шифрования и дешифрования данных.
QSslConfiguration предоставляет функции для задания упорядоченного списка шифров, из которого фаза рукопожатия в конечном итоге выберет сеансовый шифр. Этот упорядоченный список должен быть задан перед началом фазы рукопожатия.
См. также QSslConfiguration, setDtlsConfiguration() и dtlsConfiguration().
QSsl::SslProtocol QDtls::sessionProtocol() const
Возвращает версию протокола DTLS, используемую этим соединением, или UnknownProtocol, если соединение еще не зашифровано. Протокол для соединения выбирается во время фазы рукопожатия.
setDtlsConfiguration() может установить предпочтительную версию перед началом рукопожатия.
См. также setDtlsConfiguration(), QSslConfiguration, QSslConfiguration::defaultDtlsConfiguration() и QSslConfiguration::setProtocol().
bool QDtls::setCookieGeneratorParameters(const QDtls::GeneratorParameters ¶ms)
Устанавливает алгоритм криптографического хеширования и секрет из params. Эта функция необходима только для соединения QDtls на стороне сервера. Возвращает true при успехе.
Примечание: Эту функцию необходимо вызвать перед началом рукопожатия.
См. также cookieGeneratorParameters(), doHandshake(), QDtlsClientVerifier и QDtlsClientVerifier::cookieGeneratorParameters().
bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration)
Устанавливает конфигурацию TLS соединения из configuration и возвращает true при успехе.
Примечание: Эту функцию необходимо вызвать перед началом рукопожатия.
См. также dtlsConfiguration() и doHandshake().
void QDtls::setMtuHint(quint16 mtuHint)
mtuHint — максимальный размер пакета (MTU), либо обнаруженный, либо угаданный приложением. Приложение не обязано задавать это значение.
См. также mtuHint() и QAbstractSocket::PathMtuSocketOption.
bool QDtls::setPeer(const QHostAddress &address, quint16 port, const QString &verificationName = {})
Устанавливает адрес, порт и имя хоста удаленного узла и возвращает true при успехе. address не должен быть нулевым, мультикастным или широковещательным. verificationName — имя хоста, используемое для проверки сертификата.
См. также peerAddress(), peerPort() и peerVerificationName().
bool QDtls::setPeerVerificationName(const QString &name)
Устанавливает имя хоста name, которое будет использоваться для проверки сертификата, и возвращает true при успехе.
Примечание: Эту функцию необходимо вызвать перед началом рукопожатия.
См. также peerVerificationName() и setPeer().
bool QDtls::shutdown(QUdpSocket *socket)
Отправляет зашифрованное сообщение о закрытии и закрывает соединение DTLS. Состояние рукопожатия меняется на QDtls::HandshakeNotStarted. socket должен быть допустимым указателем. Эта функция возвращает true при успехе.
См. также doHandshake().
QSslSocket::SslMode QDtls::sslMode() const
Возвращает QSslSocket::SslServerMode для соединения на стороне сервера и QSslSocket::SslClientMode для клиента.
См. также QDtls() и QSslSocket::SslMode.
qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)
Шифрует dgram и записывает зашифрованные данные в socket. Возвращает количество записанных байтов или -1 в случае ошибки. Рукопожатие должно быть завершено перед записью зашифрованных данных. socket должен быть допустимым указателем.
См. также doHandshake(), handshakeState(), isConnectionEncrypted() и dtlsError().
Связанные нечлены
[since 5.12] перечисление QDtlsError
Описывает ошибки, которые могут быть обнаружены QDtls и QDtlsClientVerifier.
Это перечисление описывает общие и специфичные для TLS ошибки, которые могут возникнуть у объектов классов QDtlsClientVerifier и QDtls.
| Константа | Значение | Описание |
|---|---|---|
QDtls::QDtlsError::NoError |
0 |
Ошибка не произошла, последнее действие выполнено успешно. |
QDtls::QDtlsError::InvalidInputParameters |
1 |
Введённые вызывающим объектом параметры некорректны. |
QDtls::QDtlsError::InvalidOperation |
2 |
Попытка выполнить операцию в состоянии, которое её не допускает. |
QDtls::QDtlsError::UnderlyingSocketError |
3 |
QUdpSocket::writeDatagram() завершился неудачей, QUdpSocket::error() и QUdpSocket::errorString() могут предоставить более подробную информацию. |
QDtls::QDtlsError::RemoteClosedConnectionError |
4 |
Получено сообщение об отключении TLS. |
QDtls::QDtlsError::PeerVerificationError |
5 |
Идентичность удалённого узла не могла быть проверена во время рукопожатия TLS. |
QDtls::QDtlsError::TlsInitializationError |
6 |
Произошла ошибка при инициализации базового TLS-обработчика. |
QDtls::QDtlsError::TlsFatalError |
7 |
Произошла фатальная ошибка во время рукопожатия TLS, отличная от ошибки проверки подлинности удалённого узла или ошибки инициализации TLS. |
QDtls::QDtlsError::TlsNonFatalError |
8 |
Ошибка шифрования или дешифрования датаграммы, не фатальная, означающая, что QDtls может продолжить работу после этой ошибки. |
Это перечисление было введено или изменено в Qt 5.12.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qdtls.html