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 { enum KeyAlgorithm {
Opaque,
Rsa, Rsa,
Dsa Dsa
}; };

View File

@ -89,6 +89,11 @@ void QSslKeyPrivate::clear(bool deep)
q_DSA_free(dsa); q_DSA_free(dsa);
dsa = 0; 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); 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. Constructs an identical copy of \a other.
*/ */
@ -315,8 +337,9 @@ void QSslKey::clear()
*/ */
int QSslKey::length() const int QSslKey::length() const
{ {
if (d->isNull) if (d->isNull || d->algorithm == QSsl::Opaque)
return -1; return -1;
return (d->algorithm == QSsl::Rsa) return (d->algorithm == QSsl::Rsa)
? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p); ? 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 // ### autotest failure for non-empty passPhrase and private key
QByteArray QSslKey::toDer(const QByteArray &passPhrase) const QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
{ {
if (d->isNull) if (d->isNull || d->algorithm == QSsl::Opaque)
return QByteArray(); return QByteArray();
return d->derFromPem(toPem(passPhrase)); return d->derFromPem(toPem(passPhrase));
} }
@ -357,7 +381,7 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
*/ */
QByteArray QSslKey::toPem(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(); return QByteArray();
BIO *bio = q_BIO_new(q_BIO_s_mem()); 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 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; return false;
if (length() != other.length()) if (length() != other.length())
return false; return false;
if (algorithm() == QSsl::Opaque)
return handle() == other.handle();
return toDer() == other.toDer(); return toDer() == other.toDer();
} }
@ -450,7 +485,8 @@ QDebug operator<<(QDebug debug, const QSslKey &key)
{ {
debug << "QSslKey(" debug << "QSslKey("
<< (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey") << (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() << ", " << key.length()
<< ')'; << ')';
return debug; return debug;

View File

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

View File

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

View File

@ -364,6 +364,9 @@ init_context:
return false; return false;
} }
if (configuration.privateKey.algorithm() == QSsl::Opaque) {
pkey = reinterpret_cast<EVP_PKEY *>(configuration.privateKey.handle());
} else {
// Load private key // Load private key
pkey = q_EVP_PKEY_new(); pkey = q_EVP_PKEY_new();
// before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
@ -373,11 +376,15 @@ init_context:
q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle()); q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle());
else else
q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle()); q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle());
}
if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) { if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) {
q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl())); q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl()));
emit q->error(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError);
return false; 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. // Check if the certificate matches the private key.
if (!q_SSL_CTX_check_private_key(ctx)) { if (!q_SSL_CTX_check_private_key(ctx)) {
@ -1383,7 +1390,6 @@ void QSslSocketBackendPrivate::disconnected()
q_EVP_PKEY_free(pkey); q_EVP_PKEY_free(pkey);
pkey = 0; pkey = 0;
} }
} }
QSslCipher QSslSocketBackendPrivate::sessionCipher() const QSslCipher QSslSocketBackendPrivate::sessionCipher() const