qssl: add support for QSsl::Opaque key

This allow to use directly EVP_PKEY * with QSslKey (for
example comming from a PKCS#11 dongle).

Change-Id: Icb1ba5081506a831ec3d8cfffe13ce70939608ea
Merge-request: 48
Reviewed-by: Peter Hartmann <peter.hartmann@nokia.com>
Reviewed-on: http://codereview.qt.nokia.com/4010
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
This commit is contained in:
Corentin Chary 2011-08-31 19:35:35 +02:00 committed by Qt by Nokia
parent a3e8f1ab0c
commit a4878db8df
5 changed files with 61 additions and 15 deletions

View File

@ -63,6 +63,7 @@ namespace QSsl {
};
enum KeyAlgorithm {
Opaque,
Rsa,
Dsa
};

View File

@ -89,6 +89,11 @@ void QSslKeyPrivate::clear(bool deep)
q_DSA_free(dsa);
dsa = 0;
}
if (opaque) {
if (deep)
q_EVP_PKEY_free(opaque);
opaque = 0;
}
}
/*!
@ -264,6 +269,23 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
passPhrase);
}
/*!
Constructs a QSslKey from a valid native key \a handle.
\a type specifies whether the key is public or private.
QSslKey will take ownership for this key and you must not
free the key using the native library. The algorithm used
when creating a key from a handle will always be QSsl::Opaque.
*/
QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
: d(new QSslKeyPrivate)
{
d->opaque = reinterpret_cast<EVP_PKEY *>(handle);
d->algorithm = QSsl::Opaque;
d->type = type;
d->isNull = !d->opaque;
}
/*!
Constructs an identical copy of \a other.
*/
@ -315,8 +337,9 @@ void QSslKey::clear()
*/
int QSslKey::length() const
{
if (d->isNull)
if (d->isNull || d->algorithm == QSsl::Opaque)
return -1;
return (d->algorithm == QSsl::Rsa)
? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
}
@ -345,8 +368,9 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const
// ### autotest failure for non-empty passPhrase and private key
QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
{
if (d->isNull)
if (d->isNull || d->algorithm == QSsl::Opaque)
return QByteArray();
return d->derFromPem(toPem(passPhrase));
}
@ -357,7 +381,7 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
*/
QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
{
if (!QSslSocket::supportsSsl() || d->isNull)
if (!QSslSocket::supportsSsl() || d->isNull || d->algorithm == QSsl::Opaque)
return QByteArray();
BIO *bio = q_BIO_new(q_BIO_s_mem());
@ -417,7 +441,16 @@ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
*/
Qt::HANDLE QSslKey::handle() const
{
return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
switch (d->algorithm) {
case QSsl::Opaque:
return Qt::HANDLE(d->opaque);
case QSsl::Rsa:
return Qt::HANDLE(d->rsa);
case QSsl::Dsa:
return Qt::HANDLE(d->dsa);
default:
return Qt::HANDLE(NULL);
}
}
/*!
@ -435,6 +468,8 @@ bool QSslKey::operator==(const QSslKey &other) const
return false;
if (length() != other.length())
return false;
if (algorithm() == QSsl::Opaque)
return handle() == other.handle();
return toDer() == other.toDer();
}
@ -450,7 +485,8 @@ QDebug operator<<(QDebug debug, const QSslKey &key)
{
debug << "QSslKey("
<< (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
<< ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
<< ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
(key.algorithm() == QSsl::Rsa ? "RSA" : "DSA"))
<< ", " << key.length()
<< ')';
return debug;

View File

@ -73,6 +73,7 @@ public:
QSsl::EncodingFormat format = QSsl::Pem,
QSsl::KeyType type = QSsl::PrivateKey,
const QByteArray &passPhrase = QByteArray());
explicit QSslKey(Qt::HANDLE handle, QSsl::KeyType type = QSsl::PrivateKey);
QSslKey(const QSslKey &other);
~QSslKey();
QSslKey &operator=(const QSslKey &other);

View File

@ -67,6 +67,7 @@ public:
inline QSslKeyPrivate()
: rsa(0)
, dsa(0)
, opaque(0)
{
clear();
}
@ -88,6 +89,7 @@ public:
QSsl::KeyAlgorithm algorithm;
RSA *rsa;
DSA *dsa;
EVP_PKEY *opaque;
QAtomicInt ref;

View File

@ -364,20 +364,27 @@ init_context:
return false;
}
// Load private key
pkey = q_EVP_PKEY_new();
// before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
// this lead to a memory leak. Now we use the *_set1_* functions which do not
// take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
if (configuration.privateKey.algorithm() == QSsl::Rsa)
q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle());
else
q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle());
if (configuration.privateKey.algorithm() == QSsl::Opaque) {
pkey = reinterpret_cast<EVP_PKEY *>(configuration.privateKey.handle());
} else {
// Load private key
pkey = q_EVP_PKEY_new();
// before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
// this lead to a memory leak. Now we use the *_set1_* functions which do not
// take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
if (configuration.privateKey.algorithm() == QSsl::Rsa)
q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle());
else
q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle());
}
if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) {
q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl()));
emit q->error(QAbstractSocket::UnknownSocketError);
return false;
}
if (configuration.privateKey.algorithm() == QSsl::Opaque)
pkey = 0; // Don't free the private key, it belongs to QSslKey
// Check if the certificate matches the private key.
if (!q_SSL_CTX_check_private_key(ctx)) {
@ -1383,7 +1390,6 @@ void QSslSocketBackendPrivate::disconnected()
q_EVP_PKEY_free(pkey);
pkey = 0;
}
}
QSslCipher QSslSocketBackendPrivate::sessionCipher() const