Qt网络编程:QDtls

一、描述

QDtls 类可用于使用用户数据报协议 (UDP) 与网络对等点建立安全连接。

基于基本无连接 UDP 的 DTLS 连接意味着两个对等方首先必须通过调用 doHandshake() 成功完成 TLS 握手。握手完成后,可以使用 writeDatagramEncrypted() 将加密的数据报发送到对等方。来自对等方的加密数据报可以通过decryptDatagram() 解密。

QDtls 旨在与 QUdpSocket 一起使用。由于 QUdpSocket 可以接收来自不同对等方的数据报,因此应用程序必须实现多路分解,将来自不同对等方的数据报转发到其对应的 QDtls 实例。网络对等体与其 QDtls 对象之间的关联可以使用对等体的地址和端口号来建立。在开始握手之前,应用程序必须使用 setPeer() 设置对等方的地址和端口号。

QDtls 不从 QUdpSocket 读取数据报,这应该由应用程序完成,例如在QUdpSocket::readyRead() 信号的槽函数中读取数据报。然后,这些数据报必须由 QDtls 处理。

通常,在握手阶段,双方将接收和发送几个数据报。在读取数据报时,服务器和客户端必须将这些数据报传递给 doHandshake(),直到发现某些错误或 handshakeState() 返回 HandshakeComplete

 // 客户端发起握手:
 QUdpSocket clientSocket;
 QDtls clientDtls;
 clientDtls.setPeer(address, port, peerName);
 clientDtls.doHandshake(&clientSocket);

 // 接受传入连接的服务器; 地址、端口、clientHello 由 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);

 // 握手完成,对于服务器和客户端:
 void DtlsConnection::continueHandshake(const QByteArray &datagram)
 {
     if (dtls.doHandshake(&udpSocket, datagram)) 
     {
         // Check handshake status:
         if (dtls.handshakeStatus() == QDlts::HandshakeComplete) 
         {
             // 安全 DTLS 连接现已建立。
         }
     }
     else 
     {
         // Error handling.
     }
 }

对于服务器,对 doHandshake() 的第一次调用需要一个包含 ClientHello 消息的非空数据报。 如果服务器也部署了 QDtlsClientVerifier,那么第一条 ClientHello 消息应该是 QDtlsClientVerifier 验证过的消息。

如果在握手期间无法验证对等方的身份,应用程序必须检查 peerVerificationErrors() 返回的错误,然后通过调用 ignoreVerificationErrors() 忽略错误或通过调用 abortHandshake() 中止握手。 如果错误被忽略,则可以通过调用 resumeHandshake() 来恢复握手。

握手完成后,数据报可以安全地发送到网络对等点并从网络对等点接收:

 // 发送加密数据报:
 dtlsConnection.writeDatagramEncrypted(&clientSocket, "Hello DTLS server!");

 // 解密:
 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 消息。

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

 QSslConfiguration config = QSslConfiguration::defaultDtlsConfiguration();
 config.setDtlsCookieVerificationEnabled(false);
 dtlsConnection.setDtlsConfiguration(config);


二、相关非成员

1、enum class QDtlsError:此枚举描述了 QDtlsClientVerifier 和 QDtls 类的对象可能遇到的一般错误和特定于 TLS 的错误。

  • QDtls::QDtlsError::NoError:没有发生错误,上次操作成功。
  • QDtls::QDtlsError::InvalidInputParameters:调用者提供的输入参数无效。
  • QDtls::QDtlsError::InvalidOperation:在不允许的状态下尝试操作。
  • QDtls::QDtlsError::UnderlyingSocketError:QUdpSocket::writeDatagram() 失败,QUdpSocket::error() 和 QUdpSocket::errorString() 可以提供更具体的信息。
  • QDtls::QDtlsError::RemoteClosedConnectionError:收到 TLS 关闭警报消息。
  • QDtls::QDtlsError::PeerVerificationError:在 TLS 握手期间无法验证对等方的身份。
  • QDtls::QDtlsError::TlsInitializationError:初始化底层 TLS 后端时出错。
  • QDtls::QDtlsError::TlsFatalError:TLS 握手期间发生了致命错误,而不是对等验证错误或 TLS 初始化错误。
  • QDtls::QDtlsError::TlsNonFatalError:加密或解密数据报失败,非致命的,QDtls 可以在出现此错误后继续工作。

三、类型成员

1、enum QDtls::HandshakeState:此枚举描述了 QDtls 连接的 DTLS 握手的当前状态。

  • HandshakeNotStarted:未开始连接。
  • HandshakeInProgress:握手已启动,目前未发现任何错误。
  • PeerVerificationFailed:无法建立对等方的身份。
  • HandshakeComplete:握手成功完成并建立加密连接。

四、成员函数

1、[signal] void handshakeTimeout()

握手超时信号。数据包丢失可能导致握手阶段超时。在这种情况下,QDtls 发出此信号。

调用 handleTimeout() 重传握手消息:

 DtlsClient::DtlsClient()
 {
     connect(&clientDtls, &QDtls::handshakeTimeout, this, &DtlsClient::handleTimeout);
 }

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

2、[signal] void pskRequired(QSslPreSharedKeyAuthenticator *authenticator)

QDtls 在协商 PSK 密码套件时发出此信号。

使用 PSK 时,客户端必须向服务器发送有效身份和有效的预共享密钥,以便 TLS 握手继续。 应用程序可以在连接到这个信号的槽函数中提供相应信息,根据需要填充传递的验证器对象。

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

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

3、bool abortHandshake(QUdpSocket *socket)

中止正在进行的握手。成功则返回 true,否则设置一个合适的错误并返回 false。

4、QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram)

解密 dgram 并以纯文本形式返回其内容。

5、bool doHandshake(QUdpSocket *socket, const QByteArray &dgram = {})

开始或继续 DTLS 握手。开始服务器端 DTLS 握手时,dgram 必须包含从 QUdpSocket 读取的初始 ClientHello 消息。 未发现错误返回 true。

6、bool handleTimeout(QUdpSocket *socket)

如果握手期间发生超时,则会发出 handshakeTimeout() 信号,这时可以调用此函数来重新传输握手消息。

7、void ignoreVerificationErrors(const QVector &errorsToIgnore)

忽略在 errorsToIgnore 中给出的错误。

也可以在 doHandshake() 遇到 QDtlsError::PeerVerificationError 错误后调用此函数,然后通过调用 resumeHandshake() 恢复握手

例如,如果想连接到使用自签名证书的服务器,可以考虑使用以下代码段:

 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);

8、bool isConnectionEncrypted()

DTLS 是否握手成功完成。

9、QVectorpeerVerificationErrors()

返回对等体身份验证时发现的错误。如果想在发生错误的情况下继续连接,必须调用 ignoreVerificationErrors()。

10、bool resumeHandshake(QUdpSocket *socket)

如果在握手期间忽略对等身份验证错误,此函数将恢复并完成握手并返回 true。 

11、QSslCipher sessionCipher()

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

QSslConfiguration 提供了用于设置密码的有序列表的功能,握手阶段最终将从中选择会话密码。 

12、QSsl::SslProtocol sessionProtocol()

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

setDtlsConfiguration() 可以在握手开始之前设置首选版本。

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

设置密码哈希算法和来自 params 的秘密。此函数仅用于服务器端 QDtls 连接。

14、bool setDtlsConfiguration(const QSslConfiguration &configuration)

设置连接的 TLS 配置,如果成功则返回 true。这个函数必须在握手开始前调用。

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

设置对等方的地址、端口和主机名。 地址不得为空、多播地址、广播地址。verifyName 是用于证书验证的主机名。

16、bool setPeerVerificationName(const QString &name)

设置将用于证书验证的主机名。

17、bool shutdown(QUdpSocket *socket)

发送加密关闭警报消息并关闭 DTLS 连接。握手状态更改为 QDtls::HandshakeNotStarted

18、QSslSocket::SslMode sslMode()

为服务器端连接返回 QSslSocket::SslServerMode

为客户端返回 QSslSocket::SslClientMode

19、qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)

加密 dgram 并将加密数据写入socket。 返回写入的字节数,如果出错则返回 -1。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值