Make ephemeral server key available

When using cipher algorithms with forward secrecy an ephemeral key is
used to generate the symmetric session key. Beside the SSL certificate's
key, this ephemeral key is of cryptographic interest.

The ephemeral key is chosen by the server side - currently statically in
the Qt implementation - so it is only of interest on the client side to
check it. Therefore the ephemeral key is the null key if the connection
is set up in server mode or a cipher without forward secrecy is used.

Change-Id: If241247dbb8490a91233ae47f2b38952c6591bf4
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
bb10
Sebastian Lösch 2015-08-11 10:27:16 +02:00 committed by André Klitzing
parent a9e1fc29ca
commit 73ad2bf32f
6 changed files with 68 additions and 0 deletions

View File

@ -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

View File

@ -128,6 +128,8 @@ public:
void setSessionTicket(const QByteArray &sessionTicket);
int sessionTicketLifeTimeHint() const;
QSslKey ephemeralServerKey() const;
// EC settings
QVector<QSslEllipticCurve> ellipticCurves() const;
void setEllipticCurves(const QVector<QSslEllipticCurve> &curves);

View File

@ -113,6 +113,8 @@ public:
QByteArray sslSession;
int sslSessionTicketLifeTimeHint;
QSslKey ephemeralServerKey;
QList<QByteArray> nextAllowedProtocols;
QByteArray nextNegotiatedProtocol;
QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus;

View File

@ -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) {

View File

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

View File

@ -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<QString>("cipher");
QTest::addColumn<bool>("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>() << QSslCipher(cipher));
QVERIFY(server.listen());
QSslSocketPtr client = newSocket();
socket = client.data();
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), 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