summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2021-02-02 14:55:00 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2021-02-19 11:57:05 +0100
commit43d933bf5095170aef1f8223d333a13ea07c3031 (patch)
tree425331de13b19166f784c8c5e27b3e45eb09bfe3 /src/network/ssl
parent9385e5407102240555ea1c18871f9adbff600afa (diff)
Port QSslKey to the new plugin-based implementation
The idea is to have QSslKey(Private) backend-neutral and hide all library-specific code inside plugins. Fixes: QTBUG-90953 Task-number: QTBUG-65922 Change-Id: I2eeee3b2b72c78c2e24f2fb914abce3caa913be8 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> (cherry picked from commit 0352cf8e1bf57615b9faf3f6f383896444e762ac) Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qssl_p.h13
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp23
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp15
-rw-r--r--src/network/ssl/qsslkey.h1
-rw-r--r--src/network/ssl/qsslkey_mac.cpp99
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp383
-rw-r--r--src/network/ssl/qsslkey_p.cpp304
-rw-r--r--src/network/ssl/qsslkey_p.h59
-rw-r--r--src/network/ssl/qsslkey_qt.cpp785
-rw-r--r--src/network/ssl/qsslkey_schannel.cpp178
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp117
-rw-r--r--src/network/ssl/qsslsocket_qt.cpp2
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp2
-rw-r--r--src/network/ssl/qtlsbackend_p.h10
-rw-r--r--src/network/ssl/qtlskey_base_p.h4
-rw-r--r--src/network/ssl/qtlskey_generic.cpp97
-rw-r--r--src/network/ssl/qtlskey_generic_p.h40
-rw-r--r--src/network/ssl/qtlskey_openssl.cpp14
-rw-r--r--src/network/ssl/qtlskey_openssl_p.h2
-rw-r--r--src/network/ssl/qtlskey_schannel.cpp13
-rw-r--r--src/network/ssl/qtlskey_schannel_p.h4
-rw-r--r--src/network/ssl/qtlskey_st.cpp16
-rw-r--r--src/network/ssl/qtlskey_st_p.h2
23 files changed, 248 insertions, 1935 deletions
diff --git a/src/network/ssl/qssl_p.h b/src/network/ssl/qssl_p.h
index 83ccdc7fc3..fa292837cb 100644
--- a/src/network/ssl/qssl_p.h
+++ b/src/network/ssl/qssl_p.h
@@ -60,6 +60,19 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcSsl)
+namespace QSsl {
+
+enum class Cipher {
+ DesCbc,
+ DesEde3Cbc,
+ Rc2Cbc,
+ Aes128Cbc,
+ Aes192Cbc,
+ Aes256Cbc
+};
+
+} // namespace QSsl
+
QT_END_NAMESPACE
#endif // QSSL_P_H
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index af65f3b6f7..2d85171c75 100644
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ b/src/network/ssl/qsslcertificate_openssl.cpp
@@ -43,6 +43,8 @@
#include "qsslcertificate_p.h"
#include "qsslkey_p.h"
#include "qsslcertificateextension_p.h"
+#include "qtlsbackend_openssl_p.h"
+#include "qtlskey_openssl_p.h"
#include <QtCore/qendian.h>
#include <QtCore/qmutex.h>
@@ -295,25 +297,26 @@ QSslKey QSslCertificate::publicKey() const
QSslKey key;
- key.d->type = QSsl::PublicKey;
+ auto *tlsKey = QTlsBackend::backend<QSsl::TlsKeyOpenSSL>(key);
+ tlsKey->keyType = QSsl::PublicKey;
EVP_PKEY *pkey = q_X509_get_pubkey(d->x509);
Q_ASSERT(pkey);
const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
if (keyType == EVP_PKEY_RSA) {
- key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
- key.d->algorithm = QSsl::Rsa;
- key.d->isNull = false;
+ tlsKey->rsa = q_EVP_PKEY_get1_RSA(pkey);
+ tlsKey->keyAlgorithm = QSsl::Rsa;
+ tlsKey->keyIsNull = false;
} else if (keyType == EVP_PKEY_DSA) {
- key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
- key.d->algorithm = QSsl::Dsa;
- key.d->isNull = false;
+ 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) {
- key.d->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
- key.d->algorithm = QSsl::Ec;
- key.d->isNull = false;
+ 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
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index 7c050e9c3b..d17ce534cb 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -39,6 +39,8 @@
#include "qsslcertificate.h"
#include "qsslcertificate_p.h"
+#include "qtlsbackend_p.h"
+#include "qtlskey_generic_p.h"
#include "qssl_p.h"
#ifndef QT_NO_SSL
@@ -153,11 +155,14 @@ Qt::HANDLE QSslCertificate::handle() const
QSslKey QSslCertificate::publicKey() const
{
QSslKey key;
- key.d->type = QSsl::PublicKey;
- if (d->publicKeyAlgorithm != QSsl::Opaque) {
- key.d->algorithm = d->publicKeyAlgorithm;
- key.d->decodeDer(d->publicKeyDerData);
- }
+ auto *tlsKey = QTlsBackend::backend<QSsl::TlsKeyGeneric>(key);
+ if (!tlsKey)
+ return key;
+
+ tlsKey->keyType = QSsl::PublicKey;
+ if (d->publicKeyAlgorithm != QSsl::Opaque)
+ tlsKey->decodeDer(QSsl::PublicKey, d->publicKeyAlgorithm, d->publicKeyDerData, {}, false);
+
return key;
}
#endif
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index 9ed29c2561..94ce2246c9 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -88,6 +88,7 @@ public:
QSsl::KeyAlgorithm algorithm() const;
QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const;
+ // ### Qt 7: drop passPhrase
QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const;
Qt::HANDLE handle() const;
diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp
deleted file mode 100644
index 814fe1c4bc..0000000000
--- a/src/network/ssl/qsslkey_mac.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** 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 "qsslkey.h"
-#include "qsslkey_p.h"
-
-#include <CommonCrypto/CommonCrypto.h>
-
-#include <cstddef>
-
-QT_BEGIN_NAMESPACE
-
-static 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;
- }
- 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 QByteArray();
-}
-
-QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
deleted file mode 100644
index 43cb8c6de8..0000000000
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Richard J. Moore <rich@kde.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 "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-#include "qsslsocket.h"
-#include "qsslsocket_p.h"
-
-#include <QtCore/qatomic.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qiodevice.h>
-#ifndef QT_NO_DEBUG_STREAM
-#include <QtCore/qdebug.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-void QSslKeyPrivate::clear(bool deep)
-{
- isNull = true;
- if (!QSslSocket::supportsSsl())
- return;
- 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;
- }
-}
-
-bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
-{
- if (pkey == nullptr)
- return false;
-
- const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
- if (keyType == EVP_PKEY_RSA) {
- isNull = false;
- algorithm = QSsl::Rsa;
- type = QSsl::PrivateKey;
- rsa = q_EVP_PKEY_get1_RSA(pkey);
- return true;
- } else if (keyType == EVP_PKEY_DSA) {
- isNull = false;
- algorithm = QSsl::Dsa;
- type = QSsl::PrivateKey;
- dsa = q_EVP_PKEY_get1_DSA(pkey);
- return true;
- } else if (keyType == EVP_PKEY_DH) {
- isNull = false;
- algorithm = QSsl::Dh;
- type = QSsl::PrivateKey;
- dh = q_EVP_PKEY_get1_DH(pkey);
- return true;
- }
-#ifndef OPENSSL_NO_EC
- else if (keyType == EVP_PKEY_EC) {
- isNull = false;
- algorithm = QSsl::Ec;
- type = QSsl::PrivateKey;
- ec = q_EVP_PKEY_get1_EC_KEY(pkey);
- return true;
- }
-#endif
- else {
- // 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;
-}
-
-void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
-{
- QMap<QByteArray, QByteArray> headers;
- decodePem(pemFromDer(der, headers), passPhrase, deepClear);
-}
-
-void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
- bool deepClear)
-{
- if (pem.isEmpty())
- return;
-
- clear(deepClear);
-
- if (!QSslSocket::supportsSsl())
- return;
-
- BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
- if (!bio)
- return;
-
- void *phrase = const_cast<char *>(passPhrase.constData());
-
- 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)
- isNull = 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)
- isNull = 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)
- isNull = 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)
- isNull = false;
-#endif
- }
-
- q_BIO_free(bio);
-}
-
-int QSslKeyPrivate::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 QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
-{
- if (!QSslSocket::supportsSsl() || isNull || algorithm == QSsl::Opaque)
- return QByteArray();
-
- // ### the cipher should be selectable in the API:
- const EVP_CIPHER *cipher = nullptr;
- if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) {
-#ifndef OPENSSL_NO_DES
- cipher = q_EVP_des_ede3_cbc();
-#else
- return QByteArray();
-#endif
- }
-
- BIO *bio = q_BIO_new(q_BIO_s_mem());
- if (!bio)
- return QByteArray();
-
- 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;
- long size = q_BIO_get_mem_data(bio, &data);
- pem = QByteArray(data, size);
- }
- q_BIO_free(bio);
- return pem;
-}
-
-Qt::HANDLE QSslKeyPrivate::handle() const
-{
- switch (algorithm) {
- 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);
- }
-}
-
-static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, int 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();
-
- 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 QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, 0);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, 1);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index 7ba8d16f63..e0f8651982 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -75,11 +75,6 @@
QT_BEGIN_NAMESPACE
/*!
- \fn void QSslKeyPrivate::clear(bool deep)
- \internal
- */
-
-/*!
\fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
bool deepClear)
\internal
@@ -99,10 +94,7 @@ QT_BEGIN_NAMESPACE
\internal
*/
QSslKeyPrivate::QSslKeyPrivate()
- : algorithm(QSsl::Opaque)
- , opaque(nullptr)
{
- clear(false); // TLSTODO: remove
const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
if (!tlsBackend)
return;
@@ -118,216 +110,38 @@ QSslKeyPrivate::QSslKeyPrivate()
*/
QSslKeyPrivate::~QSslKeyPrivate()
{
- clear(); // TLSTODO: remove
if (keyBackend.get())
keyBackend->clear(true /*deep clear*/);
}
-/*!
- Constructs a null key.
-
- \sa isNull()
-*/
-QSslKey::QSslKey()
- : d(new QSslKeyPrivate)
-{
-}
-
-/*!
- \internal
-*/
-QByteArray QSslKeyPrivate::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();
-}
-
-static QByteArray pkcs8Header(bool encrypted)
+QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
- return encrypted
- ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
- : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
-}
-
-/*!
- \internal
-*/
-QByteArray QSslKeyPrivate::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 QByteArray();
-}
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
+ const std::unique_ptr<QSsl::TlsKey> cryptor(tlsBackend->createKey());
+ return cryptor->decrypt(cipher, data, key, iv);
+ }
-static QByteArray pkcs8Footer(bool encrypted)
-{
- return encrypted
- ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
- : QByteArrayLiteral("-----END PRIVATE KEY-----");
+ return {};
}
-/*!
- \internal
-
- Returns a DER key formatted as PEM.
-*/
-QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
+QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
- QByteArray pem(der.toBase64());
-
- const int lineWidth = 64; // RFC 1421
- const int newLines = pem.size() / lineWidth;
- const bool rem = pem.size() % lineWidth;
-
- // ### optimize
- for (int i = 0; i < newLines; ++i)
- pem.insert((i + 1) * lineWidth + i, '\n');
- if (rem)
- pem.append('\n'); // ###
-
- QByteArray extra;
- if (!headers.isEmpty()) {
- QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
- do {
- --it;
- extra += it.key() + ": " + it.value() + '\n';
- } while (it != headers.constBegin());
- extra += '\n';
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
+ const std::unique_ptr<QSsl::TlsKey> cryptor(tlsBackend->createKey());
+ return cryptor->encrypt(cipher, data, key, iv);
}
- if (isEncryptedPkcs8(der)) {
- pem.prepend(pkcs8Header(true) + '\n' + extra);
- pem.append(pkcs8Footer(true) + '\n');
-#if !QT_CONFIG(openssl)
- } else if (isPkcs8) {
- pem.prepend(pkcs8Header(false) + '\n' + extra);
- pem.append(pkcs8Footer(false) + '\n');
-#endif
- } else {
- pem.prepend(pemHeader() + '\n' + extra);
- pem.append(pemFooter() + '\n');
- }
-
- return pem;
+ return {};
}
/*!
- \internal
+ Constructs a null key.
- Returns a PEM key formatted as DER.
+ \sa isNull()
*/
-QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
-{
- QByteArray header = pemHeader();
- QByteArray footer = pemFooter();
-
- QByteArray der(pem);
-
- int headerIndex = der.indexOf(header);
- int footerIndex = der.indexOf(footer, headerIndex + header.length());
- if (type != QSsl::PublicKey) {
- if (headerIndex == -1 || footerIndex == -1) {
- header = pkcs8Header(true);
- footer = pkcs8Footer(true);
- headerIndex = der.indexOf(header);
- footerIndex = der.indexOf(footer, headerIndex + header.length());
- }
- if (headerIndex == -1 || footerIndex == -1) {
- header = pkcs8Header(false);
- footer = pkcs8Footer(false);
- headerIndex = der.indexOf(header);
- footerIndex = der.indexOf(footer, headerIndex + header.length());
- }
- }
- if (headerIndex == -1 || footerIndex == -1)
- return QByteArray();
-
- der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
-
- if (der.contains("Proc-Type:")) {
- // taken from QHttpNetworkReplyPrivate::parseHeader
- int i = 0;
- while (i < der.count()) {
- int j = der.indexOf(':', i); // field-name
- if (j == -1)
- break;
- const QByteArray field = der.mid(i, j - i).trimmed();
- j++;
- // any number of LWS is allowed before and after the value
- QByteArray value;
- do {
- i = der.indexOf('\n', j);
- if (i == -1)
- break;
- if (!value.isEmpty())
- value += ' ';
- // check if we have CRLF or only LF
- bool hasCR = (i && der[i-1] == '\r');
- int length = i -(hasCR ? 1: 0) - j;
- value += der.mid(j, length).trimmed();
- j = ++i;
- } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t'));
- if (i == -1)
- break; // something is wrong
-
- headers->insert(field, value);
- }
- der = der.mid(i);
- }
-
- return QByteArray::fromBase64(der); // ignores newlines
-}
-
-bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const
+QSslKey::QSslKey()
+ : d(new QSslKeyPrivate)
{
- static const QList<QByteArray> pbes1OIds {
- // PKCS5
- { PKCS5_MD2_DES_CBC_OID }, { PKCS5_MD2_RC2_CBC_OID }, { PKCS5_MD5_DES_CBC_OID },
- { PKCS5_MD5_RC2_CBC_OID }, { PKCS5_SHA1_DES_CBC_OID }, { PKCS5_SHA1_RC2_CBC_OID },
- };
- QAsn1Element elem;
- if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- const auto items = elem.toList();
- if (items.size() != 2
- || items[0].type() != QAsn1Element::SequenceType
- || items[1].type() != QAsn1Element::OctetStringType) {
- return false;
- }
-
- const auto encryptionSchemeContainer = items[0].toList();
- if (encryptionSchemeContainer.size() != 2
- || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
- || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
- return false;
- }
-
- const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
- return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID
- || pbes1OIds.contains(encryptionScheme)
- || encryptionScheme.startsWith(PKCS12_OID);
}
/*!
@@ -344,12 +158,12 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
: d(new QSslKeyPrivate)
{
- d->type = type;
- d->algorithm = algorithm;
- if (encoding == QSsl::Der)
- d->decodeDer(encoded, passPhrase);
- else
- d->decodePem(encoded, passPhrase);
+ if (auto *tlsKey = d->keyBackend.get()) {
+ if (encoding == QSsl::Der)
+ tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ else
+ tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ }
}
/*!
@@ -369,12 +183,13 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
QByteArray encoded;
if (device)
encoded = device->readAll();
- d->type = type;
- d->algorithm = algorithm;
- if (encoding == QSsl::Der)
- d->decodeDer(encoded, passPhrase);
- else
- d->decodePem(encoded, passPhrase);
+
+ if (auto *tlsKey = d->keyBackend.get()) {
+ if (encoding == QSsl::Der)
+ tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ else
+ tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ }
}
/*!
@@ -388,20 +203,8 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
: d(new QSslKeyPrivate)
{
-#ifndef QT_NO_OPENSSL
- EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
- if (!evpKey || !d->fromEVP_PKEY(evpKey)) {
- d->opaque = evpKey;
- d->algorithm = QSsl::Opaque;
- } else {
- q_EVP_PKEY_free(evpKey);
- }
-#else
- d->opaque = handle;
- d->algorithm = QSsl::Opaque;
-#endif
- d->type = type;
- d->isNull = !d->opaque;
+ if (auto *tlsKey = d->keyBackend.get())
+ tlsKey->fromHandle(handle, type);
}
/*!
@@ -463,7 +266,10 @@ QSslKey &QSslKey::operator=(const QSslKey &other)
*/
bool QSslKey::isNull() const
{
- return d->isNull;
+ if (const auto *tlsKey = d->keyBackend.get())
+ return tlsKey->isNull();
+
+ return true;
}
/*!
@@ -481,7 +287,10 @@ void QSslKey::clear()
*/
int QSslKey::length() const
{
- return d->length();
+ if (const auto *tlsKey = d->keyBackend.get())
+ return tlsKey->length();
+
+ return -1;
}
/*!
@@ -489,7 +298,10 @@ int QSslKey::length() const
*/
QSsl::KeyType QSslKey::type() const
{
- return d->type;
+ if (const auto *tlsKey = d->keyBackend.get())
+ return tlsKey->type();
+
+ return QSsl::PublicKey;
}
/*!
@@ -497,7 +309,10 @@ QSsl::KeyType QSslKey::type() const
*/
QSsl::KeyAlgorithm QSslKey::algorithm() const
{
- return d->algorithm;
+ if (const auto *tlsKey = d->keyBackend.get())
+ return tlsKey->algorithm();
+
+ return QSsl::Opaque;
}
/*!
@@ -508,19 +323,18 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const
*/
QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
{
- if (d->isNull || d->algorithm == QSsl::Opaque)
- return QByteArray();
+ if (isNull() || algorithm() == QSsl::Opaque)
+ return {};
// Encrypted DER is nonsense, see QTBUG-41038.
- if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty())
- return QByteArray();
+ if (type() == QSsl::PrivateKey && !passPhrase.isEmpty())
+ return {};
-#ifndef QT_NO_OPENSSL
QMap<QByteArray, QByteArray> headers;
- return d->derFromPem(toPem(passPhrase), &headers);
-#else
- return d->derData;
-#endif
+ if (const auto *tlsKey = d->keyBackend.get())
+ return tlsKey->derFromPem(toPem(passPhrase), &headers);
+
+ return {};
}
/*!
@@ -530,7 +344,10 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
*/
QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
{
- return d->toPem(passPhrase);
+ if (const auto *tlsKey = d->keyBackend.get())
+ return tlsKey->toPem(passPhrase);
+
+ return {};
}
/*!
@@ -546,7 +363,10 @@ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
*/
Qt::HANDLE QSslKey::handle() const
{
- return d->handle();
+ if (d->keyBackend.get())
+ return d->keyBackend->handle();
+
+ return nullptr;
}
/*!
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index 402d00daf1..8afb50b421 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -53,13 +53,9 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qsslkey.h"
-#include "qsslsocket_p.h" // includes wincrypt.h
-#ifndef QT_NO_OPENSSL
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#endif
+#include "qsslkey.h"
+#include "qssl_p.h"
#include <memory>
@@ -75,60 +71,11 @@ public:
QSslKeyPrivate();
~QSslKeyPrivate();
- void clear(bool deep = true);
-
-#ifndef QT_NO_OPENSSL
- bool fromEVP_PKEY(EVP_PKEY *pkey);
-#endif
- void decodeDer(const QByteArray &der, const QByteArray &passPhrase = {}, bool deepClear = true);
- void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true);
- QByteArray pemHeader() const;
- QByteArray pemFooter() const;
- QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const;
- QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const;
-
- int length() const;
- QByteArray toPem(const QByteArray &passPhrase) const;
- Qt::HANDLE handle() const;
-
- bool isEncryptedPkcs8(const QByteArray &der) const;
-#if !QT_CONFIG(openssl)
- QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
- bool isPkcs8 = false;
-#endif
-
- bool isNull;
- QSsl::KeyType type;
- QSsl::KeyAlgorithm algorithm;
-
- enum Cipher {
- DesCbc,
- DesEde3Cbc,
- Rc2Cbc,
- Aes128Cbc,
- Aes192Cbc,
- Aes256Cbc
- };
+ using Cipher = QSsl::Cipher;
Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
Q_AUTOTEST_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
-#ifndef QT_NO_OPENSSL
- union {
- EVP_PKEY *opaque;
- RSA *rsa;
- DSA *dsa;
- DH *dh;
-#ifndef OPENSSL_NO_EC
- EC_KEY *ec;
-#endif
- };
-#else
- Qt::HANDLE opaque;
- QByteArray derData;
- int keyLength;
-#endif
-
std::unique_ptr<QSsl::TlsKey> keyBackend;
QAtomicInt ref;
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp
deleted file mode 100644
index 3b8fada8fc..0000000000
--- a/src/network/ssl/qsslkey_qt.cpp
+++ /dev/null
@@ -1,785 +0,0 @@
-/****************************************************************************
-**
-** 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 "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qasn1element_p.h"
-
-#include <QtCore/qdatastream.h>
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/QMessageAuthenticationCode>
-#include <QtCore/qrandom.h>
-
-#include <QtNetwork/qpassworddigestor.h>
-
-#include <cstring>
-
-QT_USE_NAMESPACE
-
-static 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,
-};
-
-// 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
-
-typedef QMap<QByteArray, int> OidLengthMap;
-static 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;
-}
-Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap()))
-
-static int curveBits(const QByteArray &oid)
-{
- const int length = oidLengthMap->value(oid);
- return length ? length : -1;
-}
-
-static 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;
-}
-
-static 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());
-}
-
-static 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;
-}
-
-void QSslKeyPrivate::clear(bool deep)
-{
- isNull = true;
- if (deep)
- std::memset(derData.data(), 0, derData.size());
- derData.clear();
- keyLength = -1;
-}
-
-static int extractPkcs8KeyLength(const QList<QAsn1Element> &items, QSslKeyPrivate *that)
-{
- Q_ASSERT(items.size() == 3);
- int keyLength;
-
- 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(items[2].value());
- // 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;
-}
-
-void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
-{
- 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;
- isPkcs8 = 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;
- isNull = false;
-}
-
-void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
- bool deepClear)
-{
- 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;
- }
-
- Cipher cipher;
- if (dekInfo.first() == "DES-CBC") {
- cipher = DesCbc;
- } else if (dekInfo.first() == "DES-EDE3-CBC") {
- cipher = DesEde3Cbc;
- } else if (dekInfo.first() == "RC2-CBC") {
- cipher = Rc2Cbc;
- } else if (dekInfo.first() == "AES-128-CBC") {
- cipher = Aes128Cbc;
- } else if (dekInfo.first() == "AES-192-CBC") {
- cipher = Aes192Cbc;
- } else if (dekInfo.first() == "AES-256-CBC") {
- cipher = 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(data, passPhrase, deepClear);
-}
-
-int QSslKeyPrivate::length() const
-{
- return keyLength;
-}
-
-QByteArray QSslKeyPrivate::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));
-
- Cipher cipher = 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);
-}
-
-Qt::HANDLE QSslKeyPrivate::handle() const
-{
- return opaque;
-}
-
-// Maps OIDs to the encryption cipher they specify
-static 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() : initialized(false)
- {}
- EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
- : initialized(true), cipher(cipher), key(key), iv(iv)
- {}
- bool initialized;
- QSslKeyPrivate::Cipher cipher;
- QByteArray key;
- QByteArray iv;
-};
-
-static 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 bugreport 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
-static 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}
-};
-
-static 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};
-}
-
-QByteArray QSslKeyPrivate::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();
-}
diff --git a/src/network/ssl/qsslkey_schannel.cpp b/src/network/ssl/qsslkey_schannel.cpp
deleted file mode 100644
index 1e21d123f4..0000000000
--- a/src/network/ssl/qsslkey_schannel.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 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 "qssl_p.h"
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qsslcertificate_p.h"
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qscopeguard.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) {
- qCWarning(lcSsl, "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) {
- qCWarning(lcSsl, "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);
- qCWarning(lcSsl, "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
-
-QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
- const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, false);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
- const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, true);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 72cde16dce..bd3cf4d797 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -69,6 +69,7 @@
#include "qsslkey.h"
#include "qtlsbackend_openssl_p.h"
#include "qtlskey_openssl_p.h"
+#include "qx509_openssl_p.h"
#ifdef Q_OS_WIN
#include "qwindowscarootfetcher_p.h"
@@ -2323,123 +2324,14 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName)
{
- auto roots = QSslConfiguration::defaultConfiguration().caCertificates();
-#ifndef Q_OS_WIN
- // On Windows, system CA certificates are already set as default ones.
- // No need to add them again (and again) and also, if the default configuration
- // has its own set of CAs, this probably should not be amended by the ones
- // from the 'ROOT' store, since it's not what an application chose to trust.
- if (s_loadRootCertsOnDemand)
- roots.append(systemCaCertificates());
-#endif // Q_OS_WIN
- return verify(roots, certificateChain, hostName);
+ return QSsl::X509CertificateOpenSSL::verify(certificateChain, hostName);
}
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &caCertificates,
const QList<QSslCertificate> &certificateChain,
const QString &hostName)
{
- if (certificateChain.count() <= 0)
- return {QSslError(QSslError::UnspecifiedError)};
-
- QList<QSslError> errors;
- // Setup the store with the default CA certificates
- X509_STORE *certStore = q_X509_STORE_new();
- if (!certStore) {
- qCWarning(lcSsl) << "Unable to create certificate store";
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
- const std::unique_ptr<X509_STORE, decltype(&q_X509_STORE_free)> storeGuard(certStore, q_X509_STORE_free);
-
- const QDateTime now = QDateTime::currentDateTimeUtc();
- for (const QSslCertificate &caCertificate : caCertificates) {
- // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
- //
- // If several CA certificates matching the name, key identifier, and
- // serial number condition are available, only the first one will be
- // examined. This may lead to unexpected results if the same CA
- // certificate is available with different expiration dates. If a
- // ``certificate expired'' verification error occurs, no other
- // certificate will be searched. Make sure to not have expired
- // certificates mixed with valid ones.
- //
- // See also: QSslContext::fromConfiguration()
- if (caCertificate.expiryDate() >= now) {
- q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
- }
- }
-
- QList<QSslErrorEntry> lastErrors;
- if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
- qCWarning(lcSsl) << "Unable to attach external data (error list) to a store";
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Register a custom callback to get all verification errors.
- q_X509_STORE_set_verify_cb(certStore, q_X509Callback);
-
- // Build the chain of intermediate certificates
- STACK_OF(X509) *intermediates = nullptr;
- if (certificateChain.length() > 1) {
- intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
-
- if (!intermediates) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- bool first = true;
- for (const QSslCertificate &cert : certificateChain) {
- if (first) {
- first = false;
- continue;
- }
-
- q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast<X509 *>(cert.handle()));
- }
- }
-
- X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new();
- if (!storeContext) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
- std::unique_ptr<X509_STORE_CTX, decltype(&q_X509_STORE_CTX_free)> ctxGuard(storeContext, q_X509_STORE_CTX_free);
-
- if (!q_X509_STORE_CTX_init(storeContext, certStore, reinterpret_cast<X509 *>(certificateChain[0].handle()), intermediates)) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Now we can actually perform the verification of the chain we have built.
- // We ignore the result of this function since we process errors via the
- // callback.
- (void) q_X509_verify_cert(storeContext);
- ctxGuard.reset();
- q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
-
- // Now process the errors
-
- if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) {
- QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]);
- errors << error;
- }
-
- // Check the certificate name against the hostname if one was specified
- if ((!hostName.isEmpty()) && (!isMatchingHostname(certificateChain[0], hostName))) {
- // No matches in common names or alternate names.
- QSslError error(QSslError::HostNameMismatch, certificateChain[0]);
- errors << error;
- }
-
- // Translate errors from the error list into QSslErrors.
- errors.reserve(errors.size() + lastErrors.size());
- for (const auto &error : qAsConst(lastErrors))
- errors << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth));
-
- return errors;
+ return QSsl::X509CertificateOpenSSL::verify(caCertificates, certificateChain, hostName);
}
bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
@@ -2485,7 +2377,8 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
}
// Convert to Qt types
- if (!key->d->fromEVP_PKEY(pkey)) {
+ auto *tlsKey = QTlsBackend::backend<QSsl::TlsKeyOpenSSL>(*key);
+ if (!tlsKey || !tlsKey->fromEVP_PKEY(pkey)) {
qCWarning(lcSsl, "Unable to convert private key");
q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
reinterpret_cast<void (*)(void *)>(q_X509_free));
diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp
index a5d21bd77e..0a387ff599 100644
--- a/src/network/ssl/qsslsocket_qt.cpp
+++ b/src/network/ssl/qsslsocket_qt.cpp
@@ -200,7 +200,7 @@ static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &pa
QByteArray plain;
QDataStream plainStream(&plain, QIODevice::WriteOnly);
_q_PKCS12_key(key).write(plainStream);
- QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc,
+ QByteArray crypted = QSslKeyPrivate::encrypt(QSsl::Cipher::DesEde3Cbc,
plain, cKey, cIv);
QList<QAsn1Element> items;
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index 223e4b0dab..608032d3e2 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -159,6 +159,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.schannel");
+
namespace {
bool supportsTls13();
}
diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h
index b09c631108..e077521b32 100644
--- a/src/network/ssl/qtlsbackend_p.h
+++ b/src/network/ssl/qtlsbackend_p.h
@@ -54,6 +54,7 @@
#include <private/qtnetworkglobal_p.h>
#include <private/qsslkey_p.h>
+#include <private/qssl_p.h>
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslerror.h>
@@ -71,8 +72,6 @@
#include <vector>
#include <memory>
-QT_REQUIRE_CONFIG(ssl);
-
QT_BEGIN_NAMESPACE
class QByteArray;
@@ -85,7 +84,9 @@ namespace QSsl {
// 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.
+// decoding DER, it's initializing a key from DER. Note, QSslKey requires
+// a real TLS library because private keys tend to be encrypted. This
+// base class does not need a working TLS library.
class TlsKey {
public:
virtual ~TlsKey();
@@ -112,7 +113,7 @@ public:
// Needed by QSslKeyPrivate::pemFromDer() for non-OpenSSL backends.
virtual bool isPkcs8() const = 0;
- using Cipher = QSslKeyPrivate::Cipher;
+ using Cipher = QSsl::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,
@@ -243,7 +244,6 @@ Q_DECLARE_LOGGING_CATEGORY(lcTlsBackend)
#define QTlsBackend_iid "org.qt-project.Qt.QTlsBackend"
Q_DECLARE_INTERFACE(QTlsBackend, QTlsBackend_iid);
-
QT_END_NAMESPACE
#endif // QTLSBACKEND_P_H
diff --git a/src/network/ssl/qtlskey_base_p.h b/src/network/ssl/qtlskey_base_p.h
index df1c47b20e..f08f30f14e 100644
--- a/src/network/ssl/qtlskey_base_p.h
+++ b/src/network/ssl/qtlskey_base_p.h
@@ -97,7 +97,9 @@ protected:
static QByteArray pkcs8Header(bool encrypted);
static QByteArray pkcs8Footer(bool encrypted);
static bool isEncryptedPkcs8(const QByteArray &der);
-
+public:
+ // TLSTODO: this public is quick fix needed by old _openssl classes
+ // will become non-public as soon as those classes fixed.
bool keyIsNull = true;
KeyType keyType = PublicKey;
KeyAlgorithm keyAlgorithm = Opaque;
diff --git a/src/network/ssl/qtlskey_generic.cpp b/src/network/ssl/qtlskey_generic.cpp
index 5333f28814..52d7da1b0d 100644
--- a/src/network/ssl/qtlskey_generic.cpp
+++ b/src/network/ssl/qtlskey_generic.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -60,6 +61,7 @@ QT_BEGIN_NAMESPACE
// 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 {
@@ -412,7 +414,7 @@ QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhr
hash.addData(data);
- if (cipher == QSslKeyPrivate::Aes128Cbc)
+ if (cipher == QSsl::Cipher::Aes128Cbc)
return hash.result();
QByteArray key(hash.result());
@@ -420,7 +422,7 @@ QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhr
hash.addData(key);
hash.addData(data);
- if (cipher == QSslKeyPrivate::Aes192Cbc)
+ if (cipher == QSsl::Cipher::Aes192Cbc)
return key.append(hash.result().constData(), 8);
return key.append(hash.result());
@@ -434,10 +436,10 @@ QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase
hash.addData(passPhrase);
hash.addData(iv);
switch (cipher) {
- case QSslKeyPrivate::DesCbc:
+ case QSsl::Cipher::DesCbc:
key = hash.result().left(8);
break;
- case QSslKeyPrivate::DesEde3Cbc:
+ case QSsl::Cipher::DesEde3Cbc:
key = hash.result();
hash.reset();
hash.addData(key);
@@ -445,12 +447,12 @@ QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase
hash.addData(iv);
key += hash.result().left(8);
break;
- case QSslKeyPrivate::Rc2Cbc:
+ case QSsl::Cipher::Rc2Cbc:
key = hash.result();
break;
- case QSslKeyPrivate::Aes128Cbc:
- case QSslKeyPrivate::Aes192Cbc:
- case QSslKeyPrivate::Aes256Cbc:
+ case QSsl::Cipher::Aes128Cbc:
+ case QSsl::Cipher::Aes192Cbc:
+ case QSsl::Cipher::Aes256Cbc:
return deriveAesKey(cipher, passPhrase, iv);
}
return key;
@@ -686,17 +688,17 @@ void TlsKeyGeneric::decodePem(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm,
QSslKeyPrivate::Cipher cipher;
if (dekInfo.first() == "DES-CBC") {
- cipher = QSslKeyPrivate::DesCbc;
+ cipher = QSsl::Cipher::DesCbc;
} else if (dekInfo.first() == "DES-EDE3-CBC") {
- cipher = QSslKeyPrivate::DesEde3Cbc;
+ cipher = QSsl::Cipher::DesEde3Cbc;
} else if (dekInfo.first() == "RC2-CBC") {
- cipher = QSslKeyPrivate::Rc2Cbc;
+ cipher = QSsl::Cipher::Rc2Cbc;
} else if (dekInfo.first() == "AES-128-CBC") {
- cipher = QSslKeyPrivate::Aes128Cbc;
+ cipher = QSsl::Cipher::Aes128Cbc;
} else if (dekInfo.first() == "AES-192-CBC") {
- cipher = QSslKeyPrivate::Aes192Cbc;
+ cipher = QSsl::Cipher::Aes192Cbc;
} else if (dekInfo.first() == "AES-256-CBC") {
- cipher = QSslKeyPrivate::Aes256Cbc;
+ cipher = QSsl::Cipher::Aes256Cbc;
} else {
clear(deepClear);
return;
@@ -720,7 +722,7 @@ QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const
quint64 random = QRandomGenerator::system()->generate64();
QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random));
- auto cipher = QSslKeyPrivate::DesEde3Cbc;
+ auto cipher = QSsl::Cipher::DesEde3Cbc;
const QByteArray key = deriveKey(cipher, passPhrase, iv);
data = encrypt(cipher, derData, key, iv);
@@ -735,12 +737,67 @@ QByteArray TlsKeyGeneric::toPem(const QByteArray &passPhrase) const
QByteArray TlsKeyGeneric::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
{
- Q_UNUSED(pem)
- Q_UNUSED(headers)
+ if (derData.size())
+ return derData;
+
+ 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);
+ }
- // This is quite an ugly hack, but so be it. Generic is using der, this 'pem' is coming from
- // 'toPem()' for OpenSSL.
- return derData;
+ return QByteArray::fromBase64(der); // ignores newlines
}
void TlsKeyGeneric::fromHandle(Qt::HANDLE handle, KeyType expectedType)
diff --git a/src/network/ssl/qtlskey_generic_p.h b/src/network/ssl/qtlskey_generic_p.h
index d8b4c47975..acda061d08 100644
--- a/src/network/ssl/qtlskey_generic_p.h
+++ b/src/network/ssl/qtlskey_generic_p.h
@@ -66,24 +66,11 @@ 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.
+// our SecureTransport and Schannel backends.
class TlsKeyGeneric : public TlsKeyBase
{
public:
- TlsKeyGeneric(KeyType type = PublicKey, KeyAlgorithm algorithm = Opaque)
- : TlsKeyBase(type, algorithm)
- {
- // Note, while clear is pure-virtual in the base class,
- // the final-overrider in this class is sufficient.
- clear(false);
- }
- ~TlsKeyGeneric()
- {
- clear(true);
- }
+ using TlsKeyBase::TlsKeyBase;
void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
const QByteArray &passPhrase, bool deepClear) override;
@@ -113,6 +100,29 @@ public:
{
return pkcs8;
}
+
+ QByteArray decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override
+ {
+ // The real implementation is to be provided by Schannel or SecureTransport.
+ Q_UNUSED(cipher)
+ Q_UNUSED(data)
+ Q_UNUSED(key)
+ Q_UNUSED(iv)
+
+ return {};
+ }
+ QByteArray encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override
+ {
+ // The real implementation is to be provided by Schannel or SecureTransport.
+ Q_UNUSED(cipher)
+ Q_UNUSED(data)
+ Q_UNUSED(key)
+ Q_UNUSED(iv)
+
+ return {};
+ }
private:
QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
diff --git a/src/network/ssl/qtlskey_openssl.cpp b/src/network/ssl/qtlskey_openssl.cpp
index 6517be2d76..c649d57f19 100644
--- a/src/network/ssl/qtlskey_openssl.cpp
+++ b/src/network/ssl/qtlskey_openssl.cpp
@@ -401,28 +401,28 @@ QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
int i = 0, len = 0;
switch (cipher) {
- case QSslKeyPrivate::DesCbc:
+ case QSsl::Cipher::DesCbc:
#ifndef OPENSSL_NO_DES
type = q_EVP_des_cbc();
#endif
break;
- case QSslKeyPrivate::DesEde3Cbc:
+ case QSsl::Cipher::DesEde3Cbc:
#ifndef OPENSSL_NO_DES
type = q_EVP_des_ede3_cbc();
#endif
break;
- case QSslKeyPrivate::Rc2Cbc:
+ case QSsl::Cipher::Rc2Cbc:
#ifndef OPENSSL_NO_RC2
type = q_EVP_rc2_cbc();
#endif
break;
- case QSslKeyPrivate::Aes128Cbc:
+ case QSsl::Cipher::Aes128Cbc:
type = q_EVP_aes_128_cbc();
break;
- case QSslKeyPrivate::Aes192Cbc:
+ case QSsl::Cipher::Aes192Cbc:
type = q_EVP_aes_192_cbc();
break;
- case QSslKeyPrivate::Aes256Cbc:
+ case QSsl::Cipher::Aes256Cbc:
type = q_EVP_aes_256_cbc();
break;
}
@@ -437,7 +437,7 @@ QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
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)
+ if (cipher == QSsl::Cipher::Rc2Cbc)
q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
q_EVP_CipherInit_ex(ctx, nullptr, nullptr,
diff --git a/src/network/ssl/qtlskey_openssl_p.h b/src/network/ssl/qtlskey_openssl_p.h
index 1bcad0090d..4e8377c3b3 100644
--- a/src/network/ssl/qtlskey_openssl_p.h
+++ b/src/network/ssl/qtlskey_openssl_p.h
@@ -68,6 +68,8 @@
QT_BEGIN_NAMESPACE
+QT_REQUIRE_CONFIG(ssl);
+
namespace QSsl {
class TlsKeyOpenSSL final : public TlsKeyBase
diff --git a/src/network/ssl/qtlskey_schannel.cpp b/src/network/ssl/qtlskey_schannel.cpp
index d53cfe61e9..39ffa94837 100644
--- a/src/network/ssl/qtlskey_schannel.cpp
+++ b/src/network/ssl/qtlskey_schannel.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qscopeguard.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/qt_windows.h>
#include <wincrypt.h>
QT_BEGIN_NAMESPACE
@@ -54,15 +55,15 @@ namespace {
const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
{
switch (cipher) {
- case QSslKeyPrivate::Cipher::DesCbc:
+ case QSsl::Cipher::DesCbc:
return BCRYPT_DES_ALGORITHM;
- case QSslKeyPrivate::Cipher::DesEde3Cbc:
+ case QSsl::Cipher::DesEde3Cbc:
return BCRYPT_3DES_ALGORITHM;
- case QSslKeyPrivate::Cipher::Rc2Cbc:
+ case QSsl::Cipher::Rc2Cbc:
return BCRYPT_RC2_ALGORITHM;
- case QSslKeyPrivate::Cipher::Aes128Cbc:
- case QSslKeyPrivate::Cipher::Aes192Cbc:
- case QSslKeyPrivate::Cipher::Aes256Cbc:
+ case QSsl::Cipher::Aes128Cbc:
+ case QSsl::Cipher::Aes192Cbc:
+ case QSsl::Cipher::Aes256Cbc:
return BCRYPT_AES_ALGORITHM;
}
Q_UNREACHABLE();
diff --git a/src/network/ssl/qtlskey_schannel_p.h b/src/network/ssl/qtlskey_schannel_p.h
index 2001453922..0359176b14 100644
--- a/src/network/ssl/qtlskey_schannel_p.h
+++ b/src/network/ssl/qtlskey_schannel_p.h
@@ -57,6 +57,8 @@
#include <QtCore/qglobal.h>
+QT_REQUIRE_CONFIG(ssl);
+
QT_BEGIN_NAMESPACE
namespace QSsl {
@@ -70,8 +72,6 @@ public:
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
diff --git a/src/network/ssl/qtlskey_st.cpp b/src/network/ssl/qtlskey_st.cpp
index e4616c5167..71ef33ff20 100644
--- a/src/network/ssl/qtlskey_st.cpp
+++ b/src/network/ssl/qtlskey_st.cpp
@@ -60,21 +60,21 @@ QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher,
int blockSize = {};
CCAlgorithm ccAlgorithm = {};
switch (cipher) {
- case QSslKeyPrivate::DesCbc:
+ case Cipher::DesCbc:
blockSize = kCCBlockSizeDES;
ccAlgorithm = kCCAlgorithmDES;
break;
- case QSslKeyPrivate::DesEde3Cbc:
+ case Cipher::DesEde3Cbc:
blockSize = kCCBlockSize3DES;
ccAlgorithm = kCCAlgorithm3DES;
break;
- case QSslKeyPrivate::Rc2Cbc:
+ case Cipher::Rc2Cbc:
blockSize = kCCBlockSizeRC2;
ccAlgorithm = kCCAlgorithmRC2;
break;
- case QSslKeyPrivate::Aes128Cbc:
- case QSslKeyPrivate::Aes192Cbc:
- case QSslKeyPrivate::Aes256Cbc:
+ case Cipher::Aes128Cbc:
+ case Cipher::Aes192Cbc:
+ case Cipher::Aes256Cbc:
blockSize = kCCBlockSizeAES128;
ccAlgorithm = kCCAlgorithmAES;
break;
@@ -93,13 +93,13 @@ QByteArray wrapCCCrypt(CCOperation ccOp, QSslKeyPrivate::Cipher cipher,
} // Unnamed namespace.
-QByteArray TlsKeySecureTransport::decrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
+QByteArray TlsKeySecureTransport::decrypt(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,
+QByteArray TlsKeySecureTransport::encrypt(Cipher cipher, const QByteArray &data,
const QByteArray &key, const QByteArray &iv) const
{
return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
diff --git a/src/network/ssl/qtlskey_st_p.h b/src/network/ssl/qtlskey_st_p.h
index 9a8a84094f..df690f9f53 100644
--- a/src/network/ssl/qtlskey_st_p.h
+++ b/src/network/ssl/qtlskey_st_p.h
@@ -57,6 +57,8 @@
#include <QtCore/qglobal.h>
+QT_REQUIRE_CONFIG(ssl);
+
QT_BEGIN_NAMESPACE
namespace QSsl {