QDtls 类

此类为 UDP 套接字提供加密。 更多...

头文件 #include <QDtls>
CMakefind_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(mytarget PRIVATE Qt6::Network)
qmakeQT += network
继承 QObject

公共类型

GeneratorParameters
枚举HandshakeState { HandshakeNotStarted, HandshakeInProgress, PeerVerificationFailed, HandshakeComplete }

公共函数

QDtls(QSslSocket::SslMode mode, QObject *parent = nullptr)
虚拟~QDtls()
boolabortHandshake(QUdpSocket *socket)
QDtls::GeneratorParameterscookieGeneratorParameters() const
QByteArraydecryptDatagram(QUdpSocket *socket, const QByteArray &dgram)
booldoHandshake(QUdpSocket *socket, const QByteArray &dgram = {})
QSslConfigurationdtlsConfiguration() const
QDtlsErrordtlsError() const
QStringdtlsErrorString() const
boolhandleTimeout(QUdpSocket *socket)
QDtls::HandshakeStatehandshakeState() const
voidignoreVerificationErrors(const QList<QSslError> &errorsToIgnore)
boolisConnectionEncrypted() const
quint16mtuHint() const
QHostAddresspeerAddress() const
quint16peerPort() const
QList<QSslError>peerVerificationErrors() const
QStringpeerVerificationName() const
boolresumeHandshake(QUdpSocket *socket)
QSslCiphersessionCipher() const
QSsl::SslProtocolsessionProtocol() const
boolsetCookieGeneratorParameters(const QDtls::GeneratorParameters &params)
boolsetDtlsConfiguration(const QSslConfiguration &configuration)
voidsetMtuHint(quint16 mtuHint)
boolsetPeer(const QHostAddress &address, quint16 port, const QString &verificationName = {})
boolsetPeerVerificationName(const QString &name)
boolshutdown(QUdpSocket *socket)
QSslSocket::SslModesslMode() const
qint64writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)

信号

void握手超时()
voidpskRequired(QSslPreSharedKeyAuthenticator *authenticator)
枚举类QDtlsError { NoError, InvalidInputParameters, InvalidOperation, UnderlyingSocketError, RemoteClosedConnectionError, …, TlsNonFatalError }

详细描述

QDtls类可用来通过用户数据报协议(UDP)与网络对等实体建立安全连接。在无连接的UDP上进行的DTLS连接意味着两个对等实体必须首先成功完成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);

可以使用shutdown()关闭DTLS连接。

DtlsClient::~DtlsClient()
{
    clientDtls.shutdown(&clientSocket);
}

警告:如果您计划稍后重新使用相同的端口号连接到服务器,则建议在销毁客户端的QDtls对象之前调用shutdown()。否则,服务器可能会丢弃传入的ClientHello消息,有关更多信息和建议,请参阅RFC 6347,第4.2.8节

如果服务器不使用QDtlsClientVerifier,它必须配置其QDtls对象以禁用cookie验证程序。

auto config = QSslConfiguration::defaultDtlsConfiguration();
config.setDtlsCookieVerificationEnabled(false);
// Some other customization ...
dtlsConnection.setDtlsConfiguration(config);

使用非默认生成器参数进行cookie验证的服务器必须在开始握手之前为其QDtls对象设置相同的参数。

注意:DTLS协议将最大传输单元(PMTU)发现留给应用程序。应用程序可以使用setMtuHint()()提供QDtls的MTU。此提示仅影响握手阶段,因为只有握手消息可以被DTLS进行分段和重新组装。应用程序发送的所有其他消息必须适合单个数据报。

注意:DTLS特定标题会增加应用程序数据的一些开销,从而进一步减小可能的消息大小。

警告:配置为用HelloVerifyRequest进行回复的服务器将丢弃所有分段客户端Hello消息,永远不启动握手。

DTLS服务器DTLS客户端示例说明了如何在应用程序中使用QDtls。

另请参阅:QUdpSocketQDtlsClientVerifierHandshakeStateQDtlsErrorQSslConfiguration

成员类型文档

[别名] QDtls::GeneratorParameters

enum QDtls::HandshakeState

描述DTLS握手当前状态。

此枚举描述了QDtls连接的DTLS握手的当前状态。

常量描述
QDtls::HandshakeNotStarted0尚未进行任何操作。
QDtls::HandshakeInProgress1握手已启动且迄今为止未发现任何错误。
QDtls::PeerVerificationFailed2无法建立对等方的身份。
QDtls::HandshakeComplete3握手成功完成并建立了加密连接。

另请参阅:QDtls::doHandshake()和QDtls::handshakeState

成员函数文档

[显式] QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent = nullptr)

创建一个QDtls对象,parent传递给QObject构造函数。 mode为服务器端DTLS连接的QSslSocket::SslServerMode或客户端的QSslSocket::SslClientMode

另请参阅:sslMode()和QSslSocket::SslMode

[虚拟 noexcept] QDtls::~QDtls()

销毁QDtls对象。

bool QDtls::abortHandshake(QUdpSocket *socket)

中止当前的握手。如果socket上正在进行一个,则返回true;否则,设置合适的错误并返回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必须包含从QUdpSocket读取的初始ClientHello消息。此函数在没有发现错误的情况下返回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::handshakeTimeout()

数据包丢失可能导致握手阶段出现超时。在这种情况下,QDtls 会发出 handshakeTimeout() 信号。调用 handleTimeout() 重新发送握手消息

DtlsClient::DtlsClient()
{
    // Some initialization code here ...
    connect(&clientDtls, &QDtls::handshakeTimeout, this, &DtlsClient::handleTimeout);
}

void DtlsClient::handleTimeout()
{
    clientDtls.handleTimeout(&clientSocket);
}

另请参阅handleTimeout

void QDtls::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore)

此方法告诉 QDtls 忽略仅在 errorsToIgnore 中给出的错误。

例如,如果您想连接到使用自签名证书的服务器,请考虑以下片段

QList<QSslCertificate> cert = QSslCertificate::fromPath("server-certificate.pem"_L1);
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() 继续握手。

稍后对此函数的调用将替换先前调用中传入的错误列表。您可以通过调用此函数并传入空列表来清除要忽略的错误列表。

另请参阅doHandshakeresumeHandshakeQSslError

bool QDtls::isConnectionEncrypted() const

如果 DTLS 握手成功完成,则返回 true

另请参阅doHandshakehandshakeState

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()。

[信号] void QDtls::pskRequired(QSslPreSharedKeyAuthenticator *authenticator)

QDtls在协商PSK加密套件时发出此信号,因此需要PSK认证。

当使用PSK时,客户端必须向服务器发送有效的身份和有效的预共享密钥,以便TLS握手继续。应用程序可以通过将连接到此信号的槽中提供此信息,并根据需要填充传递的authenticator对象来实现。

注意: 忽略此信号或未能提供所需的凭据,将导致握手失败,因此连接将被终止。

注意: authenticator对象由QDtls所有,应用程序不得删除。

另见 QSslPreSharedKeyAuthenticator

bool QDtls::resumeHandshake(QUdpSocket *socket)

如果在握手过程中忽略了对方验证错误,resumeHandshake()将恢复并完成握手并返回truesocket必须是一个有效的指针。如果无法恢复握手,则返回false

另见 doHandshake(),abortHandshake(),peerVerificationErrors()和ignoreVerificationErrors

QSslCipher QDtls::sessionCipher() const

返回此连接使用的加密加密器,如果不是加密连接,则返回空加密器。在握手阶段选择会话加密器。加密用于加密和解密数据。

QSslConfiguration提供了设置握手阶段最终选择会话加密器的加密器顺序列表的函数。此有序列表必须在握手阶段开始之前就绪。

另见 QSslConfigurationsetDtlsConfigurationdtlsConfiguration

QSsl::SslProtocol QDtls::sessionProtocol() const

返回此连接使用的DTLS协议版本,如果连接尚未加密,则返回UnknownProtocol。连接的协议是在握手阶段选择的。

在握手开始之前,可以使用setDtlsConfiguration设置首选版本。

另见 setDtlsConfigurationQSslConfigurationQSslConfiguration::defaultDtlsConfigurationQSslConfiguration::setProtocol

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

params设置加密散列算法和密钥。此函数仅用于服务器端QDtls连接。如果成功,则返回true

注意:此函数必须在握手开始前调用。

另请参阅:cookieGeneratorParameters(),doHandshake(),QDtlsClientVerifierQDtlsClientVerifier::cookieGeneratorParameters

bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration)

configuration设置连接的TLS配置并返回成功的true

注意:此函数必须在握手开始前调用。

另请参阅:dtlsConfiguration()和doHandshake

void QDtls::setMtuHint(quint16 mtuHint)

mtuHint是最大传输单元(MTU),可以是应用程序发现或猜测的。应用程序不必设置此值。

另请参阅:mtuHint()和QAbstractSocket::PathMtuSocketOption

bool QDtls::setPeer(const QHostAddress &address, quint16 port, const QString &verificationName = {})

设置对等方的地址、端口和主机名,如果成功则返回trueaddress不得为空、组播或多播。verificationName是进行证书验证所使用的主机名。

另请参阅:peerAddresspeerPortpeerVerificationName

bool QDtls::setPeerVerificationName(const QString &name)

设置用于证书验证的名称,如果成功则返回true

注意:此函数必须在握手开始前调用。

另请参阅:peerVerificationNamesetPeer

bool QDtls::shutdown(QUdpSocket *socket)

发送加密的关闭警告消息并关闭DTLS连接。握手状态更改为QDtls::HandshakeNotStartedsocket必须是一个有效的指针。此函数成功时返回true

另请参阅:doHandshake()。

QSslSocket::SslMode QDtls::sslMode() const

对于服务器端连接,返回QSslSocket::SslServerMode,对于客户端则返回QSslSocket::SslClientMode

另请参阅:QDtlsQSslSocket::SslMode

qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)

dgram进行加密并将其加密数据写入socket。返回写入的字节数,或者在出错的情况下返回-1。必须完成握手才能写入加密数据。socket必须是一个有效的指针。

另请参阅doHandshake(),handshakeState(),isConnectionEncrypted()和dtlsError()。

相关非成员

枚举类QDtlsError

描述QDtlsQDtlsClientVerifier可以找到的错误。

此枚举描述了QDtlsClientVerifier类和QDtls类对象可能遇到的通用和TLS特定错误。

常量描述
QDtls::QDtlsError::NoError0没有发生错误,上次操作成功。
QDtls::QDtlsError::InvalidInputParameters1调用者提供的输入参数无效。
QDtls::QDtlsError::InvalidOperation2在一个不允许该操作的状态下尝试了一个操作。
QDtls::QDtlsError::UnderlyingSocketError3QUdpSocket::writeDatagram()失败,QUdpSocket::error()和QUdpSocket::errorString()可以提供更具体的信息。
QDtls::QDtlsError::RemoteClosedConnectionError4收到了TLS关闭警报消息。
QDtls::QDtlsError::PeerVerificationError5在TLS握手过程中无法验证对等方的身份。
QDtls::QDtlsError::TlsInitializationError6在初始化底层TLS后端时出现错误。
QDtls::QDtlsError::TlsFatalError7在TLS握手过程中发生了致命错误,除了对等方验证错误或TLS初始化错误之外。
QDtls::QDtlsError::TlsNonFatalError8加密或解密数据包失败,非致命的,意味着QDtls在出现此错误后可以继续工作。

© 2024 The Qt Company Ltd。此处包含的文档贡献的版权属于其各自的所有者。此处提供的文档根据自由软件基金会发布的GNU自由文档许可协议版本1.3的条款进行许可。Qt及其相应标志是芬兰The Qt Company Ltd和/或其他国家的商标。所有其他商标均为其各自所有者的财产。