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
相关产品推荐
相关产品推荐

