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 消息时,服务器发送一个 HelloVerifyRequest,其中包含一个 cookie。这个 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::GeneratorParameterssetCookieGeneratorParameters() 设置:

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 XML 被配置为支持哈希算法,则默认哈希算法为QCryptographicHash::Sha256 ,否则为QCryptographicHash::Sha1 。默认密文是从后端特定的强加密伪随机数生成器中获取的。

另请参阅 QCryptographicHash::AlgorithmQDtlsClientVerifier::GeneratorParameterssetCookieGeneratorParameters()。

QDtlsError QDtlsClientVerifier::dtlsError() const

返回上次发生的错误或QDtlsError::NoError

另请参阅 QDtlsErrordtlsErrorString() 。

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 必须是有效指针, 必须是非空数据报, 不能为空、广播或多播。 是远程对等设备的端口。如果 包含带有有效 cookie 的 ClientHello 消息,则此函数返回 。如果找不到匹配的 cookie,verifyClient() 将使用 发送 HelloVerifyRequest 消息,并返回 。dgram address port dgram true socket 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().

© 2025 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.