src/network/ssl/qsslkey.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 
       
    43 /*!
       
    44     \class QSslKey
       
    45     \brief The QSslKey class provides an interface for private and public keys.
       
    46     \since 4.3
       
    47 
       
    48     \reentrant
       
    49     \ingroup network
       
    50     \ingroup ssl
       
    51     \inmodule QtNetwork
       
    52 
       
    53     QSslKey provides a simple API for managing keys.
       
    54 
       
    55     \sa QSslSocket, QSslCertificate, QSslCipher
       
    56 */
       
    57 
       
    58 #include "qsslsocket_openssl_symbols_p.h"
       
    59 #include "qsslkey.h"
       
    60 #include "qsslkey_p.h"
       
    61 #include "qsslsocket.h"
       
    62 #include "qsslsocket_p.h"
       
    63 
       
    64 #include <QtCore/qatomic.h>
       
    65 #include <QtCore/qbytearray.h>
       
    66 #include <QtCore/qiodevice.h>
       
    67 #ifndef QT_NO_DEBUG_STREAM
       
    68 #include <QtCore/qdebug.h>
       
    69 
       
    70 QT_BEGIN_NAMESPACE
       
    71 #endif
       
    72 
       
    73 
       
    74 /*!
       
    75     \internal
       
    76  */
       
    77 void QSslKeyPrivate::clear(bool deep)
       
    78 {
       
    79     isNull = true;
       
    80     if (!QSslSocket::supportsSsl())
       
    81         return;
       
    82     if (rsa) {
       
    83         if (deep)
       
    84             q_RSA_free(rsa);
       
    85         rsa = 0;
       
    86     }
       
    87     if (dsa) {
       
    88         if (deep)
       
    89             q_DSA_free(dsa);
       
    90         dsa = 0;
       
    91     }
       
    92 }
       
    93 
       
    94 /*!
       
    95     \internal
       
    96 
       
    97     Allocates a new rsa or dsa struct and decodes \a pem into it
       
    98     according to the current algorithm and type.
       
    99 
       
   100     If \a deepClear is true, the rsa/dsa struct is freed if it is was
       
   101     already allocated, otherwise we "leak" memory (which is exactly
       
   102     what we want for copy construction).
       
   103 
       
   104     If \a passPhrase is non-empty, it will be used for decrypting
       
   105     \a pem.
       
   106 */
       
   107 void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
       
   108                                bool deepClear)
       
   109 {
       
   110     if (pem.isEmpty())
       
   111         return;
       
   112 
       
   113     clear(deepClear);
       
   114 
       
   115     if (!QSslSocket::supportsSsl())
       
   116         return;
       
   117 
       
   118     BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
       
   119     if (!bio)
       
   120         return;
       
   121 
       
   122     void *phrase = passPhrase.isEmpty()
       
   123         ? (void *)0
       
   124         : (void *)passPhrase.constData();
       
   125 
       
   126     if (algorithm == QSsl::Rsa) {
       
   127         RSA *result = (type == QSsl::PublicKey)
       
   128             ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase)
       
   129             : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
       
   130         if (rsa && rsa == result)
       
   131             isNull = false;
       
   132     } else {
       
   133         DSA *result = (type == QSsl::PublicKey)
       
   134             ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
       
   135             : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
       
   136         if (dsa && dsa == result)
       
   137             isNull = false;
       
   138     }
       
   139 
       
   140     q_BIO_free(bio);
       
   141 }
       
   142 
       
   143 /*!
       
   144     Constructs a null key.
       
   145 
       
   146     \sa isNull()
       
   147 */
       
   148 QSslKey::QSslKey()
       
   149     : d(new QSslKeyPrivate)
       
   150 {
       
   151 }
       
   152 
       
   153 /*!
       
   154     \internal
       
   155 */
       
   156 QByteArray QSslKeyPrivate::pemHeader() const
       
   157 {
       
   158     // ### use QByteArray::fromRawData() instead
       
   159     if (type == QSsl::PublicKey)
       
   160         return QByteArray("-----BEGIN PUBLIC KEY-----\n");
       
   161     else if (algorithm == QSsl::Rsa)
       
   162         return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n");
       
   163     return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n");
       
   164 }
       
   165 
       
   166 /*!
       
   167     \internal
       
   168 */
       
   169 QByteArray QSslKeyPrivate::pemFooter() const
       
   170 {
       
   171     // ### use QByteArray::fromRawData() instead
       
   172     if (type == QSsl::PublicKey)
       
   173         return QByteArray("-----END PUBLIC KEY-----\n");
       
   174     else if (algorithm == QSsl::Rsa)
       
   175         return QByteArray("-----END RSA PRIVATE KEY-----\n");
       
   176     return QByteArray("-----END DSA PRIVATE KEY-----\n");
       
   177 }
       
   178 
       
   179 /*!
       
   180     \internal
       
   181 
       
   182     Returns a DER key formatted as PEM.
       
   183 */
       
   184 QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
       
   185 {
       
   186     QByteArray pem(der.toBase64());
       
   187 
       
   188     const int lineWidth = 64; // RFC 1421
       
   189     const int newLines = pem.size() / lineWidth;
       
   190     const bool rem = pem.size() % lineWidth;
       
   191 
       
   192     // ### optimize
       
   193     for (int i = 0; i < newLines; ++i)
       
   194         pem.insert((i + 1) * lineWidth + i, '\n');
       
   195     if (rem)
       
   196         pem.append('\n'); // ###
       
   197 
       
   198     pem.prepend(pemHeader());
       
   199     pem.append(pemFooter());
       
   200 
       
   201     return pem;
       
   202 }
       
   203 
       
   204 /*!
       
   205     \internal
       
   206 
       
   207     Returns a PEM key formatted as DER.
       
   208 */
       
   209 QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
       
   210 {
       
   211     const QByteArray header = pemHeader();
       
   212     const QByteArray footer = pemFooter();
       
   213 
       
   214     QByteArray der(pem);
       
   215 
       
   216     const int headerIndex = der.indexOf(header);
       
   217     const int footerIndex = der.indexOf(footer);
       
   218     if (headerIndex == -1 || footerIndex == -1)
       
   219         return QByteArray();
       
   220 
       
   221     der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
       
   222 
       
   223     return QByteArray::fromBase64(der); // ignores newlines
       
   224 }
       
   225 
       
   226 /*!
       
   227     Constructs a QSslKey by decoding the string in the byte array
       
   228     \a encoded using a specified \a algorithm and \a encoding format.
       
   229     If the encoded key is encrypted, \a passPhrase is used to decrypt
       
   230     it. \a type specifies whether the key is public or private.
       
   231 
       
   232     After construction, use isNull() to check if \a encoded contained
       
   233     a valid key.
       
   234 */
       
   235 QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
       
   236                  QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
       
   237     : d(new QSslKeyPrivate)
       
   238 {
       
   239     d->type = type;
       
   240     d->algorithm = algorithm;
       
   241     d->decodePem((encoding == QSsl::Der)
       
   242                  ? d->pemFromDer(encoded) : encoded,
       
   243                  passPhrase);
       
   244 }
       
   245 
       
   246 /*!
       
   247     Constructs a QSslKey by reading and decoding data from a
       
   248     \a device using a specified \a algorithm and \a encoding format.
       
   249     If the encoded key is encrypted, \a passPhrase is used to decrypt
       
   250     it. \a type specifies whether the key is public or private.
       
   251 
       
   252     After construction, use isNull() to check if \a device provided
       
   253     a valid key.
       
   254 */
       
   255 QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
       
   256 		 QSsl::KeyType type, const QByteArray &passPhrase)
       
   257     : d(new QSslKeyPrivate)
       
   258 {
       
   259     QByteArray encoded;
       
   260     if (device)
       
   261         encoded = device->readAll();
       
   262     d->type = type;
       
   263     d->algorithm = algorithm;
       
   264     d->decodePem((encoding == QSsl::Der) ?
       
   265                  d->pemFromDer(encoded) : encoded,
       
   266                  passPhrase);
       
   267 }
       
   268 
       
   269 /*!
       
   270     Constructs an identical copy of \a other.
       
   271 */
       
   272 QSslKey::QSslKey(const QSslKey &other) : d(other.d)
       
   273 {
       
   274 }
       
   275 
       
   276 /*!
       
   277     Destroys the QSslKey object.
       
   278 */
       
   279 QSslKey::~QSslKey()
       
   280 {
       
   281 }
       
   282 
       
   283 /*!
       
   284     Copies the contents of \a other into this key, making the two keys
       
   285     identical.
       
   286 
       
   287     Returns a reference to this QSslKey.
       
   288 */
       
   289 QSslKey &QSslKey::operator=(const QSslKey &other)
       
   290 {
       
   291     d = other.d;
       
   292     return *this;
       
   293 }
       
   294 
       
   295 /*!
       
   296     Returns true if this is a null key; otherwise false.
       
   297 
       
   298     \sa clear()
       
   299 */
       
   300 bool QSslKey::isNull() const
       
   301 {
       
   302     return d->isNull;
       
   303 }
       
   304 
       
   305 /*!
       
   306     Clears the contents of this key, making it a null key.
       
   307 
       
   308     \sa isNull()
       
   309 */
       
   310 void QSslKey::clear()
       
   311 {
       
   312     d = new QSslKeyPrivate;
       
   313 }
       
   314 
       
   315 /*!
       
   316     Returns the length of the key in bits, or -1 if the key is null.
       
   317 */
       
   318 int QSslKey::length() const
       
   319 {
       
   320     if (d->isNull)
       
   321         return -1;
       
   322     return (d->algorithm == QSsl::Rsa)
       
   323            ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
       
   324 }
       
   325 
       
   326 /*!
       
   327     Returns the type of the key (i.e., PublicKey or PrivateKey).
       
   328 */
       
   329 QSsl::KeyType QSslKey::type() const
       
   330 {
       
   331     return d->type;
       
   332 }
       
   333 
       
   334 /*!
       
   335     Returns the key algorithm.
       
   336 */
       
   337 QSsl::KeyAlgorithm QSslKey::algorithm() const
       
   338 {
       
   339     return d->algorithm;
       
   340 }
       
   341 
       
   342 /*!
       
   343   Returns the key in DER encoding. The result is encrypted with
       
   344   \a passPhrase if the key is a private key and \a passPhrase is
       
   345   non-empty.
       
   346 */
       
   347 // ### autotest failure for non-empty passPhrase and private key
       
   348 QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
       
   349 {
       
   350     if (d->isNull)
       
   351         return QByteArray();
       
   352     return d->derFromPem(toPem(passPhrase));
       
   353 }
       
   354 
       
   355 /*!
       
   356   Returns the key in PEM encoding. The result is encrypted with
       
   357   \a passPhrase if the key is a private key and \a passPhrase is
       
   358   non-empty.
       
   359 */
       
   360 QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
       
   361 {
       
   362     if (!QSslSocket::supportsSsl() || d->isNull)
       
   363         return QByteArray();
       
   364 
       
   365     BIO *bio = q_BIO_new(q_BIO_s_mem());
       
   366     if (!bio)
       
   367         return QByteArray();
       
   368 
       
   369     bool fail = false;
       
   370 
       
   371     if (d->algorithm == QSsl::Rsa) {
       
   372         if (d->type == QSsl::PublicKey) {
       
   373             if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa))
       
   374                 fail = true;
       
   375         } else {
       
   376             if (!q_PEM_write_bio_RSAPrivateKey(
       
   377                     bio, d->rsa,
       
   378                     // ### the cipher should be selectable in the API:
       
   379                     passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
       
   380                     (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
       
   381                 fail = true;
       
   382             }
       
   383         }
       
   384     } else {
       
   385         if (d->type == QSsl::PublicKey) {
       
   386             if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa))
       
   387                 fail = true;
       
   388         } else {
       
   389             if (!q_PEM_write_bio_DSAPrivateKey(
       
   390                     bio, d->dsa,
       
   391                     // ### the cipher should be selectable in the API:
       
   392                     passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
       
   393                     (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
       
   394                 fail = true;
       
   395             }
       
   396         }
       
   397     }
       
   398 
       
   399     QByteArray pem;
       
   400     if (!fail) {
       
   401         char *data;
       
   402         long size = q_BIO_get_mem_data(bio, &data);
       
   403         pem = QByteArray(data, size);
       
   404     }
       
   405     q_BIO_free(bio);
       
   406     return pem;
       
   407 }
       
   408 
       
   409 /*!
       
   410     Returns a pointer to the native key handle, if it is available;
       
   411     otherwise a null pointer is returned.
       
   412 
       
   413     You can use this handle together with the native API to access
       
   414     extended information about the key.
       
   415 
       
   416     \warning Use of this function has a high probability of being
       
   417     non-portable, and its return value may vary across platforms, and
       
   418     between minor Qt releases.
       
   419 */
       
   420 Qt::HANDLE QSslKey::handle() const
       
   421 {
       
   422     return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
       
   423 }
       
   424 
       
   425 /*!
       
   426     Returns true if this key is equal to \a other; otherwise returns false.
       
   427 */
       
   428 bool QSslKey::operator==(const QSslKey &other) const
       
   429 {
       
   430     if (isNull())
       
   431         return other.isNull();
       
   432     if (other.isNull())
       
   433         return isNull();
       
   434     if (algorithm() != other.algorithm())
       
   435         return false;
       
   436     if (type() != other.type())
       
   437         return false;
       
   438     if (length() != other.length())
       
   439         return false;
       
   440     return toDer() == other.toDer();
       
   441 }
       
   442 
       
   443 /*! \fn bool QSslKey::operator!=(const QSslKey &other) const
       
   444 
       
   445   Returns true if this key is not equal to key \a other; otherwise
       
   446   returns false.
       
   447 */
       
   448 
       
   449 #ifndef QT_NO_DEBUG_STREAM
       
   450 class QDebug;
       
   451 QDebug operator<<(QDebug debug, const QSslKey &key)
       
   452 {
       
   453     debug << "QSslKey("
       
   454           << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
       
   455           << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
       
   456           << ", " << key.length()
       
   457           << ')';
       
   458     return debug;
       
   459 }
       
   460 #endif
       
   461 
       
   462 QT_END_NAMESPACE