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>
This commit is contained in:
parent
9385e54071
commit
43d933bf50
@ -336,8 +336,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s
|
||||
ssl/qsslcertificate_schannel.cpp
|
||||
ssl/qssldiffiehellmanparameters_dummy.cpp
|
||||
ssl/qsslellipticcurve_dummy.cpp
|
||||
ssl/qsslkey_qt.cpp
|
||||
ssl/qsslkey_schannel.cpp
|
||||
ssl/qsslsocket_qt.cpp
|
||||
ssl/qsslsocket_schannel.cpp ssl/qsslsocket_schannel_p.h
|
||||
ssl/qtlsbackend_schannel_p.h
|
||||
@ -356,8 +354,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FE
|
||||
SOURCES
|
||||
ssl/qssldiffiehellmanparameters_dummy.cpp
|
||||
ssl/qsslellipticcurve_dummy.cpp
|
||||
ssl/qsslkey_mac.cpp
|
||||
ssl/qsslkey_qt.cpp
|
||||
ssl/qsslsocket_mac.cpp ssl/qsslsocket_mac_p.h
|
||||
ssl/qsslsocket_mac_shared.cpp
|
||||
ssl/qsslsocket_qt.cpp
|
||||
@ -379,7 +375,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ss
|
||||
ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h
|
||||
ssl/qssldiffiehellmanparameters_openssl.cpp
|
||||
ssl/qsslellipticcurve_openssl.cpp
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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
|
@ -74,11 +74,6 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\fn void QSslKeyPrivate::clear(bool deep)
|
||||
\internal
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
|
||||
bool deepClear)
|
||||
@ -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,11 +110,30 @@ QSslKeyPrivate::QSslKeyPrivate()
|
||||
*/
|
||||
QSslKeyPrivate::~QSslKeyPrivate()
|
||||
{
|
||||
clear(); // TLSTODO: remove
|
||||
if (keyBackend.get())
|
||||
keyBackend->clear(true /*deep clear*/);
|
||||
}
|
||||
|
||||
QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
|
||||
{
|
||||
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
|
||||
const std::unique_ptr<QSsl::TlsKey> cryptor(tlsBackend->createKey());
|
||||
return cryptor->decrypt(cipher, data, key, iv);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
|
||||
{
|
||||
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
|
||||
const std::unique_ptr<QSsl::TlsKey> cryptor(tlsBackend->createKey());
|
||||
return cryptor->encrypt(cipher, data, key, iv);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a null key.
|
||||
|
||||
@ -133,203 +144,6 @@ QSslKey::QSslKey()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
static QByteArray pkcs8Footer(bool encrypted)
|
||||
{
|
||||
return encrypted
|
||||
? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
|
||||
: QByteArrayLiteral("-----END PRIVATE KEY-----");
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Returns a DER key formatted as PEM.
|
||||
*/
|
||||
QByteArray QSslKeyPrivate::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;
|
||||
|
||||
// ### 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 (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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Returns a PEM key formatted as DER.
|
||||
*/
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QSslKey by decoding the string in the byte array
|
||||
\a encoded using a specified \a algorithm and \a encoding format.
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -159,6 +159,8 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.schannel");
|
||||
|
||||
namespace {
|
||||
bool supportsTls13();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
// This is quite an ugly hack, but so be it. Generic is using der, this 'pem' is coming from
|
||||
// 'toPem()' for OpenSSL.
|
||||
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);
|
||||
}
|
||||
|
||||
return QByteArray::fromBase64(der); // ignores newlines
|
||||
}
|
||||
|
||||
void TlsKeyGeneric::fromHandle(Qt::HANDLE handle, KeyType expectedType)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -68,6 +68,8 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_REQUIRE_CONFIG(ssl);
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
class TlsKeyOpenSSL final : public TlsKeyBase
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -57,6 +57,8 @@
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(ssl);
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
@ -614,7 +614,7 @@ Q_DECLARE_METATYPE(QSslKeyPrivate::Cipher)
|
||||
|
||||
void tst_QSslKey::encrypt_data()
|
||||
{
|
||||
QTest::addColumn<QSslKeyPrivate::Cipher>("cipher");
|
||||
QTest::addColumn<QSsl::Cipher>("cipher");
|
||||
QTest::addColumn<QByteArray>("key");
|
||||
QTest::addColumn<QByteArray>("plainText");
|
||||
QTest::addColumn<QByteArray>("cipherText");
|
||||
@ -622,91 +622,91 @@ void tst_QSslKey::encrypt_data()
|
||||
|
||||
QByteArray iv("abcdefgh");
|
||||
QTest::newRow("DES-CBC, length 0")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("956585228BAF9B1F")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 1")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(1, 'a')
|
||||
<< QByteArray::fromHex("E6880AF202BA3C12")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 2")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(2, 'a')
|
||||
<< QByteArray::fromHex("A82492386EED6026")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 3")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(3, 'a')
|
||||
<< QByteArray::fromHex("90B76D5B79519CBA")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 4")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(4, 'a')
|
||||
<< QByteArray::fromHex("63E3DD6FED87052A")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 5")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(5, 'a')
|
||||
<< QByteArray::fromHex("03ACDB0EACBDFA94")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 6")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(6, 'a')
|
||||
<< QByteArray::fromHex("7D95024E42A3A88A")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 7")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(7, 'a')
|
||||
<< QByteArray::fromHex("5003436B8A8E42E9")
|
||||
<< iv;
|
||||
QTest::newRow("DES-CBC, length 8")
|
||||
<< QSslKeyPrivate::DesCbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::DesCbc << QByteArray("01234567")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("E4C1F054BF5521C0A4A0FD4A2BC6C1B1")
|
||||
<< iv;
|
||||
|
||||
QTest::newRow("DES-EDE3-CBC, length 0")
|
||||
<< QSslKeyPrivate::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QSsl::Cipher::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("3B2B4CD0B0FD495F")
|
||||
<< iv;
|
||||
QTest::newRow("DES-EDE3-CBC, length 8")
|
||||
<< QSslKeyPrivate::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QSsl::Cipher::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("F2A5A87763C54A72A3224103D90CDB03")
|
||||
<< iv;
|
||||
|
||||
QTest::newRow("RC2-40-CBC, length 0")
|
||||
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234")
|
||||
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("6D05D52392FF6E7A")
|
||||
<< iv;
|
||||
QTest::newRow("RC2-40-CBC, length 8")
|
||||
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234")
|
||||
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("75768E64C5749072A5D168F3AFEB0005")
|
||||
<< iv;
|
||||
|
||||
QTest::newRow("RC2-64-CBC, length 0")
|
||||
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234567")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("ADAE6BF70F420130")
|
||||
<< iv;
|
||||
QTest::newRow("RC2-64-CBC, length 8")
|
||||
<< QSslKeyPrivate::Rc2Cbc << QByteArray("01234567")
|
||||
<< QSsl::Cipher::Rc2Cbc << QByteArray("01234567")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("C7BF5C80AFBE9FBEFBBB9FD935F6D0DF")
|
||||
<< iv;
|
||||
|
||||
QTest::newRow("RC2-128-CBC, length 0")
|
||||
<< QSslKeyPrivate::Rc2Cbc << QByteArray("012345679abcdefg")
|
||||
<< QSsl::Cipher::Rc2Cbc << QByteArray("012345679abcdefg")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("1E965D483A13C8FB")
|
||||
<< iv;
|
||||
QTest::newRow("RC2-128-CBC, length 8")
|
||||
<< QSslKeyPrivate::Rc2Cbc << QByteArray("012345679abcdefg")
|
||||
<< QSsl::Cipher::Rc2Cbc << QByteArray("012345679abcdefg")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("5AEC1A5B295660B02613454232F7DECE")
|
||||
<< iv;
|
||||
@ -715,34 +715,34 @@ void tst_QSslKey::encrypt_data()
|
||||
// AES needs a longer IV
|
||||
iv = QByteArray("abcdefghijklmnop");
|
||||
QTest::newRow("AES-128-CBC, length 0")
|
||||
<< QSslKeyPrivate::Aes128Cbc << QByteArray("012345679abcdefg")
|
||||
<< QSsl::Cipher::Aes128Cbc << QByteArray("012345679abcdefg")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("28DE1A9AA26601C30DD2527407121D1A")
|
||||
<< iv;
|
||||
QTest::newRow("AES-128-CBC, length 8")
|
||||
<< QSslKeyPrivate::Aes128Cbc << QByteArray("012345679abcdefg")
|
||||
<< QSsl::Cipher::Aes128Cbc << QByteArray("012345679abcdefg")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("08E880B1BA916F061C1E801D7F44D0EC")
|
||||
<< iv;
|
||||
|
||||
QTest::newRow("AES-192-CBC, length 0")
|
||||
<< QSslKeyPrivate::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QSsl::Cipher::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("E169E0E205CDC2BA895B7CF6097673B1")
|
||||
<< iv;
|
||||
QTest::newRow("AES-192-CBC, length 8")
|
||||
<< QSslKeyPrivate::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QSsl::Cipher::Aes192Cbc << QByteArray("0123456789abcdefghijklmn")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("3A227D6A3A13237316D30AA17FF9B0A7")
|
||||
<< iv;
|
||||
|
||||
QTest::newRow("AES-256-CBC, length 0")
|
||||
<< QSslKeyPrivate::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
|
||||
<< QSsl::Cipher::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
|
||||
<< QByteArray()
|
||||
<< QByteArray::fromHex("4BAACAA0D22199C97DE206C465B7B14A")
|
||||
<< iv;
|
||||
QTest::newRow("AES-256-CBC, length 8")
|
||||
<< QSslKeyPrivate::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
|
||||
<< QSsl::Cipher::Aes256Cbc << QByteArray("0123456789abcdefghijklmnopqrstuv")
|
||||
<< QByteArray(8, 'a')
|
||||
<< QByteArray::fromHex("879C8C25EC135CDF0B14490A0A7C2F67")
|
||||
<< iv;
|
||||
|
Loading…
Reference in New Issue
Block a user