diff options
Diffstat (limited to 'src/network/ssl/qsslkey_p.cpp')
-rw-r--r-- | src/network/ssl/qsslkey_p.cpp | 348 |
1 files changed, 79 insertions, 269 deletions
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index ce12f49989..55cb2b0436 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -54,14 +18,12 @@ \sa QSslSocket, QSslCertificate, QSslCipher */ +#include "qssl_p.h" #include "qsslkey.h" #include "qsslkey_p.h" -#ifndef QT_NO_OPENSSL -#include "qsslsocket_openssl_symbols_p.h" -#endif #include "qsslsocket.h" #include "qsslsocket_p.h" -#include "qasn1element_p.h" +#include "qtlsbackend_p.h" #include <QtCore/qatomic.h> #include <QtCore/qbytearray.h> @@ -73,11 +35,6 @@ QT_BEGIN_NAMESPACE /*! - \fn void QSslKeyPrivate::clear(bool deep) - \internal - */ - -/*! \fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) \internal @@ -94,210 +51,57 @@ QT_BEGIN_NAMESPACE */ /*! - Constructs a null key. - - \sa isNull() -*/ -QSslKey::QSslKey() - : d(new QSslKeyPrivate) -{ -} - -/*! \internal */ -QByteArray QSslKeyPrivate::pemHeader() const +QSslKeyPrivate::QSslKeyPrivate() { - if (type == QSsl::PublicKey) - return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----"); - else if (algorithm == QSsl::Rsa) - return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Dsa) - return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Ec) - return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----"); - else if (algorithm == QSsl::Dh) - return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); - - Q_UNREACHABLE(); - return QByteArray(); -} - -static QByteArray pkcs8Header(bool encrypted) -{ - return encrypted - ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----") - : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); + const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse(); + if (!tlsBackend) + return; + backend.reset(tlsBackend->createKey()); + if (backend.get()) + backend->clear(false /*not deep clear*/); + else + qCWarning(lcSsl, "Active TLS backend does not support key creation"); } /*! \internal */ -QByteArray QSslKeyPrivate::pemFooter() const +QSslKeyPrivate::~QSslKeyPrivate() { - if (type == QSsl::PublicKey) - return QByteArrayLiteral("-----END PUBLIC KEY-----"); - else if (algorithm == QSsl::Rsa) - return QByteArrayLiteral("-----END RSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Dsa) - return QByteArrayLiteral("-----END DSA PRIVATE KEY-----"); - else if (algorithm == QSsl::Ec) - return QByteArrayLiteral("-----END EC PRIVATE KEY-----"); - else if (algorithm == QSsl::Dh) - return QByteArrayLiteral("-----END PRIVATE KEY-----"); - - Q_UNREACHABLE(); - return QByteArray(); + if (backend.get()) + backend->clear(true /*deep clear*/); } -static QByteArray pkcs8Footer(bool encrypted) +QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) { - return encrypted - ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----") - : QByteArrayLiteral("-----END PRIVATE KEY-----"); -} + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) { + const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey()); + return cryptor->decrypt(cipher, data, key, iv); + } -/*! - \internal + return {}; +} - Returns a DER key formatted as PEM. -*/ -QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const +QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) { - QByteArray pem(der.toBase64()); - - const int lineWidth = 64; // RFC 1421 - const int newLines = pem.size() / lineWidth; - const bool rem = pem.size() % lineWidth; - - // ### optimize - for (int i = 0; i < newLines; ++i) - pem.insert((i + 1) * lineWidth + i, '\n'); - if (rem) - pem.append('\n'); // ### - - QByteArray extra; - if (!headers.isEmpty()) { - QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd(); - do { - --it; - extra += it.key() + ": " + it.value() + '\n'; - } while (it != headers.constBegin()); - extra += '\n'; + if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) { + const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey()); + return cryptor->encrypt(cipher, data, key, iv); } - if (isEncryptedPkcs8(der)) { - pem.prepend(pkcs8Header(true) + '\n' + extra); - pem.append(pkcs8Footer(true) + '\n'); -#if !QT_CONFIG(openssl) - } else if (isPkcs8) { - pem.prepend(pkcs8Header(false) + '\n' + extra); - pem.append(pkcs8Footer(false) + '\n'); -#endif - } else { - pem.prepend(pemHeader() + '\n' + extra); - pem.append(pemFooter() + '\n'); - } - - return pem; + return {}; } /*! - \internal + Constructs a null key. - Returns a PEM key formatted as DER. + \sa isNull() */ -QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const -{ - QByteArray header = pemHeader(); - QByteArray footer = pemFooter(); - - QByteArray der(pem); - - int headerIndex = der.indexOf(header); - int footerIndex = der.indexOf(footer, headerIndex + header.length()); - if (type != QSsl::PublicKey) { - if (headerIndex == -1 || footerIndex == -1) { - header = pkcs8Header(true); - footer = pkcs8Footer(true); - headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); - } - if (headerIndex == -1 || footerIndex == -1) { - header = pkcs8Header(false); - footer = pkcs8Footer(false); - headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); - } - } - if (headerIndex == -1 || footerIndex == -1) - return QByteArray(); - - der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size())); - - if (der.contains("Proc-Type:")) { - // taken from QHttpNetworkReplyPrivate::parseHeader - int i = 0; - while (i < der.count()) { - int j = der.indexOf(':', i); // field-name - if (j == -1) - break; - const QByteArray field = der.mid(i, j - i).trimmed(); - j++; - // any number of LWS is allowed before and after the value - QByteArray value; - do { - i = der.indexOf('\n', j); - if (i == -1) - break; - if (!value.isEmpty()) - value += ' '; - // check if we have CRLF or only LF - bool hasCR = (i && der[i-1] == '\r'); - int length = i -(hasCR ? 1: 0) - j; - value += der.mid(j, length).trimmed(); - j = ++i; - } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t')); - if (i == -1) - break; // something is wrong - - headers->insert(field, value); - } - der = der.mid(i); - } - - return QByteArray::fromBase64(der); // ignores newlines -} - -bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const +QSslKey::QSslKey() + : d(new QSslKeyPrivate) { - static const QList<QByteArray> pbes1OIds { - // PKCS5 - { PKCS5_MD2_DES_CBC_OID }, { PKCS5_MD2_RC2_CBC_OID }, { PKCS5_MD5_DES_CBC_OID }, - { PKCS5_MD5_RC2_CBC_OID }, { PKCS5_SHA1_DES_CBC_OID }, { PKCS5_SHA1_RC2_CBC_OID }, - }; - QAsn1Element elem; - if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType) - return false; - - const auto items = elem.toList(); - if (items.size() != 2 - || items[0].type() != QAsn1Element::SequenceType - || items[1].type() != QAsn1Element::OctetStringType) { - return false; - } - - const auto encryptionSchemeContainer = items[0].toList(); - if (encryptionSchemeContainer.size() != 2 - || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType - || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) { - return false; - } - - const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId(); - return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID - || pbes1OIds.contains(encryptionScheme) - || encryptionScheme.startsWith(PKCS12_OID); } /*! @@ -314,12 +118,12 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase) : d(new QSslKeyPrivate) { - d->type = type; - d->algorithm = algorithm; - if (encoding == QSsl::Der) - d->decodeDer(encoded, passPhrase); - else - d->decodePem(encoded, passPhrase); + if (auto *tlsKey = d->backend.get()) { + if (encoding == QSsl::Der) + tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/); + else + tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/); + } } /*! @@ -339,12 +143,13 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding QByteArray encoded; if (device) encoded = device->readAll(); - d->type = type; - d->algorithm = algorithm; - if (encoding == QSsl::Der) - d->decodeDer(encoded, passPhrase); - else - d->decodePem(encoded, passPhrase); + + if (auto *tlsKey = d->backend.get()) { + if (encoding == QSsl::Der) + tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/); + else + tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/); + } } /*! @@ -358,20 +163,8 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type) : d(new QSslKeyPrivate) { -#ifndef QT_NO_OPENSSL - EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle); - if (!evpKey || !d->fromEVP_PKEY(evpKey)) { - d->opaque = evpKey; - d->algorithm = QSsl::Opaque; - } else { - q_EVP_PKEY_free(evpKey); - } -#else - d->opaque = handle; - d->algorithm = QSsl::Opaque; -#endif - d->type = type; - d->isNull = !d->opaque; + if (auto *tlsKey = d->backend.get()) + tlsKey->fromHandle(handle, type); } /*! @@ -433,7 +226,10 @@ QSslKey &QSslKey::operator=(const QSslKey &other) */ bool QSslKey::isNull() const { - return d->isNull; + if (const auto *tlsKey = d->backend.get()) + return tlsKey->isNull(); + + return true; } /*! @@ -451,7 +247,10 @@ void QSslKey::clear() */ int QSslKey::length() const { - return d->length(); + if (const auto *tlsKey = d->backend.get()) + return tlsKey->length(); + + return -1; } /*! @@ -459,7 +258,10 @@ int QSslKey::length() const */ QSsl::KeyType QSslKey::type() const { - return d->type; + if (const auto *tlsKey = d->backend.get()) + return tlsKey->type(); + + return QSsl::PublicKey; } /*! @@ -467,7 +269,10 @@ QSsl::KeyType QSslKey::type() const */ QSsl::KeyAlgorithm QSslKey::algorithm() const { - return d->algorithm; + if (const auto *tlsKey = d->backend.get()) + return tlsKey->algorithm(); + + return QSsl::Opaque; } /*! @@ -478,19 +283,18 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const */ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const { - if (d->isNull || d->algorithm == QSsl::Opaque) - return QByteArray(); + if (isNull() || algorithm() == QSsl::Opaque) + return {}; // Encrypted DER is nonsense, see QTBUG-41038. - if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty()) - return QByteArray(); + if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) + return {}; -#ifndef QT_NO_OPENSSL QMap<QByteArray, QByteArray> headers; - return d->derFromPem(toPem(passPhrase), &headers); -#else - return d->derData; -#endif + if (const auto *tlsKey = d->backend.get()) + return tlsKey->derFromPem(toPem(passPhrase), &headers); + + return {}; } /*! @@ -500,7 +304,10 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const */ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const { - return d->toPem(passPhrase); + if (const auto *tlsKey = d->backend.get()) + return tlsKey->toPem(passPhrase); + + return {}; } /*! @@ -516,7 +323,10 @@ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const */ Qt::HANDLE QSslKey::handle() const { - return d->handle(); + if (d->backend.get()) + return d->backend->handle(); + + return nullptr; } /*! |