You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt C++中QSslServer获取客户端Peer Certificate始终为空的问题

解决Qt SSL服务器无法获取客户端证书的问题

问题根源

默认情况下QSslServer不会主动向客户端请求证书,因此即便客户端配置了本地证书,服务器也无法获取到。必须通过SSL配置明确要求客户端提交证书,并完成证书验证流程。

具体修复方案

1. 服务器端关键修改

(1)更新SSL配置,强制要求客户端提供证书

需要在服务器的QSslConfiguration中添加以下核心配置:

  • 设置peerVerifyMode为QSslSocket::VerifyPeer,强制服务器请求客户端证书并验证
  • 设置peerVerifyDepth指定证书链验证深度
  • 加载信任的CA证书(如果客户端是自签名证书,直接将客户端证书加入信任列表)

(2)更换监听信号

startedEncryptionHandshake信号在握手启动时触发,此时客户端证书尚未传递完成,应改用QSslSocket::encrypted信号,等握手完成后再读取客户端证书。

修改后的服务器代码:

#include <QCoreApplication>
#include <QtNetwork>
#include <QSslSocket>
#include <QSslKey>
#include <QSslCertificate>
#include <iostream>
#include <QFile>

int main(int argc, char **argv) {
    QCoreApplication app(argc, argv);

    // 服务器密钥、证书,以及用于验证客户端的CA证书(自签名场景用客户端证书即可)
    QFile serverKey("C:/Users/dharu/Documents/server/key.pem");
    QFile serverCertFile("C:\\Users\\dharu\\Documents\\server\\cert.pem");
    QFile clientCAFile("C:\\Users\\dharu\\Documents\\server\\cert.pem");

    serverKey.open(QIODevice::ReadOnly);
    serverCertFile.open(QIODevice::ReadOnly);
    clientCAFile.open(QIODevice::ReadOnly);

    QSslKey privateKey(serverKey.readAll(), QSsl::Rsa);
    QSslCertificate serverCert(serverCertFile.readAll());
    QSslCertificate clientCA(clientCAFile.readAll());

    // 配置SSL
    QSslConfiguration sslConfig;
    sslConfig.setPrivateKey(privateKey);
    sslConfig.setLocalCertificate(serverCert);
    // 强制要求客户端提供证书并验证
    sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
    sslConfig.setPeerVerifyDepth(1);
    // 添加信任的CA证书,用于验证客户端证书
    QList<QSslCertificate> caCerts = sslConfig.caCertificates();
    caCerts.append(clientCA);
    sslConfig.setCaCertificates(caCerts);

    QSslServer server;
    server.setSslConfiguration(sslConfig);
    server.listen(QHostAddress::Any, 5000);

    // 监听新连接,获取socket后等待握手完成
    QObject::connect(&server, &QSslServer::newConnection, [&](){
        QSslSocket *sock = qobject_cast<QSslSocket*>(server.nextPendingConnection());
        qDebug() << "Client connection initiated";

        // 握手完成后检查客户端证书
        QObject::connect(sock, &QSslSocket::encrypted, [sock](){
            qDebug() << "Handshake completed";
            if (sock->peerCertificate().isNull()) {
                qDebug() << "Cannot retrieve Client Certificate";
            } else {
                qDebug() << "Client Certificate is Obtained !";
                qDebug() << "Client Cert Subject:" << sock->peerCertificate().subjectInfo(QSslCertificate::CommonName);
            }
        });

        // 调试用:打印SSL错误
        QObject::connect(sock, &QSslSocket::sslErrors, [sock](const QList<QSslError>& errors){
            qDebug() << "SSL Errors:";
            for (const QSslError& err : errors) {
                qDebug() << err.errorString();
            }
            // 自签名证书场景可忽略错误继续连接
            sock->ignoreSslErrors();
        });
    });

    std::cout << "Started server at 5000!" << std::endl;

    return app.exec();
}

2. 客户端端优化(可选但推荐)

将客户端的peerVerifyMode改为QSslSocket::VerifyPeer,并加载服务器证书作为CA,确保客户端能验证服务器证书的合法性:

#include <QCoreApplication>
#include <QtNetwork>
#include <QSslSocket>
#include <QSslKey>
#include <QSslCertificate>
#include <iostream>

int main(int argc, char **argv) {
    QCoreApplication app(argc, argv);

    QFile clientKey("C:/Users/dharu/Documents/server/key.pem");
    QFile clientCertFile("C:\\Users\\dharu\\Documents\\server\\cert.pem");
    QFile serverCAFile("C:\\Users\\dharu\\Documents\\server\\cert.pem");

    clientKey.open(QIODevice::ReadOnly);
    clientCertFile.open(QIODevice::ReadOnly);
    serverCAFile.open(QIODevice::ReadOnly);

    QSslKey privateKey(clientKey.readAll(), QSsl::Rsa);
    QSslCertificate clientCert(clientCertFile.readAll());
    QSslCertificate serverCA(serverCAFile.readAll());

    QSslConfiguration sslConfig;
    sslConfig.setPrivateKey(privateKey);
    sslConfig.setLocalCertificate(clientCert);
    // 验证服务器证书
    sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
    QList<QSslCertificate> caCerts = sslConfig.caCertificates();
    caCerts.append(serverCA);
    sslConfig.setCaCertificates(caCerts);

    QSslSocket socket;
    std::cout << "setting Configuration .... " << std::endl;
    socket.setSslConfiguration(sslConfig);

    std::cout << "Connecting..." << std::endl;
    socket.connectToHostEncrypted("127.0.0.1", 5000);

    QObject::connect(&socket, &QSslSocket::encrypted, [&](){
        qDebug() << "Connected to server securely." << socket.isEncrypted();
        if(socket.peerCertificate().isNull())
            qDebug() << "Server Certificate is null";
        else
            qDebug() << "Server Certificate is Obtained";

        QByteArray requestData = socket.readAll();
        qDebug() << "Received from server:" << requestData;
    });

    // 调试用:打印客户端SSL错误
    QObject::connect(&socket, &QSslSocket::sslErrors, [&](const QList<QSslError>& errors){
        qDebug() << "Client SSL Errors:";
        for (const QSslError& err : errors) {
            qDebug() << err.errorString();
        }
        socket.ignoreSslErrors();
    });

    return app.exec();
}

核心说明

  • 服务器必须设置peerVerifyMode为VerifyPeer,这是触发客户端发送证书的关键配置
  • 必须确保服务器信任客户端的证书(通过添加到CA列表),否则握手会失败,无法获取证书
  • 必须在握手完成后(encrypted信号触发时)再读取客户端证书,避免提前读取导致为空

内容的提问来源于stack exchange,提问作者srilakshmikanthanp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:00:54