QSsl::TlsKey - provide the interface and implementations
which will become parts of TLS plugins in the future. Task-number: QTBUG-65922 Change-Id: I4ee3c59c435fc34a9f4dacd3ff0e3cfb44251e23 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
59252a3a96
commit
1a0da3ae69
@ -327,6 +327,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
|
||||
ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
|
||||
ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h
|
||||
ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
|
||||
ssl/qtlskey_base.cpp ssl/qtlskey_base_p.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
|
||||
@ -338,6 +339,8 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s
|
||||
ssl/qsslkey_schannel.cpp
|
||||
ssl/qsslsocket_qt.cpp
|
||||
ssl/qsslsocket_schannel.cpp ssl/qsslsocket_schannel_p.h
|
||||
ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
|
||||
ssl/qtlskey_schannel.cpp ssl/qtlskey_schannel_p.h
|
||||
LIBRARIES
|
||||
Crypt32
|
||||
Secur32
|
||||
@ -354,6 +357,8 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FE
|
||||
ssl/qsslsocket_mac.cpp ssl/qsslsocket_mac_p.h
|
||||
ssl/qsslsocket_mac_shared.cpp
|
||||
ssl/qsslsocket_qt.cpp
|
||||
ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
|
||||
ssl/qtlskey_st.cpp ssl/qtlskey_st_p.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl
|
||||
@ -370,6 +375,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ss
|
||||
ssl/qsslkey_openssl.cpp
|
||||
ssl/qsslsocket_openssl.cpp ssl/qsslsocket_openssl_p.h
|
||||
ssl/qsslsocket_openssl_symbols.cpp ssl/qsslsocket_openssl_symbols_p.h
|
||||
ssl/qtlskey_openssl.cpp ssl/qtlskey_openssl_p.h
|
||||
DEFINES
|
||||
OPENSSL_API_COMPAT=0x10100000L
|
||||
)
|
||||
|
@ -52,6 +52,10 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
|
||||
namespace QSsl {
|
||||
class TlsKey;
|
||||
}
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class QSslKeyPrivate;
|
||||
@ -92,9 +96,12 @@ public:
|
||||
inline bool operator!=(const QSslKey &key) const { return !operator==(key); }
|
||||
|
||||
private:
|
||||
QSsl::TlsKey *backendImplementation() const;
|
||||
|
||||
QExplicitlySharedDataPointer<QSslKeyPrivate> d;
|
||||
friend class QSslCertificate;
|
||||
friend class QSslSocketBackendPrivate;
|
||||
friend class QTlsBackend;
|
||||
};
|
||||
|
||||
Q_DECLARE_SHARED(QSslKey)
|
||||
|
@ -54,6 +54,7 @@
|
||||
\sa QSslSocket, QSslCertificate, QSslCipher
|
||||
*/
|
||||
|
||||
#include "qssl_p.h"
|
||||
#include "qsslkey.h"
|
||||
#include "qsslkey_p.h"
|
||||
#ifndef QT_NO_OPENSSL
|
||||
@ -62,6 +63,7 @@
|
||||
#include "qsslsocket.h"
|
||||
#include "qsslsocket_p.h"
|
||||
#include "qasn1element_p.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
|
||||
#include <QtCore/qatomic.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
@ -93,6 +95,34 @@ QT_BEGIN_NAMESPACE
|
||||
\a pem.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QSslKeyPrivate::QSslKeyPrivate()
|
||||
: algorithm(QSsl::Opaque)
|
||||
, opaque(nullptr)
|
||||
{
|
||||
clear(false); // TLSTODO: remove
|
||||
const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
|
||||
if (!tlsBackend)
|
||||
return;
|
||||
keyBackend.reset(tlsBackend->createKey());
|
||||
if (keyBackend.get())
|
||||
keyBackend->clear(false /*not deep clear*/);
|
||||
else
|
||||
qCWarning(lcSsl, "Active TLS backend does not support key creation");
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QSslKeyPrivate::~QSslKeyPrivate()
|
||||
{
|
||||
clear(); // TLSTODO: remove
|
||||
if (keyBackend.get())
|
||||
keyBackend->clear(true /*deep clear*/);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a null key.
|
||||
|
||||
@ -539,6 +569,14 @@ bool QSslKey::operator==(const QSslKey &other) const
|
||||
return toDer() == other.toDer();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.1
|
||||
Returns TLS backend-specific implementation this QSslKey is using.
|
||||
*/
|
||||
QSsl::TlsKey *QSslKey::backendImplementation() const
|
||||
{
|
||||
return d->keyBackend.get();
|
||||
}
|
||||
/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
|
||||
|
||||
Returns \c true if this key is not equal to key \a other; otherwise
|
||||
|
@ -61,20 +61,19 @@
|
||||
#include <openssl/dsa.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
class TlsKey;
|
||||
}
|
||||
|
||||
class QSslKeyPrivate
|
||||
{
|
||||
public:
|
||||
inline QSslKeyPrivate()
|
||||
: algorithm(QSsl::Opaque)
|
||||
, opaque(nullptr)
|
||||
{
|
||||
clear(false);
|
||||
}
|
||||
|
||||
inline ~QSslKeyPrivate()
|
||||
{ clear(); }
|
||||
QSslKeyPrivate();
|
||||
~QSslKeyPrivate();
|
||||
|
||||
void clear(bool deep = true);
|
||||
|
||||
@ -130,6 +129,7 @@ public:
|
||||
int keyLength;
|
||||
#endif
|
||||
|
||||
std::unique_ptr<QSsl::TlsKey> keyBackend;
|
||||
QAtomicInt ref;
|
||||
|
||||
private:
|
||||
|
@ -1603,7 +1603,7 @@ bool QSslSocket::setActiveBackend(const QString &backendName)
|
||||
}
|
||||
|
||||
QMutexLocker locker(&QSslSocketPrivate::backendMutex);
|
||||
if (QSslSocketPrivate::tlsBackend.get()) {
|
||||
if (QSslSocketPrivate::tlsBackend) {
|
||||
qCWarning(lcSsl) << "Cannot set backend named" << backendName
|
||||
<< "as active, another backend is already in use";
|
||||
locker.unlock();
|
||||
@ -2832,6 +2832,21 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QTlsBackend *QSslSocketPrivate::tlsBackendInUse()
|
||||
{
|
||||
const QMutexLocker locker(&backendMutex);
|
||||
if (tlsBackend)
|
||||
return tlsBackend;
|
||||
|
||||
if (!activeBackendName.size())
|
||||
activeBackendName = QTlsBackend::defaultBackendName();
|
||||
|
||||
return tlsBackend = QTlsBackend::findBackend(activeBackendName);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qsslsocket.cpp"
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "qsslcertificate_p.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
#include "qsslcipher_p.h"
|
||||
#include "qtlskey_st_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
|
||||
#include <QtCore/qmessageauthenticationcode.h>
|
||||
@ -86,6 +87,10 @@ private:
|
||||
{
|
||||
return builtinBackendNames[nameIndexSecureTransport];
|
||||
}
|
||||
QSsl::TlsKey *createKey() const override
|
||||
{
|
||||
return new QSsl::TlsKeySecureTransport;
|
||||
}
|
||||
|
||||
QList<QSsl::SslProtocol> supportedProtocols() const override
|
||||
{
|
||||
|
@ -67,6 +67,8 @@
|
||||
#include "qsslpresharedkeyauthenticator_p.h"
|
||||
#include "qocspresponse_p.h"
|
||||
#include "qsslkey.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
#include "qtlskey_openssl_p.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "qwindowscarootfetcher_p.h"
|
||||
@ -110,6 +112,10 @@ private:
|
||||
{
|
||||
return builtinBackendNames[nameIndexOpenSSL];
|
||||
}
|
||||
QSsl::TlsKey *createKey() const override
|
||||
{
|
||||
return new QSsl::TlsKeyOpenSSL;
|
||||
}
|
||||
QList<QSsl::SslProtocol> supportedProtocols() const override
|
||||
{
|
||||
QList<QSsl::SslProtocol> protocols;
|
||||
|
@ -59,7 +59,6 @@
|
||||
#include "qsslkey.h"
|
||||
#include "qsslconfiguration_p.h"
|
||||
#include "qocspresponse.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include <private/qsslcontext_openssl_p.h>
|
||||
#else
|
||||
@ -108,6 +107,7 @@ using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>;
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
class QTlsBackend;
|
||||
class QSslSocketPrivate : public QTcpSocketPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QSslSocket)
|
||||
@ -209,7 +209,7 @@ public:
|
||||
|
||||
Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported();
|
||||
|
||||
static bool loadBackend(const QString &backendName);
|
||||
static QTlsBackend *tlsBackendInUse();
|
||||
static void registerAdHocFactory();
|
||||
|
||||
private:
|
||||
@ -221,6 +221,7 @@ private:
|
||||
|
||||
static bool s_libraryLoaded;
|
||||
static bool s_loadedCiphersAndCerts;
|
||||
|
||||
protected:
|
||||
bool verifyErrorsHaveBeenIgnored();
|
||||
bool paused;
|
||||
@ -233,7 +234,7 @@ protected:
|
||||
|
||||
static inline QMutex backendMutex;
|
||||
static inline QString activeBackendName;
|
||||
static inline std::unique_ptr<QTlsBackend> tlsBackend;
|
||||
static inline QTlsBackend *tlsBackend = nullptr;
|
||||
};
|
||||
|
||||
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "qsslcertificate_p.h"
|
||||
#include "qsslcipher_p.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
#include "qtlskey_schannel_p.h"
|
||||
|
||||
#include <QtCore/qscopeguard.h>
|
||||
#include <QtCore/qoperatingsystemversion.h>
|
||||
@ -167,6 +168,11 @@ private:
|
||||
return builtinBackendNames[nameIndexSchannel];
|
||||
}
|
||||
|
||||
QSsl::TlsKey *createKey() const override
|
||||
{
|
||||
return new QSsl::TlsKeySchannel;
|
||||
}
|
||||
|
||||
QList<QSsl::SslProtocol> supportedProtocols() const override
|
||||
{
|
||||
QList<QSsl::SslProtocol> protocols;
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#include <QtCore/private/qfactoryloader_p.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -137,6 +138,47 @@ private:
|
||||
|
||||
Q_GLOBAL_STATIC(BackendCollection, backends);
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
TlsKey::~TlsKey() = default;
|
||||
|
||||
QByteArray TlsKey::pemHeader() const
|
||||
{
|
||||
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 TlsKey::pemFooter() const
|
||||
{
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
const QString QTlsBackend::builtinBackendNames[] = {
|
||||
QStringLiteral("schannel"),
|
||||
QStringLiteral("securetransport"),
|
||||
@ -282,7 +324,6 @@ QList<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &bac
|
||||
return fct->implementedClasses();
|
||||
|
||||
return {};
|
||||
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -53,11 +53,14 @@
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <private/qsslkey_p.h>
|
||||
|
||||
#include <QtNetwork/qsslcertificate.h>
|
||||
#include <QtNetwork/qsslerror.h>
|
||||
#include <QtNetwork/qsslkey.h>
|
||||
#include <QtNetwork/qssl.h>
|
||||
|
||||
#include <QtCore/qnamespace.h>
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qstring.h>
|
||||
@ -76,9 +79,49 @@ class QIODevice;
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
// Encapsulates key's data or backend-specific
|
||||
// The class TlsKey encapsulates key's data (DER) or backend-specific
|
||||
// data-structure, like RSA/DSA/DH structs in OpenSSL.
|
||||
class TlsKey;
|
||||
// TLSTODO: Interface is mostly what QSslKeyPrivate is now. Names,
|
||||
// however strange they are, for now preserved to ease the transition
|
||||
// (this may change in future - for example, 'decodeDer' is not just
|
||||
// decoding DER, it's initializing a key from DER.
|
||||
class TlsKey {
|
||||
public:
|
||||
virtual ~TlsKey();
|
||||
|
||||
virtual void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
|
||||
const QByteArray &passPhrase, bool deepClear) = 0;
|
||||
virtual void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
|
||||
const QByteArray &passPhrase, bool deepClear) = 0;
|
||||
|
||||
virtual QByteArray toPem(const QByteArray &passPhrase) const = 0;
|
||||
virtual QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const = 0;
|
||||
virtual QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const = 0;
|
||||
|
||||
virtual void fromHandle(Qt::HANDLE opaque, KeyType type) = 0;
|
||||
virtual Qt::HANDLE handle() const = 0;
|
||||
|
||||
virtual bool isNull() const = 0;
|
||||
virtual KeyType type() const = 0;
|
||||
virtual KeyAlgorithm algorithm() const = 0;
|
||||
virtual int length() const = 0;
|
||||
|
||||
virtual void clear(bool deepClear) = 0;
|
||||
|
||||
// Needed by QSslKeyPrivate::pemFromDer() for non-OpenSSL backends.
|
||||
virtual bool isPkcs8() const = 0;
|
||||
|
||||
using Cipher = QSslKeyPrivate::Cipher;
|
||||
virtual QByteArray decrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const = 0;
|
||||
virtual QByteArray encrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const = 0;
|
||||
|
||||
// Those two are non-virtual, always the same and only depend on the key type
|
||||
// and algorithm:
|
||||
QByteArray pemHeader() const;
|
||||
QByteArray pemFooter() const;
|
||||
};
|
||||
|
||||
// Abstraction above OpenSSL's X509, or our generic
|
||||
// 'derData'-based code.
|
||||
@ -149,6 +192,12 @@ public:
|
||||
|
||||
static const QString builtinBackendNames[];
|
||||
|
||||
template<class DynamicType, class TLSObject>
|
||||
static DynamicType *backend(const TLSObject &o)
|
||||
{
|
||||
return static_cast<DynamicType *>(o.backendImplementation());
|
||||
}
|
||||
|
||||
Q_DISABLE_COPY_MOVE(QTlsBackend)
|
||||
};
|
||||
|
||||
|
133
src/network/ssl/qtlskey_base.cpp
Normal file
133
src/network/ssl/qtlskey_base.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtlskey_base_p.h"
|
||||
#include "qasn1element_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
QByteArray TlsKeyBase::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
|
||||
{
|
||||
QByteArray pem(der.toBase64());
|
||||
|
||||
const int lineWidth = 64; // RFC 1421
|
||||
const int newLines = pem.size() / lineWidth;
|
||||
const bool rem = pem.size() % lineWidth;
|
||||
|
||||
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 (isEncryptedPkcs8(der)) {
|
||||
pem.prepend(pkcs8Header(true) + '\n' + extra);
|
||||
pem.append(pkcs8Footer(true) + '\n');
|
||||
} else if (isPkcs8()) {
|
||||
pem.prepend(pkcs8Header(false) + '\n' + extra);
|
||||
pem.append(pkcs8Footer(false) + '\n');
|
||||
} else {
|
||||
pem.prepend(pemHeader() + '\n' + extra);
|
||||
pem.append(pemFooter() + '\n');
|
||||
}
|
||||
|
||||
return pem;
|
||||
}
|
||||
|
||||
QByteArray TlsKeyBase::pkcs8Header(bool encrypted)
|
||||
{
|
||||
return encrypted
|
||||
? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
|
||||
: QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
|
||||
}
|
||||
|
||||
QByteArray TlsKeyBase::pkcs8Footer(bool encrypted)
|
||||
{
|
||||
return encrypted
|
||||
? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
|
||||
: QByteArrayLiteral("-----END PRIVATE KEY-----");
|
||||
}
|
||||
|
||||
bool TlsKeyBase::isEncryptedPkcs8(const QByteArray &der)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
104
src/network/ssl/qtlskey_base_p.h
Normal file
104
src/network/ssl/qtlskey_base_p.h
Normal file
@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTLSKEY_BASE_P_H
|
||||
#define QTLSKEY_BASE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <private/qtlsbackend_p.h>
|
||||
|
||||
#include <qssl.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
// TLSTODO: Note, 'base' is supposed to move to plugins together with
|
||||
// 'generic' and 'backendXXX'.
|
||||
class TlsKeyBase : public TlsKey
|
||||
{
|
||||
public:
|
||||
bool isNull() const override
|
||||
{
|
||||
return keyIsNull;
|
||||
}
|
||||
QSsl::KeyType type() const override
|
||||
{
|
||||
return keyType;
|
||||
}
|
||||
QSsl::KeyAlgorithm algorithm() const override
|
||||
{
|
||||
return keyAlgorithm;
|
||||
}
|
||||
bool isPkcs8 () const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const override;
|
||||
|
||||
protected:
|
||||
static QByteArray pkcs8Header(bool encrypted);
|
||||
static QByteArray pkcs8Footer(bool encrypted);
|
||||
static bool isEncryptedPkcs8(const QByteArray &der);
|
||||
|
||||
bool keyIsNull = true;
|
||||
QSsl::KeyType keyType = QSsl::PublicKey;
|
||||
QSsl::KeyAlgorithm keyAlgorithm = QSsl::Opaque;
|
||||
};
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTLSKEY_BASE_P_H
|
827
src/network/ssl/qtlskey_generic.cpp
Normal file
827
src/network/ssl/qtlskey_generic.cpp
Normal file
@ -0,0 +1,827 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtlskey_generic_p.h"
|
||||
#include "qasn1element_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
|
||||
#include <QtNetwork/qpassworddigestor.h>
|
||||
|
||||
#include <QtCore/QMessageAuthenticationCode>
|
||||
#include <QtCore/qcryptographichash.h>
|
||||
#include <QtCore/qrandom.h>
|
||||
|
||||
#include <QtCore/qdatastream.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qmap.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// The code here is essentially what we had in qsslkey_qt.cpp before, with
|
||||
// minimal changes/restructure.
|
||||
|
||||
namespace QSsl {
|
||||
// OIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
|
||||
// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
|
||||
namespace {
|
||||
|
||||
const quint8 bits_table[256] = {
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,
|
||||
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
|
||||
using OidLengthMap = QMap<QByteArray, int>;
|
||||
|
||||
OidLengthMap createOidMap()
|
||||
{
|
||||
OidLengthMap oids;
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.1"), 192); // secp192r1 a.k.a prime192v1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.7"), 256); // secp256r1 a.k.a prime256v1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.1"), 193); // sect193r2
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.10"), 256); // secp256k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.16"), 283); // sect283k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.17"), 283); // sect283r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.26"), 233); // sect233k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.27"), 233); // sect233r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.3"), 239); // sect239k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.30"), 160); // secp160r2
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.31"), 192); // secp192k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.32"), 224); // secp224k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.33"), 224); // secp224r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.34"), 384); // secp384r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.35"), 521); // secp521r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.36"), 409); // sect409k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.37"), 409); // sect409r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.38"), 571); // sect571k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.39"), 571); // sect571r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.8"), 160); // secp160r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.9"), 160); // secp160k1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.11"), 384); // brainpoolP384r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.13"), 512); // brainpoolP512r1
|
||||
oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.7"), 256); // brainpoolP256r1
|
||||
return oids;
|
||||
}
|
||||
|
||||
} // Unnamed namespace.
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap()))
|
||||
|
||||
namespace {
|
||||
|
||||
// Maps OIDs to the encryption cipher they specify
|
||||
const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap {
|
||||
{DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc},
|
||||
{DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc},
|
||||
// {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2
|
||||
{PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
|
||||
{PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
|
||||
// {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2
|
||||
{PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
|
||||
{PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
|
||||
{RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc}
|
||||
// {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5
|
||||
// {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES
|
||||
// {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192},
|
||||
// {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256}
|
||||
};
|
||||
|
||||
struct EncryptionData
|
||||
{
|
||||
EncryptionData() = default;
|
||||
|
||||
EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
|
||||
: initialized(true), cipher(cipher), key(key), iv(iv)
|
||||
{
|
||||
}
|
||||
bool initialized = false;
|
||||
QSslKeyPrivate::Cipher cipher;
|
||||
QByteArray key;
|
||||
QByteArray iv;
|
||||
};
|
||||
|
||||
EncryptionData readPbes2(const QList<QAsn1Element> &element, const QByteArray &passPhrase)
|
||||
{
|
||||
// RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2
|
||||
/*** Scheme: ***
|
||||
* Sequence (scheme-specific info..)
|
||||
* Sequence (key derivation info)
|
||||
* Object Identifier (Key derivation algorithm (e.g. PBKDF2))
|
||||
* Sequence (salt)
|
||||
* CHOICE (this entry can be either of the types it contains)
|
||||
* Octet string (actual salt)
|
||||
* Object identifier (Anything using this is deferred to a later version of PKCS #5)
|
||||
* Integer (iteration count)
|
||||
* Sequence (encryption algorithm info)
|
||||
* Object identifier (identifier for the algorithm)
|
||||
* Algorithm dependent, is covered in the switch further down
|
||||
*/
|
||||
|
||||
static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap {
|
||||
// PBES2/PBKDF2
|
||||
{HMAC_WITH_SHA1, QCryptographicHash::Sha1},
|
||||
{HMAC_WITH_SHA224, QCryptographicHash::Sha224},
|
||||
{HMAC_WITH_SHA256, QCryptographicHash::Sha256},
|
||||
{HMAC_WITH_SHA512, QCryptographicHash::Sha512},
|
||||
{HMAC_WITH_SHA512_224, QCryptographicHash::Sha512},
|
||||
{HMAC_WITH_SHA512_256, QCryptographicHash::Sha512},
|
||||
{HMAC_WITH_SHA384, QCryptographicHash::Sha384}
|
||||
};
|
||||
|
||||
// Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2
|
||||
static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap {
|
||||
{QSslKeyPrivate::Cipher::DesCbc, 8},
|
||||
{QSslKeyPrivate::Cipher::DesEde3Cbc, 24},
|
||||
// @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3)
|
||||
{QSslKeyPrivate::Cipher::Rc2Cbc, 4}
|
||||
// @todo: AES(, rc5?)
|
||||
};
|
||||
|
||||
const QList<QAsn1Element> keyDerivationContainer = element[0].toList();
|
||||
if (keyDerivationContainer.size() != 2
|
||||
|| keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType
|
||||
|| keyDerivationContainer[1].type() != QAsn1Element::SequenceType) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId();
|
||||
const auto keyDerivationParams = keyDerivationContainer[1].toList();
|
||||
|
||||
const auto encryptionAlgorithmContainer = element[1].toList();
|
||||
if (encryptionAlgorithmContainer.size() != 2
|
||||
|| encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId());
|
||||
if (iterator == oidCipherMap.cend()) {
|
||||
qWarning()
|
||||
<< "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId()
|
||||
<< "\nFile a bug report to Qt (include the line above).";
|
||||
return {};
|
||||
}
|
||||
|
||||
QSslKeyPrivate::Cipher cipher = *iterator;
|
||||
QByteArray key;
|
||||
QByteArray iv;
|
||||
switch (cipher) {
|
||||
case QSslKeyPrivate::Cipher::DesCbc:
|
||||
case QSslKeyPrivate::Cipher::DesEde3Cbc:
|
||||
// https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD)
|
||||
// https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD)
|
||||
// @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD)
|
||||
/*** Scheme: ***
|
||||
* Octet string (IV)
|
||||
*/
|
||||
if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType)
|
||||
return {};
|
||||
|
||||
// @note: All AES identifiers should be able to use this branch!!
|
||||
iv = encryptionAlgorithmContainer[1].value();
|
||||
|
||||
if (iv.size() != 8) // @note: AES needs 16 bytes
|
||||
return {};
|
||||
break;
|
||||
case QSslKeyPrivate::Cipher::Rc2Cbc: {
|
||||
// https://tools.ietf.org/html/rfc8018#appendix-B.2.3
|
||||
/*** Scheme: ***
|
||||
* Sequence (rc2 parameters)
|
||||
* Integer (rc2 parameter version)
|
||||
* Octet string (IV)
|
||||
*/
|
||||
if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType)
|
||||
return {};
|
||||
const auto rc2ParametersContainer = encryptionAlgorithmContainer[1].toList();
|
||||
if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2)
|
||||
|| rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) {
|
||||
return {};
|
||||
}
|
||||
iv = rc2ParametersContainer.back().value();
|
||||
if (iv.size() != 8)
|
||||
return {};
|
||||
break;
|
||||
} // @todo(?): case (RC5 , AES)
|
||||
case QSslKeyPrivate::Cipher::Aes128Cbc:
|
||||
case QSslKeyPrivate::Cipher::Aes192Cbc:
|
||||
case QSslKeyPrivate::Cipher::Aes256Cbc:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {
|
||||
// Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2
|
||||
QByteArray salt;
|
||||
if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) {
|
||||
salt = keyDerivationParams[0].value();
|
||||
} else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) {
|
||||
Q_UNIMPLEMENTED();
|
||||
/* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2
|
||||
which ends with: "such facilities are deferred to a future version of PKCS #5"
|
||||
*/
|
||||
return {};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Iterations needed to derive the key
|
||||
int iterationCount = keyDerivationParams[1].toInteger();
|
||||
// Optional integer
|
||||
int keyLength = -1;
|
||||
int vectorPos = 2;
|
||||
if (keyDerivationParams.size() > vectorPos
|
||||
&& keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) {
|
||||
keyLength = keyDerivationParams[vectorPos].toInteger(nullptr);
|
||||
++vectorPos;
|
||||
} else {
|
||||
keyLength = cipherKeyLengthMap[cipher];
|
||||
}
|
||||
|
||||
// Optional algorithm identifier (default: HMAC-SHA-1)
|
||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
|
||||
if (keyDerivationParams.size() > vectorPos
|
||||
&& keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) {
|
||||
const auto hashAlgorithmContainer = keyDerivationParams[vectorPos].toList();
|
||||
hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()];
|
||||
Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType);
|
||||
++vectorPos;
|
||||
}
|
||||
Q_ASSERT(keyDerivationParams.size() == vectorPos);
|
||||
|
||||
key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength);
|
||||
} else {
|
||||
qWarning()
|
||||
<< "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm
|
||||
<< "\nFile a bugreport to Qt (include the line above).";
|
||||
return {};
|
||||
}
|
||||
return {cipher, key, iv};
|
||||
}
|
||||
|
||||
// Maps OIDs to the hash function it specifies
|
||||
const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap {
|
||||
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
||||
// PKCS5
|
||||
//{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2
|
||||
//{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2},
|
||||
{PKCS5_MD5_DES_CBC_OID, QCryptographicHash::Md5},
|
||||
{PKCS5_MD5_RC2_CBC_OID, QCryptographicHash::Md5},
|
||||
#endif
|
||||
{PKCS5_SHA1_DES_CBC_OID, QCryptographicHash::Sha1},
|
||||
{PKCS5_SHA1_RC2_CBC_OID, QCryptographicHash::Sha1},
|
||||
// PKCS12 (unimplemented)
|
||||
// {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4
|
||||
// {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1},
|
||||
// @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac...
|
||||
// further note that more work may be required for the 3DES variations listed to be available.
|
||||
// {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
|
||||
// {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
|
||||
// {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1},
|
||||
// {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1}
|
||||
};
|
||||
|
||||
EncryptionData readPbes1(const QList<QAsn1Element> &element, const QByteArray &encryptionScheme,
|
||||
const QByteArray &passPhrase)
|
||||
{
|
||||
// RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1
|
||||
// Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2
|
||||
/*** Scheme: ***
|
||||
* Sequence (PBE Parameter)
|
||||
* Octet string (salt)
|
||||
* Integer (iteration counter)
|
||||
*/
|
||||
// Step 1
|
||||
if (element.size() != 2
|
||||
|| element[0].type() != QAsn1Element::ElementType::OctetStringType
|
||||
|| element[1].type() != QAsn1Element::ElementType::IntegerType) {
|
||||
return {};
|
||||
}
|
||||
QByteArray salt = element[0].value();
|
||||
if (salt.size() != 8)
|
||||
return {};
|
||||
|
||||
int iterationCount = element[1].toInteger();
|
||||
if (iterationCount < 0)
|
||||
return {};
|
||||
|
||||
// Step 2
|
||||
auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme);
|
||||
if (iterator == pbes1OidHashFunctionMap.cend()) {
|
||||
// Qt was compiled with ONLY_SHA1 (or it's MD2)
|
||||
return {};
|
||||
}
|
||||
QCryptographicHash::Algorithm hashAlgorithm = *iterator;
|
||||
QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16);
|
||||
if (key.size() != 16)
|
||||
return {};
|
||||
|
||||
// Step 3
|
||||
QByteArray iv = key.right(8); // last 8 bytes are used as IV
|
||||
key.truncate(8); // first 8 bytes are used for the key
|
||||
|
||||
QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme];
|
||||
// Steps 4-6 are done after returning
|
||||
return {cipher, key, iv};
|
||||
}
|
||||
|
||||
int curveBits(const QByteArray &oid)
|
||||
{
|
||||
const int length = oidLengthMap->value(oid);
|
||||
return length ? length : -1;
|
||||
}
|
||||
|
||||
int numberOfBits(const QByteArray &modulus)
|
||||
{
|
||||
int bits = modulus.size() * 8;
|
||||
for (int i = 0; i < modulus.size(); ++i) {
|
||||
quint8 b = modulus[i];
|
||||
bits -= 8;
|
||||
if (b != 0) {
|
||||
bits += bits_table[b];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase,
|
||||
const QByteArray &iv)
|
||||
{
|
||||
// This is somewhat simplified and shortened version of what OpenSSL does.
|
||||
// See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere
|
||||
// in their code for what they pass as arguments to EVP_BytesToKey when
|
||||
// deriving encryption keys (when reading/writing pems files with encrypted
|
||||
// keys).
|
||||
|
||||
Q_ASSERT(iv.size() >= 8);
|
||||
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
|
||||
QByteArray data(passPhrase);
|
||||
data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL.
|
||||
|
||||
hash.addData(data);
|
||||
|
||||
if (cipher == QSslKeyPrivate::Aes128Cbc)
|
||||
return hash.result();
|
||||
|
||||
QByteArray key(hash.result());
|
||||
hash.reset();
|
||||
hash.addData(key);
|
||||
hash.addData(data);
|
||||
|
||||
if (cipher == QSslKeyPrivate::Aes192Cbc)
|
||||
return key.append(hash.result().constData(), 8);
|
||||
|
||||
return key.append(hash.result());
|
||||
}
|
||||
|
||||
QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase,
|
||||
const QByteArray &iv)
|
||||
{
|
||||
QByteArray key;
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
hash.addData(passPhrase);
|
||||
hash.addData(iv);
|
||||
switch (cipher) {
|
||||
case QSslKeyPrivate::DesCbc:
|
||||
key = hash.result().left(8);
|
||||
break;
|
||||
case QSslKeyPrivate::DesEde3Cbc:
|
||||
key = hash.result();
|
||||
hash.reset();
|
||||
hash.addData(key);
|
||||
hash.addData(passPhrase);
|
||||
hash.addData(iv);
|
||||
key += hash.result().left(8);
|
||||
break;
|
||||
case QSslKeyPrivate::Rc2Cbc:
|
||||
key = hash.result();
|
||||
break;
|
||||
case QSslKeyPrivate::Aes128Cbc:
|
||||
case QSslKeyPrivate::Aes192Cbc:
|
||||
case QSslKeyPrivate::Aes256Cbc:
|
||||
return deriveAesKey(cipher, passPhrase, iv);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
int extractPkcs8KeyLength(const QList<QAsn1Element> &items, TlsKey *that)
|
||||
{
|
||||
Q_ASSERT(items.size() == 3);
|
||||
Q_ASSERT(that);
|
||||
|
||||
int keyLength = -1;
|
||||
|
||||
auto getName = [](QSsl::KeyAlgorithm algorithm) {
|
||||
switch (algorithm){
|
||||
case QSsl::Rsa: return "RSA";
|
||||
case QSsl::Dsa: return "DSA";
|
||||
case QSsl::Dh: return "DH";
|
||||
case QSsl::Ec: return "EC";
|
||||
case QSsl::Opaque: return "Opaque";
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
};
|
||||
|
||||
const auto pkcs8Info = items[1].toList();
|
||||
if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType)
|
||||
return -1;
|
||||
const QByteArray value = pkcs8Info[0].toObjectId();
|
||||
if (value == RSA_ENCRYPTION_OID) {
|
||||
if (Q_UNLIKELY(that->algorithm() != QSsl::Rsa)) {
|
||||
// We could change the 'algorithm' of QSslKey here and continue loading, but
|
||||
// this is not supported in the openssl back-end, so we'll fail here and give
|
||||
// the user some feedback.
|
||||
qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm())
|
||||
<< "\nLoading will fail.";
|
||||
return -1;
|
||||
}
|
||||
// Luckily it contains the 'normal' RSA-key format inside, so we can just recurse
|
||||
// and read the key's info.
|
||||
that->decodeDer(that->type(), that->algorithm(), items[2].value(), {}, true);
|
||||
// The real info has been filled out in the call above, so return as if it was invalid
|
||||
// to avoid overwriting the data.
|
||||
return -1;
|
||||
} else if (value == EC_ENCRYPTION_OID) {
|
||||
if (Q_UNLIKELY(that->algorithm() != QSsl::Ec)) {
|
||||
// As above for RSA.
|
||||
qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm())
|
||||
<< "\nLoading will fail.";
|
||||
return -1;
|
||||
}
|
||||
// I don't know where this is documented, but the elliptic-curve identifier has been
|
||||
// moved into the "pkcs#8 wrapper", which is what we're interested in.
|
||||
if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType)
|
||||
return -1;
|
||||
keyLength = curveBits(pkcs8Info[1].toObjectId());
|
||||
} else if (value == DSA_ENCRYPTION_OID) {
|
||||
if (Q_UNLIKELY(that->algorithm() != QSsl::Dsa)) {
|
||||
// As above for RSA.
|
||||
qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm())
|
||||
<< "\nLoading will fail.";
|
||||
return -1;
|
||||
}
|
||||
// DSA's structure is documented here:
|
||||
// https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
|
||||
if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
|
||||
return -1;
|
||||
const auto dsaInfo = pkcs8Info[1].toList();
|
||||
if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType)
|
||||
return -1;
|
||||
keyLength = numberOfBits(dsaInfo[0].value());
|
||||
} else if (value == DH_ENCRYPTION_OID) {
|
||||
if (Q_UNLIKELY(that->algorithm() != QSsl::Dh)) {
|
||||
// As above for RSA.
|
||||
qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm())
|
||||
<< "\nLoading will fail.";
|
||||
return -1;
|
||||
}
|
||||
// DH's structure is documented here:
|
||||
// https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
|
||||
if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
|
||||
return -1;
|
||||
const auto dhInfo = pkcs8Info[1].toList();
|
||||
if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType)
|
||||
return -1;
|
||||
keyLength = numberOfBits(dhInfo[0].value());
|
||||
} else {
|
||||
// in case of unexpected formats:
|
||||
qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value
|
||||
<< "\nFile a bugreport to Qt (include the line above).";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return keyLength;
|
||||
}
|
||||
|
||||
} // Unnamed namespace
|
||||
|
||||
void TlsKeyGeneric::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der,
|
||||
const QByteArray &passPhrase, bool deepClear)
|
||||
{
|
||||
keyType = type;
|
||||
keyAlgorithm = algorithm;
|
||||
|
||||
clear(deepClear);
|
||||
|
||||
if (der.isEmpty())
|
||||
return;
|
||||
// decryptPkcs8 decrypts if necessary or returns 'der' unaltered
|
||||
QByteArray decryptedDer = decryptPkcs8(der, passPhrase);
|
||||
|
||||
QAsn1Element elem;
|
||||
if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType)
|
||||
return;
|
||||
|
||||
if (type == QSsl::PublicKey) {
|
||||
// key info
|
||||
QDataStream keyStream(elem.value());
|
||||
if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
|
||||
return;
|
||||
const auto infoItems = elem.toList();
|
||||
if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType)
|
||||
return;
|
||||
if (algorithm == QSsl::Rsa) {
|
||||
if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID)
|
||||
return;
|
||||
// key data
|
||||
if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty())
|
||||
return;
|
||||
if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType)
|
||||
return;
|
||||
if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
keyLength = numberOfBits(elem.value());
|
||||
} else if (algorithm == QSsl::Dsa) {
|
||||
if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID)
|
||||
return;
|
||||
if (infoItems[1].type() != QAsn1Element::SequenceType)
|
||||
return;
|
||||
// key params
|
||||
const auto params = infoItems[1].toList();
|
||||
if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
keyLength = numberOfBits(params[0].value());
|
||||
} else if (algorithm == QSsl::Dh) {
|
||||
if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID)
|
||||
return;
|
||||
if (infoItems[1].type() != QAsn1Element::SequenceType)
|
||||
return;
|
||||
// key params
|
||||
const auto params = infoItems[1].toList();
|
||||
if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
keyLength = numberOfBits(params[0].value());
|
||||
} else if (algorithm == QSsl::Ec) {
|
||||
if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID)
|
||||
return;
|
||||
if (infoItems[1].type() != QAsn1Element::ObjectIdentifierType)
|
||||
return;
|
||||
keyLength = curveBits(infoItems[1].toObjectId());
|
||||
}
|
||||
|
||||
} else {
|
||||
const auto items = elem.toList();
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
// version
|
||||
if (items[0].type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
const QByteArray versionHex = items[0].value().toHex();
|
||||
|
||||
if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType
|
||||
&& items[2].type() == QAsn1Element::OctetStringType) {
|
||||
if (versionHex != "00" && versionHex != "01")
|
||||
return;
|
||||
int pkcs8KeyLength = extractPkcs8KeyLength(items, this);
|
||||
if (pkcs8KeyLength == -1)
|
||||
return;
|
||||
pkcs8 = true;
|
||||
keyLength = pkcs8KeyLength;
|
||||
} else if (algorithm == QSsl::Rsa) {
|
||||
if (versionHex != "00")
|
||||
return;
|
||||
if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
keyLength = numberOfBits(items[1].value());
|
||||
} else if (algorithm == QSsl::Dsa) {
|
||||
if (versionHex != "00")
|
||||
return;
|
||||
if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
keyLength = numberOfBits(items[1].value());
|
||||
} else if (algorithm == QSsl::Dh) {
|
||||
if (versionHex != "00")
|
||||
return;
|
||||
if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType)
|
||||
return;
|
||||
keyLength = numberOfBits(items[1].value());
|
||||
} else if (algorithm == QSsl::Ec) {
|
||||
if (versionHex != "01")
|
||||
return;
|
||||
if (items.size() != 4
|
||||
|| items[1].type() != QAsn1Element::OctetStringType
|
||||
|| items[2].type() != QAsn1Element::Context0Type
|
||||
|| items[3].type() != QAsn1Element::Context1Type)
|
||||
return;
|
||||
QAsn1Element oidElem;
|
||||
if (!oidElem.read(items[2].value())
|
||||
|| oidElem.type() != QAsn1Element::ObjectIdentifierType)
|
||||
return;
|
||||
keyLength = curveBits(oidElem.toObjectId());
|
||||
}
|
||||
}
|
||||
|
||||
derData = decryptedDer;
|
||||
keyIsNull = false;
|
||||
}
|
||||
|
||||
void TlsKeyGeneric::decodePem(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &pem,
|
||||
const QByteArray &passPhrase, bool deepClear)
|
||||
{
|
||||
keyType = type;
|
||||
keyAlgorithm = algorithm;
|
||||
|
||||
QMap<QByteArray, QByteArray> headers;
|
||||
QByteArray data = derFromPem(pem, &headers);
|
||||
|
||||
if (headers.value("Proc-Type") == "4,ENCRYPTED") {
|
||||
const QList<QByteArray> dekInfo = headers.value("DEK-Info").split(',');
|
||||
if (dekInfo.size() != 2) {
|
||||
clear(deepClear);
|
||||
return;
|
||||
}
|
||||
|
||||
QSslKeyPrivate::Cipher cipher;
|
||||
if (dekInfo.first() == "DES-CBC") {
|
||||
cipher = QSslKeyPrivate::DesCbc;
|
||||
} else if (dekInfo.first() == "DES-EDE3-CBC") {
|
||||
cipher = QSslKeyPrivate::DesEde3Cbc;
|
||||
} else if (dekInfo.first() == "RC2-CBC") {
|
||||
cipher = QSslKeyPrivate::Rc2Cbc;
|
||||
} else if (dekInfo.first() == "AES-128-CBC") {
|
||||
cipher = QSslKeyPrivate::Aes128Cbc;
|
||||
} else if (dekInfo.first() == "AES-192-CBC") {
|
||||
cipher = QSslKeyPrivate::Aes192Cbc;
|
||||
} else if (dekInfo.first() == "AES-256-CBC") {
|
||||
cipher = QSslKeyPrivate::Aes256Cbc;
|
||||
} else {
|
||||
clear(deepClear);
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray iv = QByteArray::fromHex(dekInfo.last());
|
||||
const QByteArray key = deriveKey(cipher, passPhrase, iv);
|
||||
data = decrypt(cipher, data, key, iv);
|
||||
}
|
||||
|
||||
decodeDer(keyType, keyAlgorithm, data, passPhrase, deepClear);
|
||||
}
|
||||
|
||||
QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const
|
||||
{
|
||||
QByteArray data;
|
||||
QMap<QByteArray, QByteArray> headers;
|
||||
|
||||
if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
|
||||
// ### use a cryptographically secure random number generator
|
||||
quint64 random = QRandomGenerator::system()->generate64();
|
||||
QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random));
|
||||
|
||||
auto cipher = QSslKeyPrivate::DesEde3Cbc;
|
||||
const QByteArray key = deriveKey(cipher, passPhrase, iv);
|
||||
data = encrypt(cipher, derData, key, iv);
|
||||
|
||||
headers.insert("Proc-Type", "4,ENCRYPTED");
|
||||
headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex());
|
||||
} else {
|
||||
data = derData;
|
||||
}
|
||||
|
||||
return pemFromDer(data, headers);
|
||||
}
|
||||
|
||||
QByteArray TlsKeyGeneric::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
|
||||
{
|
||||
Q_UNUSED(pem)
|
||||
Q_UNUSED(headers)
|
||||
|
||||
// This is quite an ugly hack, but so be it. Generic is using der, this 'pem' is coming from
|
||||
// 'toPem()' for OpenSSL.
|
||||
return derData;
|
||||
}
|
||||
|
||||
void TlsKeyGeneric::fromHandle(Qt::HANDLE handle, KeyType expectedType)
|
||||
{
|
||||
opaque = handle;
|
||||
keyType = expectedType;
|
||||
}
|
||||
|
||||
void TlsKeyGeneric::clear(bool deep)
|
||||
{
|
||||
keyIsNull = true;
|
||||
if (deep)
|
||||
std::memset(derData.data(), 0, derData.size());
|
||||
derData.clear();
|
||||
keyLength = -1;
|
||||
}
|
||||
|
||||
QByteArray TlsKeyGeneric::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase)
|
||||
{
|
||||
// RFC 5958: https://tools.ietf.org/html/rfc5958
|
||||
/*** Scheme: ***
|
||||
* Sequence
|
||||
* Sequence
|
||||
* Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12))
|
||||
* Sequence (scheme parameters)
|
||||
* Octet String (the encrypted data)
|
||||
*/
|
||||
QAsn1Element elem;
|
||||
if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType)
|
||||
return encrypted;
|
||||
|
||||
const auto items = elem.toList();
|
||||
if (items.size() != 2
|
||||
|| items[0].type() != QAsn1Element::SequenceType
|
||||
|| items[1].type() != QAsn1Element::OctetStringType) {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
const auto encryptionSchemeContainer = items[0].toList();
|
||||
|
||||
if (encryptionSchemeContainer.size() != 2
|
||||
|| encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
|
||||
|| encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
|
||||
const auto schemeParameterContainer = encryptionSchemeContainer[1].toList();
|
||||
|
||||
if (schemeParameterContainer.size() != 2
|
||||
&& schemeParameterContainer[0].type() != QAsn1Element::SequenceType
|
||||
&& schemeParameterContainer[1].type() != QAsn1Element::SequenceType) {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
EncryptionData data;
|
||||
if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) {
|
||||
data = readPbes2(schemeParameterContainer, passPhrase);
|
||||
} else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) {
|
||||
data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase);
|
||||
} else if (encryptionScheme.startsWith(PKCS12_OID)) {
|
||||
Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented
|
||||
return encrypted;
|
||||
} else {
|
||||
qWarning()
|
||||
<< "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme
|
||||
<< "\nFile a bugreport to Qt (include the line above).";
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
if (!data.initialized) {
|
||||
// something went wrong, return
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv);
|
||||
// The data is still wrapped in a octet string, so let's unwrap it
|
||||
QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey);
|
||||
return decryptedKeyElement.value();
|
||||
}
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
128
src/network/ssl/qtlskey_generic_p.h
Normal file
128
src/network/ssl/qtlskey_generic_p.h
Normal file
@ -0,0 +1,128 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTLSKEY_GENERIC_P_H
|
||||
#define QTLSKEY_GENERIC_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <private/qtlskey_base_p.h>
|
||||
#include <private/qtlsbackend_p.h>
|
||||
|
||||
#include <QtCore/qnamespace.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
// This class is what previously was known as qsslkey_qt:
|
||||
// it implements most of functionality needed by QSslKey
|
||||
// not relying on any TLS implementation. It's used by
|
||||
// our SecureTransport and Schannel backends. This
|
||||
// class is still an abstract class, since it does not
|
||||
// provide encryption and decryption - a part done by
|
||||
// a real TLS implementation.
|
||||
class TlsKeyGeneric : public TlsKeyBase
|
||||
{
|
||||
public:
|
||||
TlsKeyGeneric()
|
||||
{
|
||||
// Note, while clear is pure-virtual, the final-overrider
|
||||
// in this class is sufficient. Same for d-tor below.
|
||||
clear(false);
|
||||
}
|
||||
~TlsKeyGeneric()
|
||||
{
|
||||
clear(true);
|
||||
}
|
||||
|
||||
void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
|
||||
const QByteArray &passPhrase, bool deepClear) override;
|
||||
void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
|
||||
const QByteArray &passPhrase, bool deepClear) override;
|
||||
|
||||
QByteArray toPem(const QByteArray &passPhrase) const override;
|
||||
|
||||
QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray,
|
||||
QByteArray> *headers) const override;
|
||||
|
||||
void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override;
|
||||
|
||||
void clear(bool deep) override;
|
||||
|
||||
Qt::HANDLE handle() const override
|
||||
{
|
||||
return Qt::HANDLE(opaque);
|
||||
}
|
||||
|
||||
int length() const override
|
||||
{
|
||||
return keyLength;
|
||||
}
|
||||
|
||||
bool isPkcs8() const override
|
||||
{
|
||||
return pkcs8;
|
||||
}
|
||||
private:
|
||||
QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
|
||||
|
||||
bool pkcs8 = false;
|
||||
Qt::HANDLE opaque = nullptr;
|
||||
QByteArray derData;
|
||||
int keyLength = -1;
|
||||
};
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTLSKEY_GENERIC_P_H
|
509
src/network/ssl/qtlskey_openssl.cpp
Normal file
509
src/network/ssl/qtlskey_openssl.cpp
Normal file
@ -0,0 +1,509 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qsslsocket_openssl_symbols_p.h"
|
||||
#include "qtlskey_openssl_p.h"
|
||||
#include "qsslsocket.h"
|
||||
#include "qsslkey_p.h"
|
||||
|
||||
#include <qscopeguard.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
void TlsKeyOpenSSL::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der,
|
||||
const QByteArray &passPhrase, bool deepClear)
|
||||
{
|
||||
if (der.isEmpty())
|
||||
return;
|
||||
|
||||
keyType = type;
|
||||
keyAlgorithm = algorithm;
|
||||
|
||||
QMap<QByteArray, QByteArray> headers;
|
||||
const auto pem = pemFromDer(der, headers);
|
||||
|
||||
decodePem(type, algorithm, pem, passPhrase, deepClear);
|
||||
}
|
||||
|
||||
void TlsKeyOpenSSL::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
|
||||
const QByteArray &passPhrase, bool deepClear)
|
||||
{
|
||||
if (pem.isEmpty())
|
||||
return;
|
||||
|
||||
keyType = type;
|
||||
keyAlgorithm = algorithm;
|
||||
|
||||
clear(deepClear);
|
||||
|
||||
BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
|
||||
if (!bio)
|
||||
return;
|
||||
|
||||
const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
|
||||
|
||||
void *phrase = const_cast<char *>(passPhrase.data());
|
||||
|
||||
if (algorithm == QSsl::Rsa) {
|
||||
RSA *result = (type == QSsl::PublicKey)
|
||||
? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase)
|
||||
: q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase);
|
||||
if (rsa && rsa == result)
|
||||
keyIsNull = false;
|
||||
} else if (algorithm == QSsl::Dsa) {
|
||||
DSA *result = (type == QSsl::PublicKey)
|
||||
? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase)
|
||||
: q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
|
||||
if (dsa && dsa == result)
|
||||
keyIsNull = false;
|
||||
} else if (algorithm == QSsl::Dh) {
|
||||
EVP_PKEY *result = (type == QSsl::PublicKey)
|
||||
? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase)
|
||||
: q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
|
||||
if (result)
|
||||
dh = q_EVP_PKEY_get1_DH(result);
|
||||
if (dh)
|
||||
keyIsNull = false;
|
||||
q_EVP_PKEY_free(result);
|
||||
#ifndef OPENSSL_NO_EC
|
||||
} else if (algorithm == QSsl::Ec) {
|
||||
EC_KEY *result = (type == QSsl::PublicKey)
|
||||
? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase)
|
||||
: q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase);
|
||||
if (ec && ec == result)
|
||||
keyIsNull = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray TlsKeyOpenSSL::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
|
||||
}
|
||||
|
||||
void TlsKeyOpenSSL::clear(bool deep)
|
||||
{
|
||||
keyIsNull = true;
|
||||
|
||||
if (algorithm() == QSsl::Rsa && rsa) {
|
||||
if (deep)
|
||||
q_RSA_free(rsa);
|
||||
rsa = nullptr;
|
||||
}
|
||||
if (algorithm() == QSsl::Dsa && dsa) {
|
||||
if (deep)
|
||||
q_DSA_free(dsa);
|
||||
dsa = nullptr;
|
||||
}
|
||||
if (algorithm() == QSsl::Dh && dh) {
|
||||
if (deep)
|
||||
q_DH_free(dh);
|
||||
dh = nullptr;
|
||||
}
|
||||
#ifndef OPENSSL_NO_EC
|
||||
if (algorithm() == QSsl::Ec && ec) {
|
||||
if (deep)
|
||||
q_EC_KEY_free(ec);
|
||||
ec = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (algorithm() == QSsl::Opaque && opaque) {
|
||||
if (deep)
|
||||
q_EVP_PKEY_free(opaque);
|
||||
opaque = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Qt::HANDLE TlsKeyOpenSSL::handle() const
|
||||
{
|
||||
switch (keyAlgorithm) {
|
||||
case QSsl::Opaque:
|
||||
return Qt::HANDLE(opaque);
|
||||
case QSsl::Rsa:
|
||||
return Qt::HANDLE(rsa);
|
||||
case QSsl::Dsa:
|
||||
return Qt::HANDLE(dsa);
|
||||
case QSsl::Dh:
|
||||
return Qt::HANDLE(dh);
|
||||
#ifndef OPENSSL_NO_EC
|
||||
case QSsl::Ec:
|
||||
return Qt::HANDLE(ec);
|
||||
#endif
|
||||
default:
|
||||
return Qt::HANDLE(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
int TlsKeyOpenSSL::length() const
|
||||
{
|
||||
if (isNull() || algorithm() == QSsl::Opaque)
|
||||
return -1;
|
||||
|
||||
switch (algorithm()) {
|
||||
case QSsl::Rsa:
|
||||
return q_RSA_bits(rsa);
|
||||
case QSsl::Dsa:
|
||||
return q_DSA_bits(dsa);
|
||||
case QSsl::Dh:
|
||||
return q_DH_bits(dh);
|
||||
#ifndef OPENSSL_NO_EC
|
||||
case QSsl::Ec:
|
||||
return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec));
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray TlsKeyOpenSSL::toPem(const QByteArray &passPhrase) const
|
||||
{
|
||||
if (!QSslSocket::supportsSsl() || isNull() || algorithm() == QSsl::Opaque)
|
||||
return {};
|
||||
|
||||
const EVP_CIPHER *cipher = nullptr;
|
||||
if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
|
||||
#ifndef OPENSSL_NO_DES
|
||||
cipher = q_EVP_des_ede3_cbc();
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
BIO *bio = q_BIO_new(q_BIO_s_mem());
|
||||
if (!bio)
|
||||
return {};
|
||||
|
||||
const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
|
||||
|
||||
bool fail = false;
|
||||
|
||||
if (algorithm() == QSsl::Rsa) {
|
||||
if (type() == QSsl::PublicKey) {
|
||||
if (!q_PEM_write_bio_RSA_PUBKEY(bio, rsa))
|
||||
fail = true;
|
||||
} else {
|
||||
if (!q_PEM_write_bio_RSAPrivateKey(
|
||||
bio, rsa, cipher, (uchar *)passPhrase.data(),
|
||||
passPhrase.size(), nullptr, nullptr)) {
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
} else if (algorithm() == QSsl::Dsa) {
|
||||
if (type() == QSsl::PublicKey) {
|
||||
if (!q_PEM_write_bio_DSA_PUBKEY(bio, dsa))
|
||||
fail = true;
|
||||
} else {
|
||||
if (!q_PEM_write_bio_DSAPrivateKey(
|
||||
bio, dsa, cipher, (uchar *)passPhrase.data(),
|
||||
passPhrase.size(), nullptr, nullptr)) {
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
} else if (algorithm() == QSsl::Dh) {
|
||||
EVP_PKEY *result = q_EVP_PKEY_new();
|
||||
if (!result || !q_EVP_PKEY_set1_DH(result, dh)) {
|
||||
fail = true;
|
||||
} else if (type() == QSsl::PublicKey) {
|
||||
if (!q_PEM_write_bio_PUBKEY(bio, result))
|
||||
fail = true;
|
||||
} else if (!q_PEM_write_bio_PrivateKey(
|
||||
bio, result, cipher, (uchar *)passPhrase.data(),
|
||||
passPhrase.size(), nullptr, nullptr)) {
|
||||
fail = true;
|
||||
}
|
||||
q_EVP_PKEY_free(result);
|
||||
#ifndef OPENSSL_NO_EC
|
||||
} else if (algorithm() == QSsl::Ec) {
|
||||
if (type() == QSsl::PublicKey) {
|
||||
if (!q_PEM_write_bio_EC_PUBKEY(bio, ec))
|
||||
fail = true;
|
||||
} else {
|
||||
if (!q_PEM_write_bio_ECPrivateKey(
|
||||
bio, ec, cipher, (uchar *)passPhrase.data(),
|
||||
passPhrase.size(), nullptr, nullptr)) {
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
fail = true;
|
||||
}
|
||||
|
||||
QByteArray pem;
|
||||
if (!fail) {
|
||||
char *data = nullptr;
|
||||
const long size = q_BIO_get_mem_data(bio, &data);
|
||||
if (size > 0 && data)
|
||||
pem = QByteArray(data, size);
|
||||
}
|
||||
|
||||
return pem;
|
||||
}
|
||||
|
||||
void TlsKeyOpenSSL::fromHandle(Qt::HANDLE handle, QSsl::KeyType expectedType)
|
||||
{
|
||||
EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
|
||||
if (!evpKey || !fromEVP_PKEY(evpKey)) {
|
||||
opaque = evpKey;
|
||||
keyAlgorithm = QSsl::Opaque;
|
||||
} else {
|
||||
q_EVP_PKEY_free(evpKey);
|
||||
}
|
||||
|
||||
keyType = expectedType;
|
||||
keyIsNull = !opaque;
|
||||
}
|
||||
|
||||
bool TlsKeyOpenSSL::fromEVP_PKEY(EVP_PKEY *pkey)
|
||||
{
|
||||
if (!pkey)
|
||||
return false;
|
||||
|
||||
switch (q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey))) {
|
||||
case EVP_PKEY_RSA:
|
||||
keyIsNull = false;
|
||||
keyAlgorithm = QSsl::Rsa;
|
||||
keyType = QSsl::PrivateKey;
|
||||
rsa = q_EVP_PKEY_get1_RSA(pkey);
|
||||
|
||||
return true;
|
||||
case EVP_PKEY_DSA:
|
||||
keyIsNull = false;
|
||||
keyAlgorithm = QSsl::Dsa;
|
||||
keyType = QSsl::PrivateKey;
|
||||
dsa = q_EVP_PKEY_get1_DSA(pkey);
|
||||
|
||||
return true;
|
||||
case EVP_PKEY_DH:
|
||||
keyIsNull = false;
|
||||
keyAlgorithm = QSsl::Dh;
|
||||
keyType = QSsl::PrivateKey;
|
||||
dh = q_EVP_PKEY_get1_DH(pkey);
|
||||
return true;
|
||||
#ifndef OPENSSL_NO_EC
|
||||
case EVP_PKEY_EC:
|
||||
keyIsNull = false;
|
||||
keyAlgorithm = QSsl::Ec;
|
||||
keyType = QSsl::PrivateKey;
|
||||
ec = q_EVP_PKEY_get1_EC_KEY(pkey);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
default:;
|
||||
// Unknown key type. This could be handled as opaque, but then
|
||||
// we'd eventually leak memory since we wouldn't be able to free
|
||||
// the underlying EVP_PKEY structure. For now, we won't support
|
||||
// this.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv, bool enc)
|
||||
{
|
||||
const EVP_CIPHER *type = nullptr;
|
||||
int i = 0, len = 0;
|
||||
|
||||
switch (cipher) {
|
||||
case QSslKeyPrivate::DesCbc:
|
||||
#ifndef OPENSSL_NO_DES
|
||||
type = q_EVP_des_cbc();
|
||||
#endif
|
||||
break;
|
||||
case QSslKeyPrivate::DesEde3Cbc:
|
||||
#ifndef OPENSSL_NO_DES
|
||||
type = q_EVP_des_ede3_cbc();
|
||||
#endif
|
||||
break;
|
||||
case QSslKeyPrivate::Rc2Cbc:
|
||||
#ifndef OPENSSL_NO_RC2
|
||||
type = q_EVP_rc2_cbc();
|
||||
#endif
|
||||
break;
|
||||
case QSslKeyPrivate::Aes128Cbc:
|
||||
type = q_EVP_aes_128_cbc();
|
||||
break;
|
||||
case QSslKeyPrivate::Aes192Cbc:
|
||||
type = q_EVP_aes_192_cbc();
|
||||
break;
|
||||
case QSslKeyPrivate::Aes256Cbc:
|
||||
type = q_EVP_aes_256_cbc();
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == nullptr)
|
||||
return {};
|
||||
|
||||
QByteArray output;
|
||||
output.resize(data.size() + EVP_MAX_BLOCK_LENGTH);
|
||||
|
||||
EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new();
|
||||
q_EVP_CIPHER_CTX_reset(ctx);
|
||||
q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc);
|
||||
q_EVP_CIPHER_CTX_set_key_length(ctx, key.size());
|
||||
if (cipher == QSslKeyPrivate::Rc2Cbc)
|
||||
q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
|
||||
|
||||
q_EVP_CipherInit_ex(ctx, nullptr, nullptr,
|
||||
reinterpret_cast<const unsigned char *>(key.constData()),
|
||||
reinterpret_cast<const unsigned char *>(iv.constData()),
|
||||
enc);
|
||||
q_EVP_CipherUpdate(ctx,
|
||||
reinterpret_cast<unsigned char *>(output.data()), &len,
|
||||
reinterpret_cast<const unsigned char *>(data.constData()), data.size());
|
||||
q_EVP_CipherFinal(ctx,
|
||||
reinterpret_cast<unsigned char *>(output.data()) + len, &i);
|
||||
len += i;
|
||||
|
||||
q_EVP_CIPHER_CTX_reset(ctx);
|
||||
q_EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
return output.left(len);
|
||||
}
|
||||
|
||||
QByteArray TlsKeyOpenSSL::decrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const
|
||||
{
|
||||
return doCrypt(cipher, data, key, iv, false);
|
||||
}
|
||||
|
||||
QByteArray TlsKeyOpenSSL::encrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const
|
||||
{
|
||||
return doCrypt(cipher, data, key, iv, true);
|
||||
}
|
||||
|
||||
TlsKeyOpenSSL *TlsKeyOpenSSL::publicKeyFromX509(X509 *x)
|
||||
{
|
||||
TlsKeyOpenSSL *tlsKey = new TlsKeyOpenSSL;
|
||||
std::unique_ptr<TlsKeyOpenSSL> keyRaii(tlsKey);
|
||||
|
||||
tlsKey->keyType = QSsl::PublicKey;
|
||||
|
||||
EVP_PKEY *pkey = q_X509_get_pubkey(x);
|
||||
Q_ASSERT(pkey);
|
||||
const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
|
||||
|
||||
if (keyType == EVP_PKEY_RSA) {
|
||||
tlsKey->rsa = q_EVP_PKEY_get1_RSA(pkey);
|
||||
tlsKey->keyAlgorithm = QSsl::Rsa;
|
||||
tlsKey->keyIsNull = false;
|
||||
} else if (keyType == EVP_PKEY_DSA) {
|
||||
tlsKey->dsa = q_EVP_PKEY_get1_DSA(pkey);
|
||||
tlsKey->keyAlgorithm = QSsl::Dsa;
|
||||
tlsKey->keyIsNull = false;
|
||||
#ifndef OPENSSL_NO_EC
|
||||
} else if (keyType == EVP_PKEY_EC) {
|
||||
tlsKey->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
|
||||
tlsKey->keyAlgorithm = QSsl::Ec;
|
||||
tlsKey->keyIsNull = false;
|
||||
#endif
|
||||
} else if (keyType == EVP_PKEY_DH) {
|
||||
// DH unsupported (key is null)
|
||||
} else {
|
||||
// error? (key is null)
|
||||
}
|
||||
|
||||
q_EVP_PKEY_free(pkey);
|
||||
return keyRaii.release();
|
||||
}
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
124
src/network/ssl/qtlskey_openssl_p.h
Normal file
124
src/network/ssl/qtlskey_openssl_p.h
Normal file
@ -0,0 +1,124 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTLSKEY_OPENSSL_H
|
||||
#define QTLSKEY_OPENSSL_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include "qtlskey_base_p.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
|
||||
#include <QtNetwork/qssl.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/dh.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
class TlsKeyOpenSSL final : public TlsKeyBase
|
||||
{
|
||||
public:
|
||||
TlsKeyOpenSSL()
|
||||
: opaque(nullptr)
|
||||
{
|
||||
clear(false);
|
||||
}
|
||||
~TlsKeyOpenSSL()
|
||||
{
|
||||
clear(true);
|
||||
}
|
||||
|
||||
void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
|
||||
const QByteArray &passPhrase, bool deepClear) override;
|
||||
void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
|
||||
const QByteArray &passPhrase, bool deepClear) override;
|
||||
|
||||
QByteArray toPem(const QByteArray &passPhrase) const override;
|
||||
QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const override;
|
||||
|
||||
void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override;
|
||||
|
||||
void clear(bool deep) override;
|
||||
Qt::HANDLE handle() const override;
|
||||
int length() const override;
|
||||
|
||||
QByteArray decrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const override;
|
||||
QByteArray encrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const override;
|
||||
|
||||
static TlsKeyOpenSSL *publicKeyFromX509(X509 *x);
|
||||
|
||||
union {
|
||||
EVP_PKEY *opaque;
|
||||
RSA *rsa;
|
||||
DSA *dsa;
|
||||
DH *dh;
|
||||
#ifndef OPENSSL_NO_EC
|
||||
EC_KEY *ec;
|
||||
#endif
|
||||
};
|
||||
|
||||
bool fromEVP_PKEY(EVP_PKEY *pkey);
|
||||
};
|
||||
|
||||
} // namespace QCrypto
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTLSKEY_OPENSSL_H
|
187
src/network/ssl/qtlskey_schannel.cpp
Normal file
187
src/network/ssl/qtlskey_schannel.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtlskey_schannel_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
#include "qsslkey.h"
|
||||
|
||||
#include <QtCore/qscopeguard.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
#include <wincrypt.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace {
|
||||
const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
|
||||
{
|
||||
switch (cipher) {
|
||||
case QSslKeyPrivate::Cipher::DesCbc:
|
||||
return BCRYPT_DES_ALGORITHM;
|
||||
case QSslKeyPrivate::Cipher::DesEde3Cbc:
|
||||
return BCRYPT_3DES_ALGORITHM;
|
||||
case QSslKeyPrivate::Cipher::Rc2Cbc:
|
||||
return BCRYPT_RC2_ALGORITHM;
|
||||
case QSslKeyPrivate::Cipher::Aes128Cbc:
|
||||
case QSslKeyPrivate::Cipher::Aes192Cbc:
|
||||
case QSslKeyPrivate::Cipher::Aes256Cbc:
|
||||
return BCRYPT_AES_ALGORITHM;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
|
||||
{
|
||||
BCRYPT_ALG_HANDLE handle;
|
||||
NTSTATUS status = BCryptOpenAlgorithmProvider(
|
||||
&handle, // phAlgorithm
|
||||
getName(cipher), // pszAlgId
|
||||
nullptr, // pszImplementation
|
||||
0 // dwFlags
|
||||
);
|
||||
if (status < 0) {
|
||||
// TLSTODO:
|
||||
//qCWarning(lcSChannel, "Failed to open algorithm handle (%ld)!", status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
|
||||
const QByteArray &key)
|
||||
{
|
||||
BCRYPT_KEY_HANDLE keyHandle;
|
||||
NTSTATUS status = BCryptGenerateSymmetricKey(
|
||||
handle, // hAlgorithm
|
||||
&keyHandle, // phKey
|
||||
nullptr, // pbKeyObject (can ignore)
|
||||
0, // cbKeyObject (also ignoring)
|
||||
reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret
|
||||
ULONG(key.length()), // cbSecret
|
||||
0 // dwFlags
|
||||
);
|
||||
if (status < 0) {
|
||||
// TLSTODO - category
|
||||
//qCWarning(lcSChannel, "Failed to generate symmetric key (%ld)!", status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = BCryptSetProperty(
|
||||
keyHandle, // hObject
|
||||
BCRYPT_CHAINING_MODE, // pszProperty
|
||||
reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput
|
||||
ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput
|
||||
0 // dwFlags
|
||||
);
|
||||
if (status < 0) {
|
||||
BCryptDestroyKey(keyHandle);
|
||||
// TLSTODO: category
|
||||
//qCWarning(lcSChannel, "Failed to change the symmetric key's chaining mode (%ld)!", status);
|
||||
return nullptr;
|
||||
}
|
||||
return keyHandle;
|
||||
}
|
||||
|
||||
QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key,
|
||||
const QByteArray &iv, bool encrypt)
|
||||
{
|
||||
BCRYPT_ALG_HANDLE handle = getHandle(cipher);
|
||||
if (!handle)
|
||||
return {};
|
||||
auto handleDealloc = qScopeGuard([&handle]() {
|
||||
BCryptCloseAlgorithmProvider(handle, 0);
|
||||
});
|
||||
|
||||
BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key);
|
||||
if (!keyHandle)
|
||||
return {};
|
||||
auto keyHandleDealloc = qScopeGuard([&keyHandle]() {
|
||||
BCryptDestroyKey(keyHandle);
|
||||
});
|
||||
|
||||
QByteArray ivCopy = iv; // This gets modified, so we take a copy
|
||||
|
||||
ULONG sizeNeeded = 0;
|
||||
QVarLengthArray<unsigned char> output;
|
||||
auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
output.resize(int(sizeNeeded));
|
||||
auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data()));
|
||||
// Need to call it twice because the first iteration lets us know the size needed.
|
||||
NTSTATUS status = cryptFunction(
|
||||
keyHandle, // hKey
|
||||
input, // pbInput
|
||||
ULONG(data.length()), // cbInput
|
||||
nullptr, // pPaddingInfo
|
||||
reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV
|
||||
ULONG(ivCopy.length()), // cbIV
|
||||
sizeNeeded ? output.data() : nullptr, // pbOutput
|
||||
ULONG(output.length()), // cbOutput
|
||||
&sizeNeeded, // pcbResult
|
||||
BCRYPT_BLOCK_PADDING // dwFlags
|
||||
);
|
||||
if (status < 0) {
|
||||
qCWarning(lcSsl, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded));
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
QByteArray TlsKeySchannel::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
|
||||
const QByteArray &iv) const
|
||||
{
|
||||
return doCrypt(cipher, data, key, iv, false);
|
||||
}
|
||||
|
||||
QByteArray TlsKeySchannel::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
|
||||
const QByteArray &iv) const
|
||||
{
|
||||
return doCrypt(cipher, data, key, iv, true);
|
||||
}
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
82
src/network/ssl/qtlskey_schannel_p.h
Normal file
82
src/network/ssl/qtlskey_schannel_p.h
Normal file
@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTLSKEY_SCHANNEL_P_H
|
||||
#define QTLSKEY_SCHANNEL_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <private/qtlskey_generic_p.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
class TlsKeySchannel final : public TlsKeyGeneric
|
||||
{
|
||||
public:
|
||||
TlsKeySchannel() = default;
|
||||
|
||||
QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
|
||||
const QByteArray &iv) const override;
|
||||
QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
|
||||
const QByteArray &iv) const override;
|
||||
|
||||
Q_DISABLE_COPY_MOVE(TlsKeySchannel)
|
||||
};
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTLSKEY_SCHANNEL_P_H
|
||||
|
110
src/network/ssl/qtlskey_st.cpp
Normal file
110
src/network/ssl/qtlskey_st.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtlskey_st_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
|
||||
#include <qbytearray.h>
|
||||
|
||||
#include <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
namespace {
|
||||
|
||||
// Before this code was located in qsslkey_mac.cpp.
|
||||
QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher,
|
||||
const QByteArray &data, const QByteArray &key,
|
||||
const QByteArray &iv)
|
||||
{
|
||||
int blockSize = {};
|
||||
CCAlgorithm ccAlgorithm = {};
|
||||
switch (cipher) {
|
||||
case QSslKeyPrivate::DesCbc:
|
||||
blockSize = kCCBlockSizeDES;
|
||||
ccAlgorithm = kCCAlgorithmDES;
|
||||
break;
|
||||
case QSslKeyPrivate::DesEde3Cbc:
|
||||
blockSize = kCCBlockSize3DES;
|
||||
ccAlgorithm = kCCAlgorithm3DES;
|
||||
break;
|
||||
case QSslKeyPrivate::Rc2Cbc:
|
||||
blockSize = kCCBlockSizeRC2;
|
||||
ccAlgorithm = kCCAlgorithmRC2;
|
||||
break;
|
||||
case QSslKeyPrivate::Aes128Cbc:
|
||||
case QSslKeyPrivate::Aes192Cbc:
|
||||
case QSslKeyPrivate::Aes256Cbc:
|
||||
blockSize = kCCBlockSizeAES128;
|
||||
ccAlgorithm = kCCAlgorithmAES;
|
||||
break;
|
||||
}
|
||||
std::size_t plainLength = 0;
|
||||
QByteArray plain(data.size() + blockSize, 0);
|
||||
CCCryptorStatus status = CCCrypt(ccOp, ccAlgorithm, kCCOptionPKCS7Padding,
|
||||
key.constData(), std::size_t(key.size()),
|
||||
iv.constData(), data.constData(), std::size_t(data.size()),
|
||||
plain.data(), std::size_t(plain.size()), &plainLength);
|
||||
if (status == kCCSuccess)
|
||||
return plain.left(int(plainLength));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // Unnamed namespace.
|
||||
|
||||
QByteArray TlsKeySecureTransport::decrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const
|
||||
{
|
||||
return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv);
|
||||
}
|
||||
|
||||
QByteArray TlsKeySecureTransport::encrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const
|
||||
{
|
||||
return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
|
||||
}
|
||||
|
||||
} // namespace QSsl.
|
||||
|
||||
QT_END_NAMESPACE
|
81
src/network/ssl/qtlskey_st_p.h
Normal file
81
src/network/ssl/qtlskey_st_p.h
Normal file
@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTLSKEY_ST_P_H
|
||||
#define QTLSKEY_ST_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <private/qtlskey_generic_p.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
class TlsKeySecureTransport final : public TlsKeyGeneric
|
||||
{
|
||||
public:
|
||||
TlsKeySecureTransport() = default;
|
||||
|
||||
QByteArray decrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const override;
|
||||
QByteArray encrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const override;
|
||||
|
||||
Q_DISABLE_COPY_MOVE(TlsKeySecureTransport)
|
||||
};
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTLSKEY_ST_P_H
|
Loading…
Reference in New Issue
Block a user