Support for DH and ECDH key exchange for QSslSocket servers
Despite supporting DH and ECDH key exchange as a client, Qt did not provide any default parameters which prevented them being used as a server. A future change should allow the user to control the parameters used, but these defaults should be okay for most users. [ChangeLog][Important Behavior Changes] Support for DH and ECDH key exchange cipher suites when acting as an SSL server has been made possible. This change means the you can now implement servers that offer forward-secrecy using Qt. Task-number: QTBUG-20666 Change-Id: I469163900e4313da9d2d0c3e1e5e47ef46320b17 Reviewed-by: Daniel Molkentin <daniel@molkentin.de> Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
This commit is contained in:
parent
71de8c0df5
commit
814a1c7b2b
@ -55,6 +55,53 @@ QT_BEGIN_NAMESPACE
|
||||
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
|
||||
extern QString getErrorsFromOpenSsl();
|
||||
|
||||
// Default DH params
|
||||
// 1024-bit MODP Group with 160-bit Prime Order Subgroup
|
||||
// From RFC 5114
|
||||
static unsigned const char dh1024_p[]={
|
||||
0xB1,0x0B,0x8F,0x96,0xA0,0x80,0xE0,0x1D,0xDE,0x92,0xDE,0x5E,
|
||||
0xAE,0x5D,0x54,0xEC,0x52,0xC9,0x9F,0xBC,0xFB,0x06,0xA3,0xC6,
|
||||
0x9A,0x6A,0x9D,0xCA,0x52,0xD2,0x3B,0x61,0x60,0x73,0xE2,0x86,
|
||||
0x75,0xA2,0x3D,0x18,0x98,0x38,0xEF,0x1E,0x2E,0xE6,0x52,0xC0,
|
||||
0x13,0xEC,0xB4,0xAE,0xA9,0x06,0x11,0x23,0x24,0x97,0x5C,0x3C,
|
||||
0xD4,0x9B,0x83,0xBF,0xAC,0xCB,0xDD,0x7D,0x90,0xC4,0xBD,0x70,
|
||||
0x98,0x48,0x8E,0x9C,0x21,0x9A,0x73,0x72,0x4E,0xFF,0xD6,0xFA,
|
||||
0xE5,0x64,0x47,0x38,0xFA,0xA3,0x1A,0x4F,0xF5,0x5B,0xCC,0xC0,
|
||||
0xA1,0x51,0xAF,0x5F,0x0D,0xC8,0xB4,0xBD,0x45,0xBF,0x37,0xDF,
|
||||
0x36,0x5C,0x1A,0x65,0xE6,0x8C,0xFD,0xA7,0x6D,0x4D,0xA7,0x08,
|
||||
0xDF,0x1F,0xB2,0xBC,0x2E,0x4A,0x43,0x71
|
||||
};
|
||||
|
||||
static unsigned const char dh1024_g[]={
|
||||
0xA4,0xD1,0xCB,0xD5,0xC3,0xFD,0x34,0x12,0x67,0x65,0xA4,0x42,
|
||||
0xEF,0xB9,0x99,0x05,0xF8,0x10,0x4D,0xD2,0x58,0xAC,0x50,0x7F,
|
||||
0xD6,0x40,0x6C,0xFF,0x14,0x26,0x6D,0x31,0x26,0x6F,0xEA,0x1E,
|
||||
0x5C,0x41,0x56,0x4B,0x77,0x7E,0x69,0x0F,0x55,0x04,0xF2,0x13,
|
||||
0x16,0x02,0x17,0xB4,0xB0,0x1B,0x88,0x6A,0x5E,0x91,0x54,0x7F,
|
||||
0x9E,0x27,0x49,0xF4,0xD7,0xFB,0xD7,0xD3,0xB9,0xA9,0x2E,0xE1,
|
||||
0x90,0x9D,0x0D,0x22,0x63,0xF8,0x0A,0x76,0xA6,0xA2,0x4C,0x08,
|
||||
0x7A,0x09,0x1F,0x53,0x1D,0xBF,0x0A,0x01,0x69,0xB6,0xA2,0x8A,
|
||||
0xD6,0x62,0xA4,0xD1,0x8E,0x73,0xAF,0xA3,0x2D,0x77,0x9D,0x59,
|
||||
0x18,0xD0,0x8B,0xC8,0x85,0x8F,0x4D,0xCE,0xF9,0x7C,0x2A,0x24,
|
||||
0x85,0x5E,0x6E,0xEB,0x22,0xB3,0xB2,0xE5
|
||||
};
|
||||
|
||||
static DH *get_dh1024()
|
||||
{
|
||||
DH *dh = q_DH_new();
|
||||
if (!dh)
|
||||
return 0;
|
||||
|
||||
dh->p = q_BN_bin2bn(dh1024_p, sizeof(dh1024_p), 0);
|
||||
dh->g = q_BN_bin2bn(dh1024_g, sizeof(dh1024_g), 0);
|
||||
if (!dh->p || !dh->g) {
|
||||
q_DH_free(dh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dh;
|
||||
}
|
||||
|
||||
QSslContext::QSslContext()
|
||||
: ctx(0),
|
||||
pkey(0),
|
||||
@ -261,6 +308,18 @@ init_context:
|
||||
if (!configuration.sessionTicket().isEmpty())
|
||||
sslContext->setSessionASN1(configuration.sessionTicket());
|
||||
|
||||
// Set temp DH params
|
||||
DH *dh = 0;
|
||||
dh = get_dh1024();
|
||||
q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
|
||||
q_DH_free(dh);
|
||||
|
||||
// Set temp ECDH params
|
||||
EC_KEY *ecdh = 0;
|
||||
ecdh = q_EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
q_SSL_CTX_set_tmp_ecdh(sslContext->ctx, ecdh);
|
||||
q_EC_KEY_free(ecdh);
|
||||
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
|
@ -361,6 +361,11 @@ DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
|
||||
DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
|
||||
const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
|
||||
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
|
||||
DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return)
|
||||
DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return 0, return)
|
||||
DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
|
||||
|
||||
#define RESOLVEFUNC(func) \
|
||||
if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
|
||||
@ -835,6 +840,11 @@ bool q_resolveOpenSslSymbols()
|
||||
RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
|
||||
RESOLVEFUNC(SSL_get0_next_proto_negotiated)
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||
RESOLVEFUNC(DH_new)
|
||||
RESOLVEFUNC(DH_free)
|
||||
RESOLVEFUNC(BN_bin2bn)
|
||||
RESOLVEFUNC(EC_KEY_new_by_curve_name)
|
||||
RESOLVEFUNC(EC_KEY_free)
|
||||
|
||||
symbolsResolved = true;
|
||||
delete libs.first;
|
||||
|
@ -427,6 +427,17 @@ int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
|
||||
X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
|
||||
STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
|
||||
|
||||
// Diffie-Hellman support
|
||||
DH *q_DH_new();
|
||||
void q_DH_free(DH *dh);
|
||||
BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
#define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh)
|
||||
|
||||
// EC Diffie-Hellman support
|
||||
EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
|
||||
void q_EC_KEY_free(EC_KEY *ecdh);
|
||||
#define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh)
|
||||
|
||||
#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
|
||||
#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
|
||||
#ifdef SSLEAY_MACROS
|
||||
|
@ -192,6 +192,8 @@ private slots:
|
||||
void resume();
|
||||
void qtbug18498_peek();
|
||||
void qtbug18498_peek2();
|
||||
void dhServer();
|
||||
void ecdhServer();
|
||||
void setEmptyDefaultConfiguration(); // this test should be last
|
||||
|
||||
static void exitLoop()
|
||||
@ -1004,6 +1006,7 @@ public:
|
||||
QString m_keyFile;
|
||||
QString m_certFile;
|
||||
QString m_interFile;
|
||||
QString ciphers;
|
||||
|
||||
protected:
|
||||
void incomingConnection(qintptr socketDescriptor)
|
||||
@ -1037,6 +1040,10 @@ protected:
|
||||
socket->setLocalCertificateChain(localCert + interCert);
|
||||
}
|
||||
|
||||
if (!ciphers.isEmpty()) {
|
||||
socket->setCiphers(ciphers);
|
||||
}
|
||||
|
||||
QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
|
||||
QVERIFY(!socket->peerAddress().isNull());
|
||||
QVERIFY(socket->peerPort() != 0);
|
||||
@ -2665,6 +2672,66 @@ void tst_QSslSocket::qtbug18498_peek2()
|
||||
QVERIFY(client->waitForDisconnected(5000));
|
||||
}
|
||||
|
||||
void tst_QSslSocket::dhServer()
|
||||
{
|
||||
if (!QSslSocket::supportsSsl()) {
|
||||
qWarning("SSL not supported, skipping test");
|
||||
return;
|
||||
}
|
||||
|
||||
QFETCH_GLOBAL(bool, setProxy);
|
||||
if (setProxy)
|
||||
return;
|
||||
|
||||
SslServer server;
|
||||
server.ciphers = QLatin1String("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA");
|
||||
QVERIFY(server.listen());
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||
|
||||
QSslSocketPtr client(new QSslSocket);
|
||||
socket = client.data();
|
||||
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
|
||||
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
|
||||
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
|
||||
|
||||
client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
|
||||
|
||||
loop.exec();
|
||||
QVERIFY(client->state() == QAbstractSocket::ConnectedState);
|
||||
}
|
||||
|
||||
void tst_QSslSocket::ecdhServer()
|
||||
{
|
||||
if (!QSslSocket::supportsSsl()) {
|
||||
qWarning("SSL not supported, skipping test");
|
||||
return;
|
||||
}
|
||||
|
||||
QFETCH_GLOBAL(bool, setProxy);
|
||||
if (setProxy)
|
||||
return;
|
||||
|
||||
SslServer server;
|
||||
server.ciphers = QLatin1String("ECDHE-RSA-AES128-SHA");
|
||||
QVERIFY(server.listen());
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||
|
||||
QSslSocketPtr client(new QSslSocket);
|
||||
socket = client.data();
|
||||
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
|
||||
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
|
||||
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
|
||||
|
||||
client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
|
||||
|
||||
loop.exec();
|
||||
QVERIFY(client->state() == QAbstractSocket::ConnectedState);
|
||||
}
|
||||
|
||||
void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects
|
||||
{
|
||||
// used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265
|
||||
|
Loading…
Reference in New Issue
Block a user