本页内容

QDtlsClientVerifier Class

该类实现了服务器端的 DTLS cookie 生成和验证。更多内容...

头文件: #include <QDtlsClientVerifier>
CMake: find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(mytarget PRIVATE Qt6::Network)
qmake: QT += network
继承自: QObject

公共类型

公共函数

QDtlsClientVerifier(QObject *parent = nullptr)
virtual ~QDtlsClientVerifier()
QDtlsClientVerifier::GeneratorParameters cookieGeneratorParameters() const
QDtlsError dtlsError() const
QString dtlsErrorString() const
bool setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters &params)
QByteArray verifiedHello() const
bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)

详细说明

QDtlsClientVerifier 类实现了服务器端的 DTLS Cookie 生成和验证。数据报安全协议极易受到各种拒绝服务攻击的影响。根据RFC 6347 第 4.2.1 节,以下是两种较为常见的攻击类型:

  • 攻击者发送一系列握手初始化请求,导致服务器分配过多资源,并可能执行耗时的加密操作。
  • 攻击者发送一系列伪造了受害者源地址的握手初始化请求,使服务器充当信号放大器。通常,服务器会向受害者机器发送证书消息作为响应,而该消息可能体积庞大,从而导致受害者机器被数据报淹没。

作为应对这些攻击的对策,RFC 6347 第 4.2.1 节提出了一种服务器可部署的无状态 Cookie 技术:

  • 在响应初始的 ClientHello 消息时,服务器会发送一个包含 cookie 的 HelloVerifyRequest。该 cookie 是一个加密哈希值,由客户端的地址、端口号以及服务器的密钥(即一个具有强加密性的伪随机字节序列)生成。
  • 可访问的 DTLS 客户端应回复一条包含该 Cookie 的新 ClientHello 消息。
  • 当服务器接收到包含 Cookie 的 ClientHello 消息时,它会按照上述方法生成一个新的 Cookie。将此新 Cookie 与 ClientHello 消息中的 Cookie 进行比对。
  • 如果两个 Cookie 相等,则认为客户端是真实的,服务器可以继续进行 TLS 握手过程。

注意: DTLS服务器 并不要求使用 DTLS cookie。

QDtlsClientVerifier 设计为与QUdpSocket 配对使用,如下面的代码片段所示:

class DtlsServer : public QObject
{
public:
    bool listen(const QHostAddress &address, quint16 port);
    // ...

private:
    void readyRead();
    // ...

    QUdpSocket serverSocket;
    QDtlsClientVerifier verifier;
    // ...
};

bool DtlsServer::listen(const QHostAddress &serverAddress, quint16 serverPort)
{
    if (serverSocket.bind(serverAddress, serverPort))
        connect(&serverSocket, &QUdpSocket::readyRead, this, &DtlsServer::readyRead);
    return serverSocket.state() == QAbstractSocket::BoundState;
}

void DtlsServer::readyRead()
{
    QByteArray dgram(serverSocket.pendingDatagramSize(), Qt::Uninitialized);
    QHostAddress address;
    quint16 port = {};
    serverSocket.readDatagram(dgram.data(), dgram.size(), &address, &port);
    if (verifiedClients.contains({address, port}) {
        // This client was verified previously, we either continue the
        // handshake or decrypt the incoming message.
    } else if (verifier.verifyClient(&serverSocket, dgram, address, port)) {
        // Apparently we have a real DTLS client who wants to send us
        // encrypted datagrams. Remember this client as verified
        // and proceed with a handshake.
    } else {
        // No matching cookie was found in the incoming datagram,
        // verifyClient() has sent a ClientVerify message.
        // We'll hear from the client again soon, if they're real.
    }
}

QDtlsClientVerifier 对应用程序使用QUdpSocket 的方式不作任何限制。例如,一台服务器上可以存在一个处于QAbstractSocket::BoundState 状态的QUdpSocket ,同时处理多个 DTLS 客户端:

  • 测试新客户端是否为真正的支持 DTLS 的客户端。
  • 与经过验证的客户端完成 TLS 握手(参见QDtls )。
  • 对来自已连接客户端的数据报进行解密(参见QDtls )。
  • 向已连接的客户端发送加密数据报(参见QDtls )。

这意味着 QDtlsClientVerifier 不会直接从套接字读取数据,而是期望应用程序读取传入的数据报,提取发送方的地址和端口,然后将这些数据传递给verifyClient()。要发送 HelloVerifyRequest 消息,verifyClient() 可以向QUdpSocket 写入数据。

注意:QDtlsClientVerifier 不会获取QUdpSocket 对象的所有权。

默认情况下,QDtlsClientVerifier 从加密强度高的伪随机数生成器中获取其密钥。

注意:默认密钥由 QDtlsClientVerifier 和QDtls 类的所有对象共享。由于这可能会带来安全风险,RFC 6347 建议频繁更改服务器的密钥。 有关服务器实现的建议,请参阅RFC 6347 第 4.2.1 节。Cookie 生成器的参数可通过类 `QDtlsClientVerifier::GeneratorParameters ` 和方法 `setCookieGeneratorParameters()` 进行设置:

void DtlsServer::updateServerSecret()
{
    const QByteArray newSecret(generateCryptoStrongSecret());
    if (newSecret.size()) {
        usedCookies.append(newSecret);
        verifier.setCookieGeneratorParameters({QCryptographicHash::Sha1, newSecret});
    }
}

DTLS 服务器示例演示了如何在服务器应用程序中使用 QDtlsClientVerifier。

另请参阅 QUdpSocket 、QAbstractSocket::BoundState 、QDtls 、verifyClient()、GeneratorParameters 、setCookieGeneratorParameters()、cookieGeneratorParameters()、QDtls::setCookieGeneratorParameters()、QDtls::cookieGeneratorParameters()、QCryptographicHash::Algorithm 、QDtlsError 、dtlsError() 以及dtlsErrorString()。

成员函数文档

[explicit] QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent = nullptr)

创建一个 QDtlsClientVerifier 对象,并将parent 传递给QObject 的构造函数。

[virtual noexcept] QDtlsClientVerifier::~QDtlsClientVerifier()

销毁QDtlsClientVerifier 对象。

QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorParameters() const

返回当前用于生成 Cookie 的密钥和哈希算法。如果 Qt 在配置时支持该算法,则默认哈希算法为QCryptographicHash::Sha256 ;否则为QCryptographicHash::Sha1 。默认密钥来自后端特有的、具有强加密性的伪随机数生成器。

另请参阅 QCryptographicHash::Algorithm 、QDtlsClientVerifier::GeneratorParameters 以及setCookieGeneratorParameters()。

QDtlsError QDtlsClientVerifier::dtlsError() const

返回最近发生的错误,或QDtlsError::NoError 。

另请参阅 QDtlsError 和dtlsErrorString()。

QString QDtlsClientVerifier::dtlsErrorString() const

返回上次错误的文本描述,或一个空字符串。

另请参阅 ` dtlsError()`。

bool QDtlsClientVerifier::setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters &params)

从params 中设置密钥和密码学哈希算法。该QDtlsClientVerifier 将使用这些参数来生成Cookie。如果新密钥的大小为零,则该函数返回false ,且不会更改Cookie生成器的参数。

注意:密钥 应为一串具有密码学安全性的字节序列。

另请参阅 QDtlsClientVerifier::GeneratorParameters 、cookieGeneratorParameters() 以及QCryptographicHash::Algorithm 。

QByteArray QDtlsClientVerifier::verifiedHello() const

便捷函数。返回最后一条成功验证的 ClientHello 消息;如果尚未完成任何验证,则返回一个空的QByteArray 。

另请参阅 verifyClient()。

bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)

socket 必须是一个有效的指针,dgram 必须是一个非空的数据报,address 不能为空、广播或组播。port 是远程对等方的端口。如果dgram 中包含一个带有有效cookie的ClientHello消息,则该函数返回true 。如果未找到匹配的cookie,verifyClient()将使用socket 发送一个HelloVerifyRequest消息,并返回false 。

以下代码片段展示了服务器应用程序如何检查错误:

if (!verifier.verifyClient(&socket, message, address, port)) {
    switch (verifyClient.dtlsError()) {
    case QDtlsError::NoError:
        // Not verified yet, but no errors found and we have to wait for the next
        // message from this client.
        return;
    case QDtlsError::TlsInitializationError:
        // This error is fatal, nothing we can do about it.
        // Probably, quit the server after reporting the error.
        return;
    case QDtlsError::UnderlyingSocketError:
        // There is some problem in QUdpSocket, handle it (see QUdpSocket::error())
        return;
    case QDtlsError::InvalidInputParameters:
    default:
        Q_UNREACHABLE();
    }
}

另请参阅 QHostAddress::isNull()、QHostAddress::isBroadcast()、QHostAddress::isMulticast()、setCookieGeneratorParameters() 以及cookieGeneratorParameters()。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.