Qt5客户端证书认证

Qt5 client certificate authentication

我在 Windows 8.1 上安装了 Qt 5.4.0,在 ArchLinux 上安装了 Qt 5.4.2,结果完全相同。

我的网站需要客户端 SSL 证书。自执行

以来服务器似乎已正确配置
openssl s_client -connect myserver:443 -cert client.crt -key client.key

打印

Verify return code: 0 (ok)

此外,

 curl --cert client.pem https://myserver/

工作正常。

服务器证书有效,浏览器接受它,等等。客户端证书是自签名的。以防万一,服务器是nginx,这里是相关的配置片段

listen                         *:443 ssl;

server_name                    myserver;

ssl                            on;
ssl_certificate                /etc/nginx/ssl/myserver.crt;
ssl_certificate_key            /etc/nginx/ssl/myserver.key;
ssl_dhparam                    /etc/nginx/ssl/myserver.dh;
ssl_protocols                  TLSv1.1 TLSv1.2;
ssl_ciphers                    "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_prefer_server_ciphers      on;
ssl_client_certificate         /etc/nginx/ssl/ca.crt;
ssl_verify_client              on;

不过下面最简单的Qt5应用

#include <qcoreapplication.h>
#include <qfile.h>
#include <qnetworkaccessmanager.h>
#include <qnetworkconfiguration.h>
#include <qnetworkproxy.h>
#include <qnetworkreply.h>
#include <qnetworkrequest.h>
#include <qsslcertificate.h>
#include <qsslconfiguration.h>
#include <qsslkey.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QNetworkProxyFactory::setUseSystemConfiguration(true);

    QSslConfiguration sslConfiguration;

    QFile privateKeyFile("client.key");
    privateKeyFile.open(QIODevice::ReadOnly);

    QFile certificateFile("client.crt");
    certificateFile.open(QIODevice::ReadOnly);

    QSslKey privateKey(&privateKeyFile, QSsl::Opaque);
    QSslCertificate certificate(&certificateFile);

    qWarning() << QSslSocket::supportsSsl();
    qWarning() << certificate.serialNumber();
    qWarning() << certificate.subjectInfo(QSslCertificate::CommonName);
    qWarning() << certificate.expiryDate();

    sslConfiguration.setPrivateKey(privateKey);
    sslConfiguration.setLocalCertificate(certificate);

    QNetworkRequest networkRequest(QUrl("https://server/"));

    networkRequest.setSslConfiguration(sslConfiguration);

    QNetworkAccessManager networkAccessManager;

    QNetworkReply* networkReply = networkAccessManager.get(networkRequest);

    QEventLoop loop;

    QObject::connect(&networkAccessManager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);

    loop.exec();

    qWarning() << networkReply->error();
    qWarning() << networkReply->errorString();

    delete networkReply;

    return a.exec();
}

在 windows

上出现以下控制台输出失败
QSslSocket: cannot resolve TLSv1_1_client_method
QSslSocket: cannot resolve TLSv1_2_client_method
QSslSocket: cannot resolve TLSv1_1_server_method
QSslSocket: cannot resolve TLSv1_2_server_method
QSslSocket: cannot resolve SSL_select_next_proto
QSslSocket: cannot resolve SSL_CTX_set_next_proto_select_cb
QSslSocket: cannot resolve SSL_get0_next_proto_negotiated
true
"01"
("AA-00-00-00")
QDateTime("2035-06-21 21:41:13.000 UTC Qt::UTC")
99
"Unable to init SSL Context: "

以及 Linux

上的以下控制台输出
true
"01"
("AA-00-00-00")
QDateTime("2035-06-21 21:41:13.000 UTC Qt::UTC")
99
"Unable to init SSL Context: "

如果我删除 "networkRequest.setSslConfiguration(sslConfiguration);",我只会从服务器收到 400 错误,指出我需要发送客户端证书。

添加 "sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);" 没有任何改变。

我很乐意得到任何可能导致 Qt5 代码失败的建议。

Qt 在运行时解析 OpenSSL 函数,这些警告是它的结果。可能是您的程序根本找不到 OpenSSL,或者它找到的版本太旧。从 Qt 5.2 开始,需要 OpenSSL v1.0.0 或更高版本。

来自Qt documentation

Support for Secure Sockets Layer (SSL) communication is provided by the OpenSSL Toolkit, which must be obtained separately.

From Qt version 5.2 onwards, the officially supported version for OpenSSL is 1.0.0 or later. Versions >= 0.9.7 and < 1.0.0 might work, but are not guaranteed to.