diff options
Diffstat (limited to 'src/network/ssl/qsslcertificate.cpp')
-rw-r--r-- | src/network/ssl/qsslcertificate.cpp | 431 |
1 files changed, 319 insertions, 112 deletions
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index c63ef5d205..9878c603b6 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! @@ -121,8 +85,7 @@ \value Wildcard This provides a simple pattern matching syntax similar to that used by shells (command interpreters) for "file - globbing". See \l {QRegularExpression#Wildcard matching} - {QRegularExpression Wildcard Matching}. + globbing". See \l {QRegularExpression::fromWildcard()}. \value FixedString The pattern is a fixed string. This is equivalent to using the RegularExpression pattern on a string in @@ -131,31 +94,46 @@ */ #include <QtNetwork/qtnetworkglobal.h> -#ifndef QT_NO_OPENSSL -#include "qsslsocket_openssl_symbols_p.h" -#endif -#ifdef QT_SECURETRANSPORT -#include "qsslsocket_mac_p.h" -#endif -#if QT_CONFIG(schannel) -#include "qsslsocket_schannel_p.h" -#endif + #if QT_CONFIG(regularexpression) #include "qregularexpression.h" #endif -#include "qssl_p.h" -#include "qsslcertificate.h" + +#include "qsslcertificateextension_p.h" #include "qsslcertificate_p.h" +#include "qsslcertificate.h" +#include "qssl_p.h" + #ifndef QT_NO_SSL +#include "qsslsocket_p.h" #include "qsslkey_p.h" #endif #include <QtCore/qdir.h> -#include <QtCore/qdiriterator.h> +#include <QtCore/qdirlisting.h> #include <QtCore/qfile.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +QT_IMPL_METATYPE_EXTERN(QSslCertificate) + +QSslCertificatePrivate::QSslCertificatePrivate() +{ +#ifndef QT_NO_SSL + QSslSocketPrivate::ensureInitialized(); +#endif + + const QTlsBackend *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (tlsBackend) + backend.reset(tlsBackend->createCertificate()); + else + qCWarning(lcSsl, "No TLS backend is available"); +} + +QSslCertificatePrivate::~QSslCertificatePrivate() = default; + /*! Constructs a QSslCertificate by reading \a format encoded data from \a device and using the first certificate found. You can @@ -165,13 +143,25 @@ QT_BEGIN_NAMESPACE QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) : d(new QSslCertificatePrivate) { -#ifndef QT_NO_OPENSSL - QSslSocketPrivate::ensureInitialized(); - if (device && QSslSocket::supportsSsl()) -#else - if (device) -#endif - d->init(device->readAll(), format); + if (device) { + const auto data = device->readAll(); + if (data.isEmpty()) + return; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) + return; + + auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!X509Reader) { + qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER"); + return; + } + + QList<QSslCertificate> certs = X509Reader(data, 1); + if (!certs.isEmpty()) + d = certs.first().d; + } } /*! @@ -183,11 +173,22 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format) : d(new QSslCertificatePrivate) { -#ifndef QT_NO_OPENSSL - QSslSocketPrivate::ensureInitialized(); - if (QSslSocket::supportsSsl()) -#endif - d->init(data, format); + if (data.isEmpty()) + return; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) + return; + + auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!X509Reader) { + qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER"); + return; + } + + const QList<QSslCertificate> certs = X509Reader(data, 1); + if (!certs.isEmpty()) + d = certs.first().d; } /*! @@ -229,6 +230,20 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) returns \c false. */ +bool QSslCertificate::operator==(const QSslCertificate &other) const +{ + if (d == other.d) + return true; + + if (isNull() && other.isNull()) + return true; + + if (d->backend.get() && other.d->backend.get()) + return d->backend->isEqual(*other.d->backend.get()); + + return false; +} + /*! \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const @@ -246,6 +261,13 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) \sa clear() */ +bool QSslCertificate::isNull() const +{ + if (const auto *backend = d->backend.get()) + return backend->isNull(); + + return true; +} /*! Returns \c true if this certificate is blacklisted; otherwise @@ -268,6 +290,13 @@ bool QSslCertificate::isBlacklisted() const A certificate is considered self-signed its issuer and subject are identical. */ +bool QSslCertificate::isSelfSigned() const +{ + if (const auto *backend = d->backend.get()) + return backend->isSelfSigned(); + + return false; +} /*! Clears the contents of this certificate, making it a null @@ -286,12 +315,26 @@ void QSslCertificate::clear() \fn QByteArray QSslCertificate::version() const Returns the certificate's version string. */ +QByteArray QSslCertificate::version() const +{ + if (const auto *backend = d->backend.get()) + return backend->version(); + + return {}; +} /*! \fn QByteArray QSslCertificate::serialNumber() const Returns the certificate's serial number string in hexadecimal format. */ +QByteArray QSslCertificate::serialNumber() const +{ + if (const auto *backend = d->backend.get()) + return backend->serialNumber(); + + return {}; +} /*! Returns a cryptographic digest of this certificate. By default, @@ -313,6 +356,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QStringList QSslCertificate::issuerInfo(SubjectInfo info) const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfo(info); + + return {}; +} /*! \fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const @@ -323,6 +373,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfo(attribute); + + return {}; +} /*! \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const @@ -333,6 +390,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa issuerInfo() */ +QStringList QSslCertificate::subjectInfo(SubjectInfo info) const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfo(info); + + return {}; +} /*! \fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const @@ -343,6 +407,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa issuerInfo() */ +QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfo(attribute); + + return {}; +} /*! \fn QList<QByteArray> QSslCertificate::subjectInfoAttributes() const @@ -356,6 +427,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QList<QByteArray> QSslCertificate::subjectInfoAttributes() const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectInfoAttributes(); + + return {}; +} /*! \fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const @@ -369,6 +447,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QList<QByteArray> QSslCertificate::issuerInfoAttributes() const +{ + if (const auto *backend = d->backend.get()) + return backend->issuerInfoAttributes(); + + return {}; +} /*! \fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const @@ -385,6 +470,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa subjectInfo() */ +QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const +{ + if (const auto *backend = d->backend.get()) + return backend->subjectAlternativeNames(); + + return {}; +} /*! \fn QDateTime QSslCertificate::effectiveDate() const @@ -394,6 +486,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa expiryDate() */ +QDateTime QSslCertificate::effectiveDate() const +{ + if (const auto *backend = d->backend.get()) + return backend->effectiveDate(); + + return {}; +} /*! \fn QDateTime QSslCertificate::expiryDate() const @@ -403,6 +502,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \sa effectiveDate() */ +QDateTime QSslCertificate::expiryDate() const +{ + if (const auto *backend = d->backend.get()) + return backend->expiryDate(); + + return {}; +} /*! \fn Qt::HANDLE QSslCertificate::handle() const @@ -416,11 +522,29 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons non-portable, and its return value may vary from platform to platform or change from minor release to minor release. */ +Qt::HANDLE QSslCertificate::handle() const +{ + if (const auto *backend = d->backend.get()) + return backend->handle(); + + return {}; +} +#ifndef QT_NO_SSL /*! \fn QSslKey QSslCertificate::publicKey() const Returns the certificate subject's public key. */ +QSslKey QSslCertificate::publicKey() const +{ + QSslKey key; + if (const auto *backend = d->backend.get()) + QTlsBackend::resetBackend(key, backend->publicKey()); + + return key; +} +#endif // QT_NO_SSL + /*! \fn QList<QSslCertificateExtension> QSslCertificate::extensions() const @@ -428,6 +552,10 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns a list containing the X509 extensions of this certificate. \since 5.0 */ +QList<QSslCertificateExtension> QSslCertificate::extensions() const +{ + return d->extensions(); +} /*! \fn QByteArray QSslCertificate::toPem() const @@ -435,6 +563,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns this certificate converted to a PEM (Base64) encoded representation. */ +QByteArray QSslCertificate::toPem() const +{ + if (const auto *backend = d->backend.get()) + return backend->toPem(); + + return {}; +} /*! \fn QByteArray QSslCertificate::toDer() const @@ -442,6 +577,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons Returns this certificate converted to a DER (binary) encoded representation. */ +QByteArray QSslCertificate::toDer() const +{ + if (const auto *backend = d->backend.get()) + return backend->toDer(); + + return {}; +} /*! \fn QString QSslCertificate::toText() const @@ -451,6 +593,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons \since 5.0 */ +QString QSslCertificate::toText() const +{ + if (const auto *backend = d->backend.get()) + return backend->toText(); + + return {}; +} /*! \since 5.15 @@ -475,16 +624,16 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, QString sourcePath = QDir::fromNativeSeparators(path); // Find the path without the filename - QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(QLatin1Char('/'))); + QStringView pathPrefix = QStringView(sourcePath).left(sourcePath.lastIndexOf(u'/')); // Check if the path contains any special chars int pos = -1; #if QT_CONFIG(regularexpression) if (syntax == PatternSyntax::Wildcard) - pos = pathPrefix.indexOf(QRegularExpression(QLatin1String("[*?[]"))); + pos = pathPrefix.indexOf(QRegularExpression("[*?[]"_L1)); else if (syntax == PatternSyntax::RegularExpression) - pos = sourcePath.indexOf(QRegularExpression(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"))); + pos = sourcePath.indexOf(QRegularExpression("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"_L1)); #else if (syntax == PatternSyntax::Wildcard || syntax == PatternSyntax::RegExp) qWarning("Regular expression support is disabled in this build. Only fixed string can be searched"); @@ -494,11 +643,11 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, if (pos != -1) { // there was a special char in the path so cut of the part containing that char. pathPrefix = pathPrefix.left(pos); - const int lastIndexOfSlash = pathPrefix.lastIndexOf(QLatin1Char('/')); + const qsizetype lastIndexOfSlash = pathPrefix.lastIndexOf(u'/'); if (lastIndexOfSlash != -1) pathPrefix = pathPrefix.left(lastIndexOfSlash); else - pathPrefix.clear(); + pathPrefix = {}; } else { // Check if the path is a file. if (QFileInfo(sourcePath).isFile()) { @@ -515,10 +664,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, // Special case - if the prefix ends up being nothing, use "." instead. int startIndex = 0; if (pathPrefix.isEmpty()) { - pathPrefix = QLatin1String("."); + pathPrefix = u"."; startIndex = 2; } + const QString pathPrefixString = pathPrefix.toString(); + // The path can be a file or directory. QList<QSslCertificate> certs; @@ -529,9 +680,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath)); #endif - QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); - while (it.hasNext()) { - QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex); + using F = QDirListing::IteratorFlag; + constexpr auto iterFlags = F::FollowSymlinks | F::Recursive; + for (const auto &dirEntry : QDirListing(pathPrefixString, QDir::Files, iterFlags)) { + QString filePath = dirEntry.filePath(); + if (startIndex > 0) + filePath.remove(0, startIndex); #if QT_CONFIG(regularexpression) if (!pattern.match(filePath).hasMatch()) @@ -576,13 +730,22 @@ QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::Enco */ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format) { - return (format == QSsl::Pem) - ? QSslCertificatePrivate::certificatesFromPem(data) - : QSslCertificatePrivate::certificatesFromDer(data); + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return {}; + } + + auto reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader(); + if (!reader) { + qCWarning(lcSsl, "The available TLS backend does not support reading PEM/DER"); + return {}; + } + + return reader(data, -1); } #ifndef QT_NO_SSL - /*! Verifies a certificate chain. The chain to be verified is passed in the \a certificateChain parameter. The first certificate in the list should @@ -597,13 +760,19 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E \since 5.0 */ -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName) -#else -QList<QSslError> QSslCertificate::verify(QList<QSslCertificate> certificateChain, const QString &hostName) -#endif { - return QSslSocketBackendPrivate::verify(certificateChain, hostName); + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return {}; + } + auto verifyPtr = tlsBackend->X509Verifier(); + if (!verifyPtr) { + qCWarning(lcSsl, "Available TLS backend does not support manual certificate verification"); + return {}; + } + return verifyPtr(certificateChain, hostName); } /*! @@ -623,10 +792,43 @@ bool QSslCertificate::importPkcs12(QIODevice *device, QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase) { - return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase); + if (!device || !key || !certificate) + return false; + + const auto *tlsBackend = QTlsBackend::activeOrAnyBackend(); + if (!tlsBackend) { + qCWarning(lcSsl, "No TLS backend is available"); + return false; + } + + if (auto reader = tlsBackend->X509Pkcs12Reader()) + return reader(device, key, certificate, caCertificates, passPhrase); + + qCWarning(lcSsl, "Available TLS backend does not support PKCS12"); + + return false; } +#endif // QT_NO_SSL -#endif +QList<QSslCertificateExtension> QSslCertificatePrivate::extensions() const +{ + QList<QSslCertificateExtension> result; + + if (backend.get()) { + auto nExt = backend->numberOfExtensions(); + for (decltype (nExt) i = 0; i < nExt; ++i) { + QSslCertificateExtension ext; + ext.d->oid = backend->oidForExtension(i); + ext.d->name = backend->nameForExtension(i); + ext.d->value = backend->valueForExtension(i); + ext.d->critical = backend->isExtensionCritical(i); + ext.d->supported = backend->isExtensionSupported(i); + result << ext; + } + } + + return result; +} // These certificates are known to be fraudulent and were created during the comodo // compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html @@ -681,7 +883,7 @@ static const char *const certificate_blacklist[] = { bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate) { for (int a = 0; certificate_blacklist[a] != nullptr; a++) { - QString blacklistedCommonName = QString::fromUtf8(certificate_blacklist[(a+1)]); + auto blacklistedCommonName = QAnyStringView(QUtf8StringView(certificate_blacklist[(a+1)])); if (certificate.serialNumber() == certificate_blacklist[a++] && (certificate.subjectInfo(QSslCertificate::CommonName).contains(blacklistedCommonName) || certificate.issuerInfo(QSslCertificate::CommonName).contains(blacklistedCommonName))) @@ -692,19 +894,18 @@ bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate) QByteArray QSslCertificatePrivate::subjectInfoToString(QSslCertificate::SubjectInfo info) { - QByteArray str; switch (info) { - case QSslCertificate::Organization: str = QByteArray("O"); break; - case QSslCertificate::CommonName: str = QByteArray("CN"); break; - case QSslCertificate::LocalityName: str = QByteArray("L"); break; - case QSslCertificate::OrganizationalUnitName: str = QByteArray("OU"); break; - case QSslCertificate::CountryName: str = QByteArray("C"); break; - case QSslCertificate::StateOrProvinceName: str = QByteArray("ST"); break; - case QSslCertificate::DistinguishedNameQualifier: str = QByteArray("dnQualifier"); break; - case QSslCertificate::SerialNumber: str = QByteArray("serialNumber"); break; - case QSslCertificate::EmailAddress: str = QByteArray("emailAddress"); break; + case QSslCertificate::Organization: return "O"_ba; + case QSslCertificate::CommonName: return "CN"_ba; + case QSslCertificate::LocalityName: return"L"_ba; + case QSslCertificate::OrganizationalUnitName: return "OU"_ba; + case QSslCertificate::CountryName: return "C"_ba; + case QSslCertificate::StateOrProvinceName: return "ST"_ba; + case QSslCertificate::DistinguishedNameQualifier: return "dnQualifier"_ba; + case QSslCertificate::SerialNumber: return "serialNumber"_ba; + case QSslCertificate::EmailAddress: return "emailAddress"_ba; } - return str; + return QByteArray(); } /*! @@ -721,13 +922,13 @@ QString QSslCertificate::issuerDisplayName() const QStringList names; names = issuerInfo(QSslCertificate::CommonName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = issuerInfo(QSslCertificate::Organization); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = issuerInfo(QSslCertificate::OrganizationalUnitName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); return QString(); } @@ -746,24 +947,30 @@ QString QSslCertificate::subjectDisplayName() const QStringList names; names = subjectInfo(QSslCertificate::CommonName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = subjectInfo(QSslCertificate::Organization); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); names = subjectInfo(QSslCertificate::OrganizationalUnitName); if (!names.isEmpty()) - return names.first(); + return names.constFirst(); return QString(); } /*! - \fn size_t qHash(const QSslCertificate &key, size_t seed) - Returns the hash value for the \a key, using \a seed to seed the calculation. \since 5.4 \relates QHash */ +size_t qHash(const QSslCertificate &key, size_t seed) noexcept +{ + if (const auto *backend = key.d->backend.get()) + return backend->hash(seed); + + return seed; + +} #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QSslCertificate &certificate) @@ -771,15 +978,15 @@ QDebug operator<<(QDebug debug, const QSslCertificate &certificate) QDebugStateSaver saver(debug); debug.resetFormat().nospace(); debug << "QSslCertificate(" - << certificate.version() - << ", " << certificate.serialNumber() - << ", " << certificate.digest().toBase64() - << ", " << certificate.issuerDisplayName() - << ", " << certificate.subjectDisplayName() - << ", " << certificate.subjectAlternativeNames() + << "Version=" << certificate.version() + << ", SerialNumber=" << certificate.serialNumber() + << ", Digest=" << certificate.digest().toBase64() + << ", Issuer=" << certificate.issuerDisplayName() + << ", Subject=" << certificate.subjectDisplayName() + << ", AlternativeSubjectNames=" << certificate.subjectAlternativeNames() #if QT_CONFIG(datestring) - << ", " << certificate.effectiveDate() - << ", " << certificate.expiryDate() + << ", EffectiveDate=" << certificate.effectiveDate() + << ", ExpiryDate=" << certificate.expiryDate() #endif << ')'; return debug; |