Schannel support

Adds support for Schannel, an SSL backend for Windows, as an
alternative to OpenSSL.

[ChangeLog][QtNetwork][Ssl] Added support for Schannel on Desktop
Windows. To build Qt with Schannel support use '-schannel' during
configure.

Task-number: QTBUG-62637
Change-Id: Ic4fb8ed3657dab994f9f4a4ac5cbddc7001a0a46
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Mårten Nordheim 2018-08-24 17:37:36 +02:00
parent e0567d137d
commit 7cc6f78dd4
18 changed files with 2428 additions and 17 deletions

View File

@ -18,6 +18,7 @@
"ocsp": "boolean",
"sctp": "boolean",
"securetransport": "boolean",
"schannel": "boolean",
"ssl": "boolean",
"system-proxies": "boolean"
}
@ -243,13 +244,13 @@
"autoDetect": "!config.winrt && !config.wasm",
"enable": "input.openssl == 'yes' || input.openssl == 'runtime'",
"disable": "input.openssl == 'no' || input.openssl == 'linked' || input.ssl == 'no'",
"condition": "!features.securetransport && libs.openssl_headers"
"condition": "!features.securetransport && !features.schannel && libs.openssl_headers"
},
"openssl-linked": {
"label": " Qt directly linked to OpenSSL",
"autoDetect": false,
"enable": "input.openssl == 'linked'",
"condition": "!features.securetransport && libs.openssl",
"condition": "!features.securetransport && !features.schannel && libs.openssl",
"output": [
"privateFeature",
{ "type": "define", "name": "QT_LINKED_OPENSSL" }
@ -264,9 +265,18 @@
{ "type": "define", "name": "QT_SECURETRANSPORT" }
]
},
"schannel": {
"label": "Schannel",
"disable": "input.schannel == 'no' || input.ssl == 'no'",
"condition": "input.schannel == 'yes' && config.win32 && !config.winrt && (input.openssl == '' || input.openssl == 'no')",
"output": [
"publicFeature",
{ "type": "define", "name": "QT_SCHANNEL" }
]
},
"ssl": {
"label": "SSL",
"condition": "config.winrt || features.securetransport || features.openssl",
"condition": "config.winrt || features.securetransport || features.openssl || features.schannel",
"output": [ "publicFeature", "feature" ]
},
"dtls": {
@ -412,6 +422,11 @@ For example:
"args": "securetransport",
"condition": "config.darwin"
},
{
"type": "feature",
"args": "schannel",
"condition": "config.win32 && !config.winrt"
},
"openssl",
"openssl-linked",
"opensslv11",

View File

@ -121,6 +121,9 @@
#ifdef QT_SECURETRANSPORT
#include "qsslsocket_mac_p.h"
#endif
#if QT_CONFIG(schannel)
#include "qsslsocket_schannel_p.h"
#endif
#include "qssl_p.h"
#include "qsslcertificate.h"

View File

@ -75,6 +75,10 @@ struct ASN1_OBJECT;
#include <windows.security.cryptography.certificates.h>
#endif
#if QT_CONFIG(schannel)
#include <wincrypt.h>
#endif
QT_BEGIN_NAMESPACE
// forward declaration
@ -95,6 +99,10 @@ public:
#ifndef QT_NO_OPENSSL
if (x509)
q_X509_free(x509);
#endif
#if QT_CONFIG(schannel)
if (certificateContext)
CertFreeCertificateContext(certificateContext);
#endif
}
@ -143,6 +151,12 @@ public:
static QSslCertificate QSslCertificate_from_Certificate(ABI::Windows::Security::Cryptography::Certificates::ICertificate *iCertificate);
#endif
#if QT_CONFIG(schannel)
const CERT_CONTEXT *certificateContext = nullptr;
static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
#endif
};
QT_END_NAMESPACE

View File

@ -139,7 +139,7 @@ QDateTime QSslCertificate::expiryDate() const
return d->notValidAfter;
}
#ifndef Q_OS_WINRT // implemented in qsslcertificate_winrt.cpp
#if !defined(Q_OS_WINRT) && !QT_CONFIG(schannel) // implemented in qsslcertificate_{winrt,schannel}.cpp
Qt::HANDLE QSslCertificate::handle() const
{
Q_UNIMPLEMENTED();
@ -206,6 +206,10 @@ void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat f
: certificatesFromDer(data, 1);
if (!certs.isEmpty()) {
*this = *certs.first().d;
#if QT_CONFIG(schannel)
if (certificateContext)
certificateContext = CertDuplicateCertificateContext(certificateContext);
#endif
}
}
}

View File

@ -0,0 +1,62 @@
/****************************************************************************
**
** 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 "qsslcertificate.h"
#include "qsslcertificate_p.h"
#include <wincrypt.h>
QT_BEGIN_NAMESPACE
QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
{
QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
certificateContext->cbCertEncoded);
QSslCertificate certificate(derData, QSsl::Der);
certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext);
return certificate;
}
Qt::HANDLE QSslCertificate::handle() const
{
return Qt::HANDLE(d->certificateContext);
}
QT_END_NAMESPACE

View File

@ -587,6 +587,8 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key)
ciphers. You can revert to using the entire set by calling
setCiphers() with the list returned by QSslSocket::supportedCiphers().
\note This is not currently supported in the Schannel backend.
\sa setCiphers(), QSslSocket::supportedCiphers()
*/
QList<QSslCipher> QSslConfiguration::ciphers() const
@ -602,6 +604,8 @@ QList<QSslCipher> QSslConfiguration::ciphers() const
Restricting the cipher suite must be done before the handshake
phase, where the session cipher is chosen.
\note This is not currently supported in the Schannel backend.
\sa ciphers(), QSslSocket::supportedCiphers()
*/
void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)

View File

@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE
QSslEllipticCurve instances can be compared for equality and can be used as keys
in QHash and QSet. They cannot be used as key in a QMap.
\note This class is currently only supported in OpenSSL.
*/
/*!

View File

@ -0,0 +1,174 @@
/****************************************************************************
**
** 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;
}
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

View File

@ -94,6 +94,8 @@ QSslPreSharedKeyAuthenticatorPrivate::QSslPreSharedKeyAuthenticatorPrivate()
\note PSK ciphersuites are supported only when using OpenSSL 1.0.1 (or
greater) as the SSL backend.
\note PSK is currently only supported in OpenSSL.
\sa QSslSocket
*/

View File

@ -203,6 +203,7 @@
does not require this certificate to be valid. This is useful when you
want to display peer certificate details to the user without affecting the
actual SSL handshake. This mode is the default for servers.
Note: In Schannel this value acts the same as VerifyNone.
\value VerifyPeer QSslSocket will request a certificate from the peer
during the SSL handshake phase, and requires that this certificate is
@ -322,6 +323,9 @@
#ifdef QT_SECURETRANSPORT
#include "qsslsocket_mac_p.h"
#endif
#if QT_CONFIG(schannel)
#include "qsslsocket_schannel_p.h"
#endif
#include "qsslconfiguration_p.h"
#include <QtCore/qdebug.h>

View File

@ -228,7 +228,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_resumeImplementation())
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !QT_CONFIG(schannel)
Q_PRIVATE_SLOT(d_func(), void _q_caRootLoaded(QSslCertificate,QSslCertificate))
#endif
friend class QSslSocketBackendPrivate;

View File

@ -171,7 +171,7 @@ public:
void _q_flushWriteBuffer();
void _q_flushReadBuffer();
void _q_resumeImplementation();
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !QT_CONFIG(schannel)
virtual void _q_caRootLoaded(QSslCertificate,QSslCertificate) = 0;
#endif
@ -209,7 +209,7 @@ protected:
bool flushTriggered;
};
#if QT_CONFIG(securetransport)
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
// Implemented in qsslsocket_qt.cpp
QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#ifndef QSSLSOCKET_SCHANNEL_P_H
#define QSSLSOCKET_SCHANNEL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
QT_REQUIRE_CONFIG(schannel);
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_p.h"
#define SECURITY_WIN32
#include <security.h>
#include <schnlsp.h>
#undef SECURITY_WIN32
#include <memory>
QT_BEGIN_NAMESPACE
struct QHCertStoreDeleter {
void operator()(HCERTSTORE store)
{
CertCloseStore(store, 0);
}
};
typedef std::unique_ptr<void, QHCertStoreDeleter> QHCertStorePointer;
class QSslSocketBackendPrivate final : public QSslSocketPrivate
{
Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
Q_DECLARE_PUBLIC(QSslSocket)
public:
QSslSocketBackendPrivate();
~QSslSocketBackendPrivate();
// Platform specific functions
void startClientEncryption() override;
void startServerEncryption() override;
void transmit() override;
void disconnectFromHost() override;
void disconnected() override;
QSslCipher sessionCipher() const override;
QSsl::SslProtocol sessionProtocol() const override;
void continueHandshake() override;
static QList<QSslCipher> defaultCiphers();
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName);
static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase);
private:
enum class SchannelState {
InitializeHandshake, // create and transmit context (client)/accept context (server)
PerformHandshake, // get token back, process it
VerifyHandshake, // Verify that things are OK
Done, // Connection encrypted!
Renegotiate // Renegotiating!
} schannelState = SchannelState::InitializeHandshake;
void reset();
bool acquireCredentialsHandle();
ULONG getContextRequirements();
bool createContext(); // for clients
bool acceptContext(); // for server
bool performHandshake();
bool verifyHandshake();
bool renegotiate();
bool sendToken(void *token, unsigned long tokenLength, bool emitError = true);
QString targetName() const;
bool checkSslErrors();
void deallocateContext();
void freeCredentialsHandle();
void closeCertificateStores();
void sendShutdown();
void initializeCertificateStores();
bool verifyCertContext(CERT_CONTEXT *certContext);
bool rootCertOnDemandLoadingAllowed();
SecPkgContext_ConnectionInfo connectionInfo = {};
SecPkgContext_StreamSizes streamSizes = {};
CredHandle credentialHandle; // Initialized in ctor
CtxtHandle contextHandle; // Initialized in ctor
QByteArray intermediateBuffer; // data which is left-over or incomplete
QHCertStorePointer localCertificateStore = nullptr;
QHCertStorePointer peerCertificateStore = nullptr;
QHCertStorePointer caCertificateStore = nullptr;
const CERT_CONTEXT *localCertContext = nullptr;
ULONG contextAttributes = 0;
bool renegotiating = false;
bool peerCertVerified = false;
};
QT_END_NAMESPACE
#endif // QSSLSOCKET_SCHANNEL_P_H

View File

@ -49,6 +49,19 @@ qtConfig(ssl) {
ssl/qsslellipticcurve_dummy.cpp
}
qtConfig(schannel) {
HEADERS += ssl/qsslsocket_schannel_p.h
SOURCES += ssl/qsslsocket_schannel.cpp \
ssl/qsslcertificate_schannel.cpp \
ssl/qsslkey_schannel.cpp \
ssl/qsslkey_qt.cpp \
ssl/qssldiffiehellmanparameters_dummy.cpp \
ssl/qsslellipticcurve_dummy.cpp \
ssl/qsslsocket_qt.cpp
LIBS_PRIVATE += "-lSecur32" "-lCrypt32" "-lbcrypt" "-lncrypt"
}
qtConfig(securetransport) {
HEADERS += ssl/qsslsocket_mac_p.h
SOURCES += ssl/qssldiffiehellmanparameters_dummy.cpp \

View File

@ -814,7 +814,7 @@ void tst_QSslCertificate::task256066toPem()
void tst_QSslCertificate::nulInCN()
{
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT)
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT) || QT_CONFIG(schannel)
QSKIP("Generic QSslCertificatePrivate fails this test");
#endif
QList<QSslCertificate> certList =
@ -833,7 +833,7 @@ void tst_QSslCertificate::nulInCN()
void tst_QSslCertificate::nulInSan()
{
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT)
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT) || QT_CONFIG(schannel)
QSKIP("Generic QSslCertificatePrivate fails this test");
#endif
QList<QSslCertificate> certList =

View File

@ -165,9 +165,9 @@ void tst_QSslKey::createPlainTestRows(bool pemOnly)
foreach (KeyInfo keyInfo, keyInfoList) {
if (pemOnly && keyInfo.format != QSsl::EncodingFormat::Pem)
continue;
#ifdef Q_OS_WINRT
#if defined(Q_OS_WINRT) || QT_CONFIG(schannel)
if (keyInfo.fileInfo.fileName().contains("RC2-64"))
continue; // WinRT treats RC2 as 128 bit
continue; // WinRT/Schannel treats RC2 as 128 bit
#endif
#if !defined(QT_NO_SSL) && defined(QT_NO_OPENSSL) // generic backend
if (keyInfo.fileInfo.fileName().contains(QRegularExpression("-aes\\d\\d\\d-")))
@ -599,11 +599,11 @@ void tst_QSslKey::encrypt()
QFETCH(QByteArray, cipherText);
QByteArray iv("abcdefgh");
#ifdef Q_OS_WINRT
QEXPECT_FAIL("RC2-40-CBC, length 0", "WinRT treats RC2 as 128-bit", Abort);
QEXPECT_FAIL("RC2-40-CBC, length 8", "WinRT treats RC2 as 128-bit", Abort);
QEXPECT_FAIL("RC2-64-CBC, length 0", "WinRT treats RC2 as 128-bit", Abort);
QEXPECT_FAIL("RC2-64-CBC, length 8", "WinRT treats RC2 as 128-bit", Abort);
#if defined(Q_OS_WINRT) || QT_CONFIG(schannel)
QEXPECT_FAIL("RC2-40-CBC, length 0", "WinRT/Schannel treats RC2 as 128-bit", Abort);
QEXPECT_FAIL("RC2-40-CBC, length 8", "WinRT/Schannel treats RC2 as 128-bit", Abort);
QEXPECT_FAIL("RC2-64-CBC, length 0", "WinRT/Schannel treats RC2 as 128-bit", Abort);
QEXPECT_FAIL("RC2-64-CBC, length 8", "WinRT/Schannel treats RC2 as 128-bit", Abort);
#endif
QByteArray encrypted = QSslKeyPrivate::encrypt(cipher, plainText, key, iv);
QCOMPARE(encrypted, cipherText);

View File

@ -641,6 +641,10 @@ void tst_QSslSocket::sslErrors()
QFETCH(int, port);
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel)
// Needs to be < 1.2 because of the old certificate and <= 1.0 because of the mail server
socket->setProtocol(QSsl::SslProtocol::TlsV1_0);
#endif
QSignalSpy sslErrorsSpy(socket.data(), SIGNAL(sslErrors(QList<QSslError>)));
QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError)));
@ -720,6 +724,9 @@ void tst_QSslSocket::connectToHostEncrypted()
return;
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
this->socket = socket.data();
QVERIFY(socket->addCaCertificates(testDataDir + "certs/qt-test-server-cacert.pem"));
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
@ -753,6 +760,9 @@ void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName()
return;
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
this->socket = socket.data();
socket->addCaCertificates(testDataDir + "certs/qt-test-server-cacert.pem");
@ -1413,6 +1423,11 @@ void tst_QSslSocket::setLocalCertificateChain()
loop.exec();
QList<QSslCertificate> chain = socket->peerCertificateChain();
#if QT_CONFIG(schannel)
QEXPECT_FAIL("", "Schannel cannot send intermediate certificates not "
"located in a system certificate store",
Abort);
#endif
QCOMPARE(chain.size(), 2);
QCOMPARE(chain[0].serialNumber(), QByteArray("10:a0:ad:77:58:f6:6e:ae:46:93:a3:43:f9:59:8a:9e"));
QCOMPARE(chain[1].serialNumber(), QByteArray("3b:eb:99:c5:ea:d8:0b:5d:0b:97:5d:4f:06:75:4b:e1"));
@ -1480,6 +1495,9 @@ void tst_QSslSocket::setSslConfiguration()
QSslSocketPtr socket = newSocket();
QFETCH(QSslConfiguration, configuration);
socket->setSslConfiguration(configuration);
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
this->socket = socket.data();
socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
QFETCH(bool, works);
@ -2174,6 +2192,9 @@ void tst_QSslSocket::verifyMode()
return;
QSslSocket socket;
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket.setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer);
socket.setPeerVerifyMode(QSslSocket::VerifyNone);
QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone);
@ -2474,6 +2495,9 @@ void tst_QSslSocket::abortOnSslErrors()
void tst_QSslSocket::readFromClosedSocket()
{
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
socket->ignoreSslErrors();
socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
socket->ignoreSslErrors();
@ -3222,6 +3246,11 @@ void tst_QSslSocket::verifyClientCertificate()
return;
QFETCH(QSslSocket::PeerVerifyMode, peerVerifyMode);
#if QT_CONFIG(schannel)
if (peerVerifyMode == QSslSocket::QueryPeer || peerVerifyMode == QSslSocket::AutoVerifyPeer)
QSKIP("Schannel doesn't tackle requesting a certificate and not receiving one.");
#endif
SslServer server;
server.addCaCertificates = testDataDir + "certs/bogus-ca.crt";
server.ignoreSslErrors = false;
@ -3252,6 +3281,14 @@ void tst_QSslSocket::verifyClientCertificate()
// check server socket
QVERIFY(server.socket);
#if QT_CONFIG(schannel)
// As additional info to the QEXPECT_FAIL below:
// This is because schannel treats it as an error (client side) if you don't have a certificate
// when asked for one.
QEXPECT_FAIL("NoCert:VerifyPeer",
"The client disconnects first, which causes the event "
"loop to quit before the server disconnects.", Continue);
#endif
QCOMPARE(server.socket->state(), expectedState);
QCOMPARE(server.socket->isEncrypted(), works);
@ -3260,6 +3297,13 @@ void tst_QSslSocket::verifyClientCertificate()
QVERIFY(server.socket->peerCertificateChain().isEmpty());
} else {
QCOMPARE(server.socket->peerCertificate(), clientCerts.first());
#if QT_CONFIG(schannel)
if (clientCerts.count() == 1 && server.socket->peerCertificateChain().count() == 2) {
QEXPECT_FAIL("",
"Schannel includes the entire chain, not just the leaf and intermediates",
Continue);
}
#endif
QCOMPARE(server.socket->peerCertificateChain(), clientCerts);
}
@ -3270,7 +3314,7 @@ void tst_QSslSocket::verifyClientCertificate()
void tst_QSslSocket::readBufferMaxSize()
{
#ifdef QT_SECURETRANSPORT
#if defined(QT_SECURETRANSPORT) || QT_CONFIG(schannel)
// QTBUG-55170:
// SecureTransport back-end was ignoring read-buffer
// size limit, resulting (potentially) in a constantly
@ -3806,6 +3850,9 @@ void tst_QSslSocket::pskServer()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
#if QT_CONFIG(schannel)
QSKIP("Schannel does not have PSK support implemented.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (!QSslSocket::supportsSsl() || setProxy)