util/src/network/ssl/qsslkey.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 = (void *)passPhrase.constData();
       
   123 
       
   124     if (algorithm == QSsl::Rsa) {
       
   125         RSA *result = (type == QSsl::PublicKey)
       
   126             ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase)
       
   127             : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
       
   128         if (rsa && rsa == result)
       
   129             isNull = false;
       
   130     } else {
       
   131         DSA *result = (type == QSsl::PublicKey)
       
   132             ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
       
   133             : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
       
   134         if (dsa && dsa == result)
       
   135             isNull = false;
       
   136     }
       
   137 
       
   138     q_BIO_free(bio);
       
   139 }
       
   140 
       
   141 /*!
       
   142     Constructs a null key.
       
   143 
       
   144     \sa isNull()
       
   145 */
       
   146 QSslKey::QSslKey()
       
   147     : d(new QSslKeyPrivate)
       
   148 {
       
   149 }
       
   150 
       
   151 /*!
       
   152     \internal
       
   153 */
       
   154 QByteArray QSslKeyPrivate::pemHeader() const
       
   155 {
       
   156     // ### use QByteArray::fromRawData() instead
       
   157     if (type == QSsl::PublicKey)
       
   158         return QByteArray("-----BEGIN PUBLIC KEY-----\n");
       
   159     else if (algorithm == QSsl::Rsa)
       
   160         return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n");
       
   161     return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n");
       
   162 }
       
   163 
       
   164 /*!
       
   165     \internal
       
   166 */
       
   167 QByteArray QSslKeyPrivate::pemFooter() const
       
   168 {
       
   169     // ### use QByteArray::fromRawData() instead
       
   170     if (type == QSsl::PublicKey)
       
   171         return QByteArray("-----END PUBLIC KEY-----\n");
       
   172     else if (algorithm == QSsl::Rsa)
       
   173         return QByteArray("-----END RSA PRIVATE KEY-----\n");
       
   174     return QByteArray("-----END DSA PRIVATE KEY-----\n");
       
   175 }
       
   176 
       
   177 /*!
       
   178     \internal
       
   179 
       
   180     Returns a DER key formatted as PEM.
       
   181 */
       
   182 QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
       
   183 {
       
   184     QByteArray pem(der.toBase64());
       
   185 
       
   186     const int lineWidth = 64; // RFC 1421
       
   187     const int newLines = pem.size() / lineWidth;
       
   188     const bool rem = pem.size() % lineWidth;
       
   189 
       
   190     // ### optimize
       
   191     for (int i = 0; i < newLines; ++i)
       
   192         pem.insert((i + 1) * lineWidth + i, '\n');
       
   193     if (rem)
       
   194         pem.append('\n'); // ###
       
   195 
       
   196     pem.prepend(pemHeader());
       
   197     pem.append(pemFooter());
       
   198 
       
   199     return pem;
       
   200 }
       
   201 
       
   202 /*!
       
   203     \internal
       
   204 
       
   205     Returns a PEM key formatted as DER.
       
   206 */
       
   207 QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
       
   208 {
       
   209     const QByteArray header = pemHeader();
       
   210     const QByteArray footer = pemFooter();
       
   211 
       
   212     QByteArray der(pem);
       
   213 
       
   214     const int headerIndex = der.indexOf(header);
       
   215     const int footerIndex = der.indexOf(footer);
       
   216     if (headerIndex == -1 || footerIndex == -1)
       
   217         return QByteArray();
       
   218 
       
   219     der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
       
   220 
       
   221     return QByteArray::fromBase64(der); // ignores newlines
       
   222 }
       
   223 
       
   224 /*!
       
   225     Constructs a QSslKey by decoding the string in the byte array
       
   226     \a encoded using a specified \a algorithm and \a encoding format.
       
   227     If the encoded key is encrypted, \a passPhrase is used to decrypt
       
   228     it. \a type specifies whether the key is public or private.
       
   229 
       
   230     After construction, use isNull() to check if \a encoded contained
       
   231     a valid key.
       
   232 */
       
   233 QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
       
   234                  QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
       
   235     : d(new QSslKeyPrivate)
       
   236 {
       
   237     d->type = type;
       
   238     d->algorithm = algorithm;
       
   239     d->decodePem((encoding == QSsl::Der)
       
   240                  ? d->pemFromDer(encoded) : encoded,
       
   241                  passPhrase);
       
   242 }
       
   243 
       
   244 /*!
       
   245     Constructs a QSslKey by reading and decoding data from a
       
   246     \a device using a specified \a algorithm and \a encoding format.
       
   247     If the encoded key is encrypted, \a passPhrase is used to decrypt
       
   248     it. \a type specifies whether the key is public or private.
       
   249 
       
   250     After construction, use isNull() to check if \a device provided
       
   251     a valid key.
       
   252 */
       
   253 QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
       
   254 		 QSsl::KeyType type, const QByteArray &passPhrase)
       
   255     : d(new QSslKeyPrivate)
       
   256 {
       
   257     QByteArray encoded;
       
   258     if (device)
       
   259         encoded = device->readAll();
       
   260     d->type = type;
       
   261     d->algorithm = algorithm;
       
   262     d->decodePem((encoding == QSsl::Der) ?
       
   263                  d->pemFromDer(encoded) : encoded,
       
   264                  passPhrase);
       
   265 }
       
   266 
       
   267 /*!
       
   268     Constructs an identical copy of \a other.
       
   269 */
       
   270 QSslKey::QSslKey(const QSslKey &other) : d(other.d)
       
   271 {
       
   272 }
       
   273 
       
   274 /*!
       
   275     Destroys the QSslKey object.
       
   276 */
       
   277 QSslKey::~QSslKey()
       
   278 {
       
   279 }
       
   280 
       
   281 /*!
       
   282     Copies the contents of \a other into this key, making the two keys
       
   283     identical.
       
   284 
       
   285     Returns a reference to this QSslKey.
       
   286 */
       
   287 QSslKey &QSslKey::operator=(const QSslKey &other)
       
   288 {
       
   289     d = other.d;
       
   290     return *this;
       
   291 }
       
   292 
       
   293 /*!
       
   294     Returns true if this is a null key; otherwise false.
       
   295 
       
   296     \sa clear()
       
   297 */
       
   298 bool QSslKey::isNull() const
       
   299 {
       
   300     return d->isNull;
       
   301 }
       
   302 
       
   303 /*!
       
   304     Clears the contents of this key, making it a null key.
       
   305 
       
   306     \sa isNull()
       
   307 */
       
   308 void QSslKey::clear()
       
   309 {
       
   310     d = new QSslKeyPrivate;
       
   311 }
       
   312 
       
   313 /*!
       
   314     Returns the length of the key in bits, or -1 if the key is null.
       
   315 */
       
   316 int QSslKey::length() const
       
   317 {
       
   318     if (d->isNull)
       
   319         return -1;
       
   320     return (d->algorithm == QSsl::Rsa)
       
   321            ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
       
   322 }
       
   323 
       
   324 /*!
       
   325     Returns the type of the key (i.e., PublicKey or PrivateKey).
       
   326 */
       
   327 QSsl::KeyType QSslKey::type() const
       
   328 {
       
   329     return d->type;
       
   330 }
       
   331 
       
   332 /*!
       
   333     Returns the key algorithm.
       
   334 */
       
   335 QSsl::KeyAlgorithm QSslKey::algorithm() const
       
   336 {
       
   337     return d->algorithm;
       
   338 }
       
   339 
       
   340 /*!
       
   341   Returns the key in DER encoding. The result is encrypted with
       
   342   \a passPhrase if the key is a private key and \a passPhrase is
       
   343   non-empty.
       
   344 */
       
   345 // ### autotest failure for non-empty passPhrase and private key
       
   346 QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
       
   347 {
       
   348     if (d->isNull)
       
   349         return QByteArray();
       
   350     return d->derFromPem(toPem(passPhrase));
       
   351 }
       
   352 
       
   353 /*!
       
   354   Returns the key in PEM encoding. The result is encrypted with
       
   355   \a passPhrase if the key is a private key and \a passPhrase is
       
   356   non-empty.
       
   357 */
       
   358 QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
       
   359 {
       
   360     if (!QSslSocket::supportsSsl() || d->isNull)
       
   361         return QByteArray();
       
   362 
       
   363     BIO *bio = q_BIO_new(q_BIO_s_mem());
       
   364     if (!bio)
       
   365         return QByteArray();
       
   366 
       
   367     bool fail = false;
       
   368 
       
   369     if (d->algorithm == QSsl::Rsa) {
       
   370         if (d->type == QSsl::PublicKey) {
       
   371             if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa))
       
   372                 fail = true;
       
   373         } else {
       
   374             if (!q_PEM_write_bio_RSAPrivateKey(
       
   375                     bio, d->rsa,
       
   376                     // ### the cipher should be selectable in the API:
       
   377                     passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
       
   378                     (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
       
   379                 fail = true;
       
   380             }
       
   381         }
       
   382     } else {
       
   383         if (d->type == QSsl::PublicKey) {
       
   384             if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa))
       
   385                 fail = true;
       
   386         } else {
       
   387             if (!q_PEM_write_bio_DSAPrivateKey(
       
   388                     bio, d->dsa,
       
   389                     // ### the cipher should be selectable in the API:
       
   390                     passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
       
   391                     (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
       
   392                 fail = true;
       
   393             }
       
   394         }
       
   395     }
       
   396 
       
   397     QByteArray pem;
       
   398     if (!fail) {
       
   399         char *data;
       
   400         long size = q_BIO_get_mem_data(bio, &data);
       
   401         pem = QByteArray(data, size);
       
   402     }
       
   403     q_BIO_free(bio);
       
   404     return pem;
       
   405 }
       
   406 
       
   407 /*!
       
   408     Returns a pointer to the native key handle, if it is available;
       
   409     otherwise a null pointer is returned.
       
   410 
       
   411     You can use this handle together with the native API to access
       
   412     extended information about the key.
       
   413 
       
   414     \warning Use of this function has a high probability of being
       
   415     non-portable, and its return value may vary across platforms, and
       
   416     between minor Qt releases.
       
   417 */
       
   418 Qt::HANDLE QSslKey::handle() const
       
   419 {
       
   420     return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
       
   421 }
       
   422 
       
   423 /*!
       
   424     Returns true if this key is equal to \a other; otherwise returns false.
       
   425 */
       
   426 bool QSslKey::operator==(const QSslKey &other) const
       
   427 {
       
   428     if (isNull())
       
   429         return other.isNull();
       
   430     if (other.isNull())
       
   431         return isNull();
       
   432     if (algorithm() != other.algorithm())
       
   433         return false;
       
   434     if (type() != other.type())
       
   435         return false;
       
   436     if (length() != other.length())
       
   437         return false;
       
   438     return toDer() == other.toDer();
       
   439 }
       
   440 
       
   441 /*! \fn bool QSslKey::operator!=(const QSslKey &other) const
       
   442 
       
   443   Returns true if this key is not equal to key \a other; otherwise
       
   444   returns false.
       
   445 */
       
   446 
       
   447 #ifndef QT_NO_DEBUG_STREAM
       
   448 class QDebug;
       
   449 QDebug operator<<(QDebug debug, const QSslKey &key)
       
   450 {
       
   451     debug << "QSslKey("
       
   452           << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
       
   453           << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
       
   454           << ", " << key.length()
       
   455           << ')';
       
   456     return debug;
       
   457 }
       
   458 #endif
       
   459 
       
   460 QT_END_NAMESPACE