diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 4803e47224..1f9055b50c 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -730,6 +730,23 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const return d->sslSessionTicketLifeTimeHint; } +/*! + \since 5.7 + + Returns the ephemeral server key used for cipher algorithms + with forward secrecy, e.g. DHE-RSA-AES128-SHA. + + The ephemeral key is only available when running in client mode, i.e. + QSslSocket::SslClientMode. When running in server mode or using a + cipher algorithm without forward secrecy a null key is returned. + The ephemeral server key will be set before emitting the encrypted() + signal. + */ +QSslKey QSslConfiguration::ephemeralServerKey() const +{ + return d->ephemeralServerKey; +} + /*! \since 5.5 diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 2cbc31b032..29ae95746e 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -128,6 +128,8 @@ public: void setSessionTicket(const QByteArray &sessionTicket); int sessionTicketLifeTimeHint() const; + QSslKey ephemeralServerKey() const; + // EC settings QVector ellipticCurves() const; void setEllipticCurves(const QVector &curves); diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 3fd1252a7b..e636699854 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -113,6 +113,8 @@ public: QByteArray sslSession; int sslSessionTicketLifeTimeHint; + QSslKey ephemeralServerKey; + QList nextAllowedProtocols; QByteArray nextNegotiatedProtocol; QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus; diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index bbd712317c..a5ca844e88 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1566,6 +1566,14 @@ void QSslSocketBackendPrivate::continueHandshake() } #endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (q_SSLeay() >= 0x10002000L && mode == QSslSocket::SslClientMode) { + EVP_PKEY *key; + if (q_SSL_get_server_tmp_key(ssl, &key)) + configuration.ephemeralServerKey = QSslKey(key, QSsl::PublicKey); + } +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ... + connectionEncrypted = true; emit q->encrypted(); if (autoStartHandshake && pendingClose) { diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 7f87f11b7c..08d84a440b 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -483,6 +483,9 @@ size_t q_EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems); int q_EC_curve_nist2nid(const char *name); #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +#define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key) +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L // PKCS#12 support int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca); diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 3e127e8111..4573de1aab 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -232,6 +232,8 @@ private slots: #ifndef QT_NO_OPENSSL void simplePskConnect_data(); void simplePskConnect(); + void ephemeralServerKey_data(); + void ephemeralServerKey(); #endif static void exitLoop() @@ -3344,6 +3346,40 @@ void tst_QSslSocket::simplePskConnect() QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); QCOMPARE(disconnectedSpy.count(), 1); } + +void tst_QSslSocket::ephemeralServerKey_data() +{ + QTest::addColumn("cipher"); + QTest::addColumn("emptyKey"); + + QTest::newRow("NonForwardSecrecyCipher") << "RC4-SHA" << true; + QTest::newRow("ForwardSecrecyCipher") << "ECDHE-RSA-AES256-SHA" << (QSslSocket::sslLibraryVersionNumber() < 0x10002000L); +} + +void tst_QSslSocket::ephemeralServerKey() +{ + QFETCH_GLOBAL(bool, setProxy); + if (!QSslSocket::supportsSsl() || setProxy) + return; + + QFETCH(QString, cipher); + QFETCH(bool, emptyKey); + SslServer server; + server.config.setCiphers(QList() << QSslCipher(cipher)); + QVERIFY(server.listen()); + QSslSocketPtr client = newSocket(); + socket = client.data(); + connect(socket, SIGNAL(sslErrors(QList)), this, SLOT(ignoreErrorSlot())); + QSignalSpy spy(client.data(), &QSslSocket::encrypted); + + client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); + spy.wait(); + + QCOMPARE(spy.count(), 1); + QVERIFY(server.config.ephemeralServerKey().isNull()); + QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey); +} + #endif // QT_NO_OPENSSL #endif // QT_NO_SSL