Move QSslCertificate's details and cert-related code to the plugins

Also since we have to properly support 'no-ssl' configure option
(alas, we support QSslCertificate on such builds) - introduce
a minimal crippled QTlsBackendCertOnly, which depends on
X509CertificateGeneric.

Pick-to: dev
Fixes: QTBUG-90954
Task-number: QTBUG-65922
Change-Id: Ib9d62903f16b7c0eaaa23e319a822c24a7631dc6
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Timur Pocheptsov 2021-02-22 13:45:07 +01:00
parent 0ea39a7c42
commit 41fc143635
20 changed files with 541 additions and 1727 deletions

View File

@ -47,6 +47,10 @@ qt_internal_add_module(Network
ssl/qsslcertificate.cpp ssl/qsslcertificate.h ssl/qsslcertificate_p.h ssl/qsslcertificate.cpp ssl/qsslcertificate.h ssl/qsslcertificate_p.h
ssl/qsslcertificateextension.cpp ssl/qsslcertificateextension.h ssl/qsslcertificateextension_p.h ssl/qsslcertificateextension.cpp ssl/qsslcertificateextension.h ssl/qsslcertificateextension_p.h
ssl/qtls_utils_p.h ssl/qtls_utils_p.h
ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
ssl/qtlsbackend_cert.cpp ssl/qtlsbackend_cert_p.h
ssl/qx509_base.cpp ssl/qx509_base_p.h
ssl/qx509_generic.cpp ssl/qx509_generic_p.h
DEFINES DEFINES
QT_NO_FOREACH QT_NO_FOREACH
QT_NO_USING_NAMESPACE QT_NO_USING_NAMESPACE
@ -310,11 +314,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_system_proxies
QT_USE_SYSTEM_PROXIES QT_USE_SYSTEM_PROXIES
) )
qt_internal_extend_target(Network CONDITION NOT QT_FEATURE_openssl
SOURCES
ssl/qsslcertificate_qt.cpp
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
SOURCES SOURCES
ssl/qocspresponse.cpp ssl/qocspresponse.h ssl/qocspresponse_p.h ssl/qocspresponse.cpp ssl/qocspresponse.h ssl/qocspresponse_p.h
@ -326,14 +325,11 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
ssl/qsslkey.h ssl/qsslkey_p.cpp ssl/qsslkey_p.h ssl/qsslkey.h ssl/qsslkey_p.cpp ssl/qsslkey_p.h
ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h
ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
ssl/qtlskey_base.cpp ssl/qtlskey_base_p.h ssl/qtlskey_base.cpp ssl/qtlskey_base_p.h
ssl/qx509_base.cpp ssl/qx509_base_p.h
) )
qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
SOURCES SOURCES
ssl/qsslcertificate_schannel.cpp
ssl/qssldiffiehellmanparameters_dummy.cpp ssl/qssldiffiehellmanparameters_dummy.cpp
ssl/qsslellipticcurve_dummy.cpp ssl/qsslellipticcurve_dummy.cpp
ssl/qsslsocket_qt.cpp ssl/qsslsocket_qt.cpp
@ -341,7 +337,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s
ssl/qtlsbackend_schannel_p.h ssl/qtlsbackend_schannel_p.h
ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
ssl/qtlskey_schannel.cpp ssl/qtlskey_schannel_p.h ssl/qtlskey_schannel.cpp ssl/qtlskey_schannel_p.h
ssl/qx509_generic.cpp ssl/qx509_generic_p.h
ssl/qx509_schannel.cpp ssl/qx509_schannel_p.h ssl/qx509_schannel.cpp ssl/qx509_schannel_p.h
LIBRARIES LIBRARIES
Crypt32 Crypt32
@ -360,7 +355,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FE
ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
ssl/qtlskey_st.cpp ssl/qtlskey_st_p.h ssl/qtlskey_st.cpp ssl/qtlskey_st_p.h
ssl/qtlsbackend_st.cpp ssl/qtlsbackend_st_p.h ssl/qtlsbackend_st.cpp ssl/qtlsbackend_st_p.h
ssl/qx509_generic.cpp ssl/qx509_generic_p.h
ssl/qx509_st.cpp ssl/qx509_st_p.h ssl/qx509_st.cpp ssl/qx509_st_p.h
) )
@ -371,7 +365,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl
qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ssl qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ssl
SOURCES SOURCES
ssl/qsslcertificate_openssl.cpp
ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h
ssl/qssldiffiehellmanparameters_openssl.cpp ssl/qssldiffiehellmanparameters_openssl.cpp
ssl/qsslellipticcurve_openssl.cpp ssl/qsslellipticcurve_openssl.cpp

View File

@ -47,6 +47,7 @@
#include "qsslsocket_openssl_p.h" #include "qsslsocket_openssl_p.h"
#include "qsslcertificate_p.h" #include "qsslcertificate_p.h"
#include "qdtls_openssl_p.h" #include "qdtls_openssl_p.h"
#include "qx509_openssl_p.h"
#include "qudpsocket.h" #include "qudpsocket.h"
#include "qssl_p.h" #include "qssl_p.h"
@ -258,7 +259,7 @@ extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx)
} }
auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic); auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
dtls->x509Errors.append(QSslErrorEntry::fromStoreContext(ctx)); dtls->x509Errors.append(QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
} }
// Always return 1 (OK) to allow verification to continue. We handle the // Always return 1 (OK) to allow verification to continue. We handle the
@ -1263,9 +1264,6 @@ unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned c
return pskLength; return pskLength;
} }
// The definition is located in qsslsocket_openssl.cpp.
QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert);
bool QDtlsPrivateOpenSSL::verifyPeer() bool QDtlsPrivateOpenSSL::verifyPeer()
{ {
// DTLSTODO: Windows-specific code for CA fetcher is not here yet. // DTLSTODO: Windows-specific code for CA fetcher is not here yet.
@ -1300,10 +1298,11 @@ bool QDtlsPrivateOpenSSL::verifyPeer()
} }
// Translate errors from the error list into QSslErrors // Translate errors from the error list into QSslErrors
using CertClass = QSsl::X509CertificateOpenSSL;
errors.reserve(errors.size() + opensslErrors.size()); errors.reserve(errors.size() + opensslErrors.size());
for (const auto &error : qAsConst(opensslErrors)) { for (const auto &error : qAsConst(opensslErrors)) {
errors << _q_OpenSSL_to_QSslError(error.code, const auto value = dtlsConfiguration.peerCertificateChain.value(error.depth);
dtlsConfiguration.peerCertificateChain.value(error.depth)); errors << CertClass::openSSLErrorToQSslError(error.code, value);
} }
tlsErrors = errors; tlsErrors = errors;
@ -1318,11 +1317,11 @@ void QDtlsPrivateOpenSSL::storePeerCertificates()
// peer certificate and the chain may be empty if the peer didn't present // peer certificate and the chain may be empty if the peer didn't present
// any certificate. // any certificate.
X509 *x509 = q_SSL_get_peer_certificate(dtls.tlsConnection.data()); X509 *x509 = q_SSL_get_peer_certificate(dtls.tlsConnection.data());
dtlsConfiguration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); dtlsConfiguration.peerCertificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509);
q_X509_free(x509); q_X509_free(x509);
if (dtlsConfiguration.peerCertificateChain.isEmpty()) { if (dtlsConfiguration.peerCertificateChain.isEmpty()) {
auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data()); auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data());
dtlsConfiguration.peerCertificateChain = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(stack); dtlsConfiguration.peerCertificateChain = QSsl::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(stack);
if (!dtlsConfiguration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode) if (!dtlsConfiguration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate); dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate);
} }

View File

@ -131,22 +131,18 @@
*/ */
#include <QtNetwork/qtnetworkglobal.h> #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) #if QT_CONFIG(regularexpression)
#include "qregularexpression.h" #include "qregularexpression.h"
#endif #endif
#include "qssl_p.h"
#include "qsslcertificate.h" #include "qsslcertificateextension_p.h"
#include "qsslcertificate_p.h" #include "qsslcertificate_p.h"
#include "qsslcertificate.h"
#include "qssl_p.h"
#ifndef QT_NO_SSL #ifndef QT_NO_SSL
#include "qsslsocket_p.h"
#include "qsslkey_p.h" #include "qsslkey_p.h"
#endif #endif
@ -156,6 +152,21 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
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 Constructs a QSslCertificate by reading \a format encoded data
from \a device and using the first certificate found. You can from \a device and using the first certificate found. You can
@ -165,13 +176,25 @@ QT_BEGIN_NAMESPACE
QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
: d(new QSslCertificatePrivate) : d(new QSslCertificatePrivate)
{ {
#ifndef QT_NO_OPENSSL if (device) {
QSslSocketPrivate::ensureInitialized(); const auto data = device->readAll();
if (device && QSslSocket::supportsSsl()) if (data.isEmpty())
#else return;
if (device)
#endif const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
d->init(device->readAll(), format); 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 +206,22 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format) QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
: d(new QSslCertificatePrivate) : d(new QSslCertificatePrivate)
{ {
#ifndef QT_NO_OPENSSL if (data.isEmpty())
QSslSocketPrivate::ensureInitialized(); return;
if (QSslSocket::supportsSsl())
#endif const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
d->init(data, format); 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;
} }
/*! /*!
@ -229,6 +263,20 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
returns \c false. 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 \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
@ -246,6 +294,13 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
\sa clear() \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 Returns \c true if this certificate is blacklisted; otherwise
@ -268,6 +323,13 @@ bool QSslCertificate::isBlacklisted() const
A certificate is considered self-signed its issuer and subject A certificate is considered self-signed its issuer and subject
are identical. 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 Clears the contents of this certificate, making it a null
@ -286,12 +348,26 @@ void QSslCertificate::clear()
\fn QByteArray QSslCertificate::version() const \fn QByteArray QSslCertificate::version() const
Returns the certificate's version string. 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 \fn QByteArray QSslCertificate::serialNumber() const
Returns the certificate's serial number string in hexadecimal format. 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, Returns a cryptographic digest of this certificate. By default,
@ -313,6 +389,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo() \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 \fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
@ -323,6 +406,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo() \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 \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
@ -333,6 +423,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa issuerInfo() \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 \fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
@ -343,6 +440,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa issuerInfo() \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 \fn QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
@ -356,6 +460,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo() \sa subjectInfo()
*/ */
QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
{
if (const auto *backend = d->backend.get())
return backend->subjectInfoAttributes();
return {};
}
/*! /*!
\fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const \fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
@ -369,6 +480,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo() \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 \fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
@ -385,6 +503,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo() \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 \fn QDateTime QSslCertificate::effectiveDate() const
@ -394,6 +519,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa expiryDate() \sa expiryDate()
*/ */
QDateTime QSslCertificate::effectiveDate() const
{
if (const auto *backend = d->backend.get())
return backend->effectiveDate();
return {};
}
/*! /*!
\fn QDateTime QSslCertificate::expiryDate() const \fn QDateTime QSslCertificate::expiryDate() const
@ -403,6 +535,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa effectiveDate() \sa effectiveDate()
*/ */
QDateTime QSslCertificate::expiryDate() const
{
if (const auto *backend = d->backend.get())
return backend->expiryDate();
return {};
}
/*! /*!
\fn Qt::HANDLE QSslCertificate::handle() const \fn Qt::HANDLE QSslCertificate::handle() const
@ -416,11 +555,29 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
non-portable, and its return value may vary from platform to non-portable, and its return value may vary from platform to
platform or change from minor release to minor release. 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 \fn QSslKey QSslCertificate::publicKey() const
Returns the certificate subject's public key. 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 \fn QList<QSslCertificateExtension> QSslCertificate::extensions() const
@ -428,6 +585,10 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
Returns a list containing the X509 extensions of this certificate. Returns a list containing the X509 extensions of this certificate.
\since 5.0 \since 5.0
*/ */
QList<QSslCertificateExtension> QSslCertificate::extensions() const
{
return d->extensions();
}
/*! /*!
\fn QByteArray QSslCertificate::toPem() const \fn QByteArray QSslCertificate::toPem() const
@ -435,6 +596,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
Returns this certificate converted to a PEM (Base64) encoded Returns this certificate converted to a PEM (Base64) encoded
representation. representation.
*/ */
QByteArray QSslCertificate::toPem() const
{
if (const auto *backend = d->backend.get())
return backend->toPem();
return {};
}
/*! /*!
\fn QByteArray QSslCertificate::toDer() const \fn QByteArray QSslCertificate::toDer() const
@ -442,6 +610,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
Returns this certificate converted to a DER (binary) encoded Returns this certificate converted to a DER (binary) encoded
representation. representation.
*/ */
QByteArray QSslCertificate::toDer() const
{
if (const auto *backend = d->backend.get())
return backend->toDer();
return {};
}
/*! /*!
\fn QString QSslCertificate::toText() const \fn QString QSslCertificate::toText() const
@ -451,6 +626,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\since 5.0 \since 5.0
*/ */
QString QSslCertificate::toText() const
{
if (const auto *backend = d->backend.get())
return backend->toText();
return {};
}
/*! /*!
\since 5.15 \since 5.15
@ -576,13 +758,22 @@ QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::Enco
*/ */
QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format) QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
{ {
return (format == QSsl::Pem) const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
? QSslCertificatePrivate::certificatesFromPem(data) if (!tlsBackend) {
: QSslCertificatePrivate::certificatesFromDer(data); 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 #ifndef QT_NO_SSL
/*! /*!
Verifies a certificate chain. The chain to be verified is passed in the Verifies a certificate chain. The chain to be verified is passed in the
\a certificateChain parameter. The first certificate in the list should \a certificateChain parameter. The first certificate in the list should
@ -597,13 +788,19 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E
\since 5.0 \since 5.0
*/ */
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName) 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 +820,43 @@ bool QSslCertificate::importPkcs12(QIODevice *device,
QList<QSslCertificate> *caCertificates, QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase) const QByteArray &passPhrase)
{ {
return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase); if (!device || !key || !certificate)
} return false;
#endif 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
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 // 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 // compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
@ -757,6 +987,16 @@ QString QSslCertificate::subjectDisplayName() const
return QString(); return QString();
} }
/*!
\internal
Returns X509 backend this QSslCertificate is using.
*/
QSsl::X509Certificate *QSslCertificate::backendImplementation() const
{
return d->backend.get();
}
/*! /*!
\fn size_t qHash(const QSslCertificate &key, size_t seed) \fn size_t qHash(const QSslCertificate &key, size_t seed)
@ -764,6 +1004,14 @@ QString QSslCertificate::subjectDisplayName() const
\since 5.4 \since 5.4
\relates QHash \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 #ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QSslCertificate &certificate) QDebug operator<<(QDebug debug, const QSslCertificate &certificate)

View File

@ -148,12 +148,7 @@ public:
const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem);
#ifndef QT_NO_SSL #ifndef QT_NO_SSL
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString()); static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString());
#else
static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName = QString());
#endif
static bool importPkcs12(QIODevice *device, static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert, QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates = nullptr, QList<QSslCertificate> *caCertificates = nullptr,
@ -163,10 +158,7 @@ public:
Qt::HANDLE handle() const; Qt::HANDLE handle() const;
private: private:
QSsl::X509Certificate *backendImplementation() const QSsl::X509Certificate *backendImplementation() const;
{
return nullptr; // TLSTODO
}
QExplicitlySharedDataPointer<QSslCertificatePrivate> d; QExplicitlySharedDataPointer<QSslCertificatePrivate> d;
friend class QSslCertificatePrivate; friend class QSslCertificatePrivate;
friend class QSslSocketBackendPrivate; friend class QSslSocketBackendPrivate;

View File

@ -1,799 +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 "qssl_p.h"
#include "qsslsocket_openssl_symbols_p.h"
#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>
QT_BEGIN_NAMESPACE
constexpr int MutexPoolSize = 17;
static QBasicMutex mutexPool[MutexPoolSize];
namespace QMutexPool {
static QBasicMutex *globalInstanceGet(const void *addr)
{
return mutexPool + (quintptr(addr) % MutexPoolSize);
}
}
// forward declaration
static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name);
bool QSslCertificate::operator==(const QSslCertificate &other) const
{
if (d == other.d)
return true;
if (d->null && other.d->null)
return true;
if (d->x509 && other.d->x509) {
const int ret = q_X509_cmp(d->x509, other.d->x509);
if (ret >= -1 && ret <= 1)
return ret == 0;
QSslSocketBackendPrivate::logAndClearErrorQueue();
}
return false;
}
size_t qHash(const QSslCertificate &key, size_t seed) noexcept
{
if (X509 * const x509 = key.d->x509) {
const EVP_MD *sha1 = q_EVP_sha1();
unsigned int len = 0;
unsigned char md[EVP_MAX_MD_SIZE];
q_X509_digest(x509, sha1, md, &len);
return qHashBits(md, len, seed);
}
return seed;
}
bool QSslCertificate::isNull() const
{
return d->null;
}
bool QSslCertificate::isSelfSigned() const
{
if (!d->x509)
return false;
return (q_X509_check_issued(d->x509, d->x509) == X509_V_OK);
}
QByteArray QSslCertificate::version() const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
if (d->versionString.isEmpty() && d->x509)
d->versionString = QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1);
return d->versionString;
}
QByteArray QSslCertificate::serialNumber() const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
if (d->serialNumberString.isEmpty() && d->x509) {
ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509);
QByteArray hexString;
hexString.reserve(serialNumber->length * 3);
for (int a = 0; a < serialNumber->length; ++a) {
hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0');
hexString += ':';
}
hexString.chop(1);
d->serialNumberString = hexString;
}
return d->serialNumberString;
}
QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
_q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
return d->issuerInfo.values(d->subjectInfoToString(info));
}
QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
_q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
return d->issuerInfo.values(attribute);
}
QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
_q_mapFromX509Name(q_X509_get_subject_name(d->x509));
return d->subjectInfo.values(d->subjectInfoToString(info));
}
QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
_q_mapFromX509Name(q_X509_get_subject_name(d->x509));
return d->subjectInfo.values(attribute);
}
QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
_q_mapFromX509Name(q_X509_get_subject_name(d->x509));
return d->subjectInfo.uniqueKeys();
}
QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
{
#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
#endif
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
_q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
return d->issuerInfo.uniqueKeys();
}
QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
{
QMultiMap<QSsl::AlternativeNameEntryType, QString> result;
if (!d->x509)
return result;
STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i(
d->x509, NID_subject_alt_name, nullptr, nullptr);
auto altName = [](ASN1_IA5STRING *ia5, int len) {
const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5));
return QString::fromLatin1(altNameStr, len);
};
if (altNames) {
for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD)
continue;
int len = q_ASN1_STRING_length(genName->d.ia5);
if (len < 0 || len >= 8192) {
// broken name
continue;
}
switch (genName->type) {
case GEN_DNS:
result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len));
break;
case GEN_EMAIL:
result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len));
break;
case GEN_IPADD: {
QHostAddress ipAddress;
switch (len) {
case 4: // IPv4
ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(genName->d.iPAddress->data)));
break;
case 16: // IPv6
ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data));
break;
default: // Unknown IP address format
break;
}
if (!ipAddress.isNull())
result.insert(QSsl::IpAddressEntry, ipAddress.toString());
break;
}
default:
break;
}
}
q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free));
}
return result;
}
QDateTime QSslCertificate::effectiveDate() const
{
return d->notValidBefore;
}
QDateTime QSslCertificate::expiryDate() const
{
return d->notValidAfter;
}
Qt::HANDLE QSslCertificate::handle() const
{
return Qt::HANDLE(d->x509);
}
QSslKey QSslCertificate::publicKey() const
{
if (!d->x509)
return QSslKey();
QSslKey key;
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) {
tlsKey->rsa = q_EVP_PKEY_get1_RSA(pkey);
tlsKey->keyAlgorithm = QSsl::Rsa;
tlsKey->keyIsNull = false;
} else if (keyType == EVP_PKEY_DSA) {
tlsKey->dsa = q_EVP_PKEY_get1_DSA(pkey);
tlsKey->keyAlgorithm = QSsl::Dsa;
tlsKey->keyIsNull = false;
#ifndef OPENSSL_NO_EC
} else if (keyType == EVP_PKEY_EC) {
tlsKey->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
tlsKey->keyAlgorithm = QSsl::Ec;
tlsKey->keyIsNull = false;
#endif
} else if (keyType == EVP_PKEY_DH) {
// DH unsupported
} else {
// error?
}
q_EVP_PKEY_free(pkey);
return key;
}
/*
* Convert unknown extensions to a QVariant.
*/
static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
{
// Get the extension specific method object if available
// we cast away the const-ness here because some versions of openssl
// don't use const for the parameters in the functions pointers stored
// in the object.
Q_ASSERT(ext);
X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
if (!meth) {
ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext);
Q_ASSERT(value);
QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(value)),
q_ASN1_STRING_length(value));
return result;
}
//const unsigned char *data = ext->value->data;
void *ext_internal = q_X509V3_EXT_d2i(ext);
// If this extension can be converted
if (meth->i2v && ext_internal) {
STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
QVariantMap map;
QVariantList list;
bool isMap = false;
for (int j = 0; j < q_SKM_sk_num(CONF_VALUE, val); j++) {
CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j);
if (nval->name && nval->value) {
isMap = true;
map[QString::fromUtf8(nval->name)] = QString::fromUtf8(nval->value);
} else if (nval->name) {
list << QString::fromUtf8(nval->name);
} else if (nval->value) {
list << QString::fromUtf8(nval->value);
}
}
if (isMap)
return map;
else
return list;
} else if (meth->i2s && ext_internal) {
QVariant result(QString::fromUtf8(meth->i2s(meth, ext_internal)));
return result;
} else if (meth->i2r && ext_internal) {
QByteArray result;
BIO *bio = q_BIO_new(q_BIO_s_mem());
if (!bio)
return result;
meth->i2r(meth, ext_internal, bio, 0);
char *bio_buffer;
long bio_size = q_BIO_get_mem_data(bio, &bio_buffer);
result = QByteArray(bio_buffer, bio_size);
q_BIO_free(bio);
return result;
}
return QVariant();
}
/*
* Convert extensions to a variant. The naming of the keys of the map are
* taken from RFC 5280, however we decided the capitalisation in the RFC
* was too silly for the real world.
*/
static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
{
ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
int nid = q_OBJ_obj2nid(obj);
switch (nid) {
case NID_basic_constraints:
{
BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext));
if (!basic)
return QVariant();
QVariantMap result;
result[QLatin1String("ca")] = basic->ca ? true : false;
if (basic->pathlen)
result[QLatin1String("pathLenConstraint")] = (qlonglong)q_ASN1_INTEGER_get(basic->pathlen);
q_BASIC_CONSTRAINTS_free(basic);
return result;
}
break;
case NID_info_access:
{
AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext));
if (!info)
return QVariant();
QVariantMap result;
for (int i=0; i < q_SKM_sk_num(ACCESS_DESCRIPTION, info); i++) {
ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i);
GENERAL_NAME *name = ad->location;
if (name->type == GEN_URI) {
int len = q_ASN1_STRING_length(name->d.uniformResourceIdentifier);
if (len < 0 || len >= 8192) {
// broken name
continue;
}
const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier));
const QString uri = QString::fromUtf8(uriStr, len);
result[QString::fromUtf8(QSslCertificatePrivate::asn1ObjectName(ad->method))] = uri;
} else {
qCWarning(lcSsl) << "Strange location type" << name->type;
}
}
q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast<void(*)(void *)>(q_OPENSSL_sk_free));
return result;
}
break;
case NID_subject_key_identifier:
{
void *ext_internal = q_X509V3_EXT_d2i(ext);
if (!ext_internal)
return QVariant();
// we cast away the const-ness here because some versions of openssl
// don't use const for the parameters in the functions pointers stored
// in the object.
X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
return QVariant(QString::fromUtf8(meth->i2s(meth, ext_internal)));
}
break;
case NID_authority_key_identifier:
{
AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(ext));
if (!auth_key)
return QVariant();
QVariantMap result;
// keyid
if (auth_key->keyid) {
QByteArray keyid(reinterpret_cast<const char *>(auth_key->keyid->data),
auth_key->keyid->length);
result[QLatin1String("keyid")] = keyid.toHex();
}
// issuer
// TODO: GENERAL_NAMES
// serial
if (auth_key->serial)
result[QLatin1String("serial")] = (qlonglong)q_ASN1_INTEGER_get(auth_key->serial);
q_AUTHORITY_KEYID_free(auth_key);
return result;
}
break;
}
return QVariant();
}
QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext)
{
Q_ASSERT(ext);
QSslCertificateExtension result;
ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
if (!obj) {
qCWarning(lcSsl, "Invalid (nullptr) ASN1_OBJECT");
return result;
}
QByteArray oid = QSslCertificatePrivate::asn1ObjectId(obj);
QByteArray name = QSslCertificatePrivate::asn1ObjectName(obj);
result.d->oid = QString::fromUtf8(oid);
result.d->name = QString::fromUtf8(name);
bool critical = q_X509_EXTENSION_get_critical(ext);
result.d->critical = critical;
// Lets see if we have custom support for this one
QVariant extensionValue = x509ExtensionToValue(ext);
if (extensionValue.isValid()) {
result.d->value = extensionValue;
result.d->supported = true;
return result;
}
extensionValue = x509UnknownExtensionToValue(ext);
if (extensionValue.isValid()) {
result.d->value = extensionValue;
result.d->supported = false;
return result;
}
return result;
}
QList<QSslCertificateExtension> QSslCertificate::extensions() const
{
QList<QSslCertificateExtension> result;
if (!d->x509)
return result;
int count = q_X509_get_ext_count(d->x509);
if (count <= 0)
return result;
result.reserve(count);
for (int i = 0; i < count; i++) {
X509_EXTENSION *ext = q_X509_get_ext(d->x509, i);
if (!ext) {
qCWarning(lcSsl) << "Invalid (nullptr) extension at index" << i;
continue;
}
result << QSslCertificatePrivate::convertExtension(ext);
}
// Converting an extension may result in an error(s), clean them up.
Q_UNUSED(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
return result;
}
QByteArray QSslCertificate::toPem() const
{
if (!d->x509)
return QByteArray();
return d->QByteArray_from_X509(d->x509, QSsl::Pem);
}
QByteArray QSslCertificate::toDer() const
{
if (!d->x509)
return QByteArray();
return d->QByteArray_from_X509(d->x509, QSsl::Der);
}
QString QSslCertificate::toText() const
{
if (!d->x509)
return QString();
return d->text_from_X509(d->x509);
}
#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
#define ENDCERTSTRING "-----END CERTIFICATE-----"
void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
{
if (!data.isEmpty()) {
const QList<QSslCertificate> certs = (format == QSsl::Pem)
? certificatesFromPem(data, 1)
: certificatesFromDer(data, 1);
if (!certs.isEmpty()) {
*this = *certs.first().d;
if (x509)
x509 = q_X509_dup(x509);
}
}
}
// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
{
if (!x509) {
qCWarning(lcSsl, "QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
return QByteArray();
}
// Use i2d_X509 to convert the X509 to an array.
int length = q_i2d_X509(x509, nullptr);
QByteArray array;
array.resize(length);
char *data = array.data();
char **dataP = &data;
unsigned char **dataPu = (unsigned char **)dataP;
if (q_i2d_X509(x509, dataPu) < 0)
return QByteArray();
if (format == QSsl::Der)
return array;
// Convert to Base64 - wrap at 64 characters.
array = array.toBase64();
QByteArray tmp;
for (int i = 0; i <= array.size() - 64; i += 64) {
tmp += QByteArray::fromRawData(array.data() + i, 64);
tmp += '\n';
}
if (int remainder = array.size() % 64) {
tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
tmp += '\n';
}
return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
}
QString QSslCertificatePrivate::text_from_X509(X509 *x509)
{
if (!x509) {
qCWarning(lcSsl, "QSslSocketBackendPrivate::text_from_X509: null X509");
return QString();
}
QByteArray result;
BIO *bio = q_BIO_new(q_BIO_s_mem());
if (!bio)
return QString();
q_X509_print(bio, x509);
QVarLengthArray<char, 16384> data;
int count = q_BIO_read(bio, data.data(), 16384);
if ( count > 0 ) {
result = QByteArray( data.data(), count );
}
q_BIO_free(bio);
return QString::fromLatin1(result);
}
QByteArray QSslCertificatePrivate::asn1ObjectId(ASN1_OBJECT *object)
{
char buf[80]; // The openssl docs a buffer length of 80 should be more than enough
q_OBJ_obj2txt(buf, sizeof(buf), object, 1); // the 1 says always use the oid not the long name
return QByteArray(buf);
}
QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object)
{
int nid = q_OBJ_obj2nid(object);
if (nid != NID_undef)
return QByteArray(q_OBJ_nid2sn(nid));
return asn1ObjectId(object);
}
static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name)
{
QMultiMap<QByteArray, QString> info;
for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) {
X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
QByteArray name = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e));
unsigned char *data = nullptr;
int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
info.insert(name, QString::fromUtf8((char*)data, size));
#if QT_CONFIG(opensslv11)
q_CRYPTO_free(data, nullptr, 0);
#else
q_CRYPTO_free(data);
#endif
}
return info;
}
QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
{
QSslCertificate certificate;
if (!x509 || !QSslSocket::supportsSsl())
return certificate;
ASN1_TIME *nbef = q_X509_getm_notBefore(x509);
ASN1_TIME *naft = q_X509_getm_notAfter(x509);
certificate.d->notValidBefore = q_getTimeFromASN1(nbef);
certificate.d->notValidAfter = q_getTimeFromASN1(naft);
certificate.d->null = false;
certificate.d->x509 = q_X509_dup(x509);
return certificate;
}
static bool matchLineFeed(const QByteArray &pem, int *offset)
{
char ch = 0;
// ignore extra whitespace at the end of the line
while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
++*offset;
if (ch == '\n') {
*offset += 1;
return true;
}
if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
*offset += 2;
return true;
}
return false;
}
QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
{
QList<QSslCertificate> certificates;
QSslSocketPrivate::ensureInitialized();
int offset = 0;
while (count == -1 || certificates.size() < count) {
int startPos = pem.indexOf(BEGINCERTSTRING, offset);
if (startPos == -1)
break;
startPos += sizeof(BEGINCERTSTRING) - 1;
if (!matchLineFeed(pem, &startPos))
break;
int endPos = pem.indexOf(ENDCERTSTRING, startPos);
if (endPos == -1)
break;
offset = endPos + sizeof(ENDCERTSTRING) - 1;
if (offset < pem.size() && !matchLineFeed(pem, &offset))
break;
QByteArray decoded = QByteArray::fromBase64(
QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
const unsigned char *data = (const unsigned char *)decoded.data();
if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) {
certificates << QSslCertificate_from_X509(x509);
q_X509_free(x509);
}
}
return certificates;
}
QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
{
QList<QSslCertificate> certificates;
QSslSocketPrivate::ensureInitialized();
const unsigned char *data = (const unsigned char *)der.data();
int size = der.size();
while (size > 0 && (count == -1 || certificates.size() < count)) {
if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) {
certificates << QSslCertificate_from_X509(x509);
q_X509_free(x509);
} else {
break;
}
size -= ((const char *)data - der.data());
}
return certificates;
}
QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtNetwork module of the Qt Toolkit. ** This file is part of the QtNetwork module of the Qt Toolkit.
@ -38,11 +38,8 @@
****************************************************************************/ ****************************************************************************/
#ifndef QSSLCERTIFICATE_OPENSSL_P_H #ifndef QSSLCERTIFICATE_P_H
#define QSSLCERTIFICATE_OPENSSL_P_H #define QSSLCERTIFICATE_P_H
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslcertificate.h"
// //
// W A R N I N G // W A R N I N G
@ -55,99 +52,34 @@
// We mean it. // We mean it.
// //
#ifndef QT_NO_SSL #include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_p.h"
#endif
#include "qsslcertificateextension.h" #include "qsslcertificateextension.h"
#include <QtCore/qdatetime.h> #include "qsslcertificate.h"
#include <QtCore/qmap.h> #include "qtlsbackend_p.h"
#ifndef QT_NO_OPENSSL #include <qlist.h>
#include <openssl/x509.h>
#else
struct X509;
struct X509_EXTENSION;
struct ASN1_OBJECT;
#endif
#if QT_CONFIG(schannel) #include <memory>
#include <wincrypt.h>
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// forward declaration
class QSslCertificatePrivate class QSslCertificatePrivate
{ {
public: public:
QSslCertificatePrivate() QSslCertificatePrivate();
: null(true), x509(nullptr) ~QSslCertificatePrivate();
{
#ifndef QT_NO_SSL
QSslSocketPrivate::ensureInitialized();
#endif
}
~QSslCertificatePrivate() QList<QSslCertificateExtension> extensions() const;
{
#ifndef QT_NO_OPENSSL
if (x509)
q_X509_free(x509);
#endif
#if QT_CONFIG(schannel)
if (certificateContext)
CertFreeCertificateContext(certificateContext);
#endif
}
bool null;
QByteArray versionString;
QByteArray serialNumberString;
QMultiMap<QByteArray, QString> issuerInfo;
QMultiMap<QByteArray, QString> subjectInfo;
QDateTime notValidAfter;
QDateTime notValidBefore;
#ifdef QT_NO_OPENSSL
bool subjectMatchesIssuer;
QSsl::KeyAlgorithm publicKeyAlgorithm;
QByteArray publicKeyDerData;
QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames;
QList<QSslCertificateExtension> extensions;
QByteArray derData;
bool parse(const QByteArray &data);
bool parseExtension(const QByteArray &data, QSslCertificateExtension *extension);
#endif
X509 *x509;
void init(const QByteArray &data, QSsl::EncodingFormat format);
static QByteArray asn1ObjectId(ASN1_OBJECT *object);
static QByteArray asn1ObjectName(ASN1_OBJECT *object);
static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format);
static QString text_from_X509(X509 *x509);
static QSslCertificate QSslCertificate_from_X509(X509 *x509);
static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count = -1);
static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count = -1);
static bool isBlacklisted(const QSslCertificate &certificate); static bool isBlacklisted(const QSslCertificate &certificate);
static QSslCertificateExtension convertExtension(X509_EXTENSION *ext);
static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info); static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
friend class QSslSocketBackendPrivate; friend class QSslSocketBackendPrivate;
QAtomicInt ref; QAtomicInt ref;
std::unique_ptr<QSsl::X509Certificate> backend;
#if QT_CONFIG(schannel)
const CERT_CONTEXT *certificateContext = nullptr;
static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
#endif
}; };
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QSSLCERTIFICATE_OPENSSL_P_H #endif // QSSLCERTIFICATE_P_H

View File

@ -1,580 +0,0 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#include "qsslcertificate.h"
#include "qsslcertificate_p.h"
#include "qssl_p.h"
#ifndef QT_NO_SSL
#include "qsslkey.h"
#include "qsslkey_p.h"
#include "qtlskey_generic_p.h"
#include "qtlsbackend_p.h"
#endif
#include "qsslcertificateextension.h"
#include "qsslcertificateextension_p.h"
#include "qasn1element_p.h"
#include <QtCore/qdatastream.h>
#include <QtCore/qendian.h>
#include <QtNetwork/qhostaddress.h>
QT_BEGIN_NAMESPACE
bool QSslCertificate::operator==(const QSslCertificate &other) const
{
if (d == other.d)
return true;
if (d->null && other.d->null)
return true;
return d->derData == other.d->derData;
}
size_t qHash(const QSslCertificate &key, size_t seed) noexcept
{
// DER is the native encoding here, so toDer() is just "return d->derData":
return qHash(key.toDer(), seed);
}
bool QSslCertificate::isNull() const
{
return d->null;
}
bool QSslCertificate::isSelfSigned() const
{
if (d->null)
return false;
qCWarning(lcSsl,
"QSslCertificate::isSelfSigned: This function does not check, whether the certificate "
"is actually signed. It just checks whether issuer and subject are identical");
return d->subjectMatchesIssuer;
}
QByteArray QSslCertificate::version() const
{
return d->versionString;
}
QByteArray QSslCertificate::serialNumber() const
{
return d->serialNumberString;
}
QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
{
return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info));
}
QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
{
return d->issuerInfo.values(attribute);
}
QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
{
return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info));
}
QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
{
return d->subjectInfo.values(attribute);
}
QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
{
return d->subjectInfo.uniqueKeys();
}
QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
{
return d->issuerInfo.uniqueKeys();
}
QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
{
return d->subjectAlternativeNames;
}
QDateTime QSslCertificate::effectiveDate() const
{
return d->notValidBefore;
}
QDateTime QSslCertificate::expiryDate() const
{
return d->notValidAfter;
}
#if !QT_CONFIG(schannel) // implemented in qsslcertificate_schannel.cpp
Qt::HANDLE QSslCertificate::handle() const
{
Q_UNIMPLEMENTED();
return nullptr;
}
#endif
#ifndef QT_NO_SSL
QSslKey QSslCertificate::publicKey() const
{
QSslKey key;
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
QList<QSslCertificateExtension> QSslCertificate::extensions() const
{
return d->extensions;
}
#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
#define ENDCERTSTRING "-----END CERTIFICATE-----"
QByteArray QSslCertificate::toPem() const
{
QByteArray array = toDer();
// Convert to Base64 - wrap at 64 characters.
array = array.toBase64();
QByteArray tmp;
for (int i = 0; i <= array.size() - 64; i += 64) {
tmp += QByteArray::fromRawData(array.data() + i, 64);
tmp += '\n';
}
if (int remainder = array.size() % 64) {
tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
tmp += '\n';
}
return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
}
QByteArray QSslCertificate::toDer() const
{
return d->derData;
}
QString QSslCertificate::toText() const
{
Q_UNIMPLEMENTED();
return QString();
}
void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
{
if (!data.isEmpty()) {
const QList<QSslCertificate> certs = (format == QSsl::Pem)
? certificatesFromPem(data, 1)
: certificatesFromDer(data, 1);
if (!certs.isEmpty()) {
*this = *certs.first().d;
#if QT_CONFIG(schannel)
if (certificateContext)
certificateContext = CertDuplicateCertificateContext(certificateContext);
#endif
}
}
}
static bool matchLineFeed(const QByteArray &pem, int *offset)
{
char ch = 0;
// ignore extra whitespace at the end of the line
while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
++*offset;
if (ch == '\n') {
*offset += 1;
return true;
}
if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
*offset += 2;
return true;
}
return false;
}
QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
{
QList<QSslCertificate> certificates;
int offset = 0;
while (count == -1 || certificates.size() < count) {
int startPos = pem.indexOf(BEGINCERTSTRING, offset);
if (startPos == -1)
break;
startPos += sizeof(BEGINCERTSTRING) - 1;
if (!matchLineFeed(pem, &startPos))
break;
int endPos = pem.indexOf(ENDCERTSTRING, startPos);
if (endPos == -1)
break;
offset = endPos + sizeof(ENDCERTSTRING) - 1;
if (offset < pem.size() && !matchLineFeed(pem, &offset))
break;
QByteArray decoded = QByteArray::fromBase64(
QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
certificates << certificatesFromDer(decoded, 1);;
}
return certificates;
}
QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
{
QList<QSslCertificate> certificates;
QByteArray data = der;
while (count == -1 || certificates.size() < count) {
QSslCertificate cert;
if (!cert.d->parse(data))
break;
certificates << cert;
data.remove(0, cert.d->derData.size());
}
return certificates;
}
static QByteArray colonSeparatedHex(const QByteArray &value)
{
const int size = value.size();
int i = 0;
while (i < size && !value.at(i)) // skip leading zeros
++i;
return value.mid(i).toHex(':');
}
bool QSslCertificatePrivate::parse(const QByteArray &data)
{
QAsn1Element root;
QDataStream dataStream(data);
if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType)
return false;
QDataStream rootStream(root.value());
QAsn1Element cert;
if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType)
return false;
// version or serial number
QAsn1Element elem;
QDataStream certStream(cert.value());
if (!elem.read(certStream))
return false;
if (elem.type() == QAsn1Element::Context0Type) {
QDataStream versionStream(elem.value());
if (!elem.read(versionStream)
|| elem.type() != QAsn1Element::IntegerType
|| elem.value().isEmpty())
return false;
versionString = QByteArray::number(elem.value().at(0) + 1);
if (!elem.read(certStream))
return false;
} else {
versionString = QByteArray::number(1);
}
// serial number
if (elem.type() != QAsn1Element::IntegerType)
return false;
serialNumberString = colonSeparatedHex(elem.value());
// algorithm ID
if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
return false;
// issuer info
if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
return false;
QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
issuerInfo = elem.toInfo();
// validity period
if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
return false;
QDataStream validityStream(elem.value());
if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
return false;
notValidBefore = elem.toDateTime();
if (!notValidBefore.isValid())
return false;
if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
return false;
notValidAfter = elem.toDateTime();
if (!notValidAfter.isValid())
return false;
// subject name
if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
return false;
QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
subjectInfo = elem.toInfo();
subjectMatchesIssuer = issuerDer == subjectDer;
// public key
qint64 keyStart = certStream.device()->pos();
if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
return false;
publicKeyDerData.resize(certStream.device()->pos() - keyStart);
QDataStream keyStream(elem.value());
if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
return false;
// key algorithm
if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType)
return false;
const QByteArray oid = elem.toObjectId();
if (oid == RSA_ENCRYPTION_OID)
publicKeyAlgorithm = QSsl::Rsa;
else if (oid == DSA_ENCRYPTION_OID)
publicKeyAlgorithm = QSsl::Dsa;
else if (oid == EC_ENCRYPTION_OID)
publicKeyAlgorithm = QSsl::Ec;
else
publicKeyAlgorithm = QSsl::Opaque;
certStream.device()->seek(keyStart);
certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size());
// extensions
while (elem.read(certStream)) {
if (elem.type() == QAsn1Element::Context3Type) {
if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) {
QDataStream extStream(elem.value());
while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) {
QSslCertificateExtension extension;
if (!parseExtension(elem.value(), &extension))
return false;
if (extension.oid() == QLatin1String("2.5.29.17")) {
// subjectAltName
// Note, parseExtension() returns true for this extensions,
// but considers it to be unsupported and assignes a useless
// value. OpenSSL also treats this extension as unsupported,
// but properly creates a map with 'name' and 'value' taken
// from the extension. We only support 'email', 'IP' and 'DNS',
// but this is what our subjectAlternativeNames map can contain
// anyway.
QVariantMap extValue;
QAsn1Element sanElem;
if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) {
QDataStream nameStream(sanElem.value());
QAsn1Element nameElem;
while (nameElem.read(nameStream)) {
switch (nameElem.type()) {
case QAsn1Element::Rfc822NameType:
subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString());
extValue[QStringLiteral("email")] = nameElem.toString();
break;
case QAsn1Element::DnsNameType:
subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString());
extValue[QStringLiteral("DNS")] = nameElem.toString();
break;
case QAsn1Element::IpAddressType: {
QHostAddress ipAddress;
QByteArray ipAddrValue = nameElem.value();
switch (ipAddrValue.length()) {
case 4: // IPv4
ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data())));
break;
case 16: // IPv6
ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data()));
break;
default: // Unknown IP address format
break;
}
if (!ipAddress.isNull()) {
subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString());
extValue[QStringLiteral("IP")] = ipAddress.toString();
}
break;
}
default:
break;
}
}
extension.d->value = extValue;
extension.d->supported = true;
}
}
extensions << extension;
}
}
}
}
derData = data.left(dataStream.device()->pos());
null = false;
return true;
}
bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension)
{
bool ok;
bool critical = false;
QAsn1Element oidElem, valElem;
QDataStream seqStream(data);
// oid
if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType)
return false;
const QByteArray oid = oidElem.toObjectId();
// critical and value
if (!valElem.read(seqStream))
return false;
if (valElem.type() == QAsn1Element::BooleanType) {
critical = valElem.toBool(&ok);
if (!ok || !valElem.read(seqStream))
return false;
}
if (valElem.type() != QAsn1Element::OctetStringType)
return false;
// interpret value
QAsn1Element val;
bool supported = true;
QVariant value;
if (oid == "1.3.6.1.5.5.7.1.1") {
// authorityInfoAccess
if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
return false;
QVariantMap result;
const auto elems = val.toList();
for (const QAsn1Element &el : elems) {
const auto items = el.toList();
if (items.size() != 2)
return false;
const QString key = QString::fromLatin1(items.at(0).toObjectName());
switch (items.at(1).type()) {
case QAsn1Element::Rfc822NameType:
case QAsn1Element::DnsNameType:
case QAsn1Element::UniformResourceIdentifierType:
result[key] = items.at(1).toString();
break;
}
}
value = result;
} else if (oid == "2.5.29.14") {
// subjectKeyIdentifier
if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType)
return false;
value = colonSeparatedHex(val.value()).toUpper();
} else if (oid == "2.5.29.19") {
// basicConstraints
if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
return false;
QVariantMap result;
const auto items = val.toList();
if (items.size() > 0) {
result[QStringLiteral("ca")] = items.at(0).toBool(&ok);
if (!ok)
return false;
} else {
result[QStringLiteral("ca")] = false;
}
if (items.size() > 1) {
result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok);
if (!ok)
return false;
}
value = result;
} else if (oid == "2.5.29.35") {
// authorityKeyIdentifier
if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
return false;
QVariantMap result;
const auto elems = val.toList();
for (const QAsn1Element &el : elems) {
if (el.type() == 0x80) {
const QString key = QStringLiteral("keyid");
result[key] = el.value().toHex();
} else if (el.type() == 0x82) {
const QString serial = QStringLiteral("serial");
result[serial] = colonSeparatedHex(el.value());
}
}
value = result;
} else {
supported = false;
value = valElem.value();
}
extension->d->critical = critical;
extension->d->supported = supported;
extension->d->oid = QString::fromLatin1(oid);
extension->d->name = QString::fromLatin1(oidElem.toObjectName());
extension->d->value = value;
return true;
}
QT_END_NAMESPACE

View File

@ -1382,7 +1382,7 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
// verify certificate chain // verify certificate chain
QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) { for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) {
QCFType<CFDataRef> certData = cert.d->derData.toCFData(); QCFType<CFDataRef> certData = cert.toDer().toCFData();
if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData)) if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData))
CFArrayAppendValue(certArray, secRef); CFArrayAppendValue(certArray, secRef);
else else

View File

@ -43,6 +43,7 @@
#include "qssl_p.h" #include "qssl_p.h"
#include "qsslsocket.h" #include "qsslsocket.h"
#include "qsslsocket_p.h"
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
# include "qsslsocket_openssl_p.h" # include "qsslsocket_openssl_p.h"

View File

@ -65,6 +65,7 @@
#include "qsslellipticcurve.h" #include "qsslellipticcurve.h"
#include "qsslpresharedkeyauthenticator.h" #include "qsslpresharedkeyauthenticator.h"
#include "qsslpresharedkeyauthenticator_p.h" #include "qsslpresharedkeyauthenticator_p.h"
#include "qtlsbackend_openssl_p.h"
#include "qocspresponse_p.h" #include "qocspresponse_p.h"
#include "qsslkey.h" #include "qsslkey.h"
#include "qtlsbackend_openssl_p.h" #include "qtlsbackend_openssl_p.h"
@ -463,14 +464,6 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER
return ciph; return ciph;
} }
QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx)
{
return {
q_X509_STORE_CTX_get_error(ctx),
q_X509_STORE_CTX_get_error_depth(ctx)
};
}
#if QT_CONFIG(ocsp) #if QT_CONFIG(ocsp)
QSslError::SslError qt_OCSP_response_status_to_SslError(long code) QSslError::SslError qt_OCSP_response_status_to_SslError(long code)
@ -612,7 +605,7 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx)
return 0; return 0;
} }
errors->append(QSslErrorEntry::fromStoreContext(ctx)); errors->append(QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
} }
// Always return OK to allow verification to continue. We handle the // Always return OK to allow verification to continue. We handle the
// errors gracefully after collecting all errors, after verification has // errors gracefully after collecting all errors, after verification has
@ -1334,55 +1327,6 @@ void QSslSocketBackendPrivate::transmit()
} while (ssl && transmitting); } while (ssl && transmitting);
} }
QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
{
QSslError error;
switch (errorCode) {
case X509_V_OK:
// X509_V_OK is also reported if the peer had no certificate.
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break;
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break;
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
error = QSslError(QSslError::CertificateSignatureFailed, cert); break;
case X509_V_ERR_CERT_NOT_YET_VALID:
error = QSslError(QSslError::CertificateNotYetValid, cert); break;
case X509_V_ERR_CERT_HAS_EXPIRED:
error = QSslError(QSslError::CertificateExpired, cert); break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
error = QSslError(QSslError::InvalidNotBeforeField, cert); break;
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
error = QSslError(QSslError::InvalidNotAfterField, cert); break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
error = QSslError(QSslError::SelfSignedCertificate, cert); break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break;
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break;
case X509_V_ERR_CERT_REVOKED:
error = QSslError(QSslError::CertificateRevoked, cert); break;
case X509_V_ERR_INVALID_CA:
error = QSslError(QSslError::InvalidCaCertificate, cert); break;
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
error = QSslError(QSslError::PathLengthExceeded, cert); break;
case X509_V_ERR_INVALID_PURPOSE:
error = QSslError(QSslError::InvalidPurpose, cert); break;
case X509_V_ERR_CERT_UNTRUSTED:
error = QSslError(QSslError::CertificateUntrusted, cert); break;
case X509_V_ERR_CERT_REJECTED:
error = QSslError(QSslError::CertificateRejected, cert); break;
default:
error = QSslError(QSslError::UnspecifiedError, cert); break;
}
return error;
}
QString QSslSocketBackendPrivate::msgErrorsDuringHandshake() QString QSslSocketBackendPrivate::msgErrorsDuringHandshake()
{ {
return QSslSocket::tr("Error during SSL handshake: %1") return QSslSocket::tr("Error during SSL handshake: %1")
@ -1422,7 +1366,7 @@ bool QSslSocketBackendPrivate::startHandshake()
if (!errorsReportedFromCallback) { if (!errorsReportedFromCallback) {
for (const auto &currentError : qAsConst(lastErrors)) { for (const auto &currentError : qAsConst(lastErrors)) {
emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code, emit q->peerVerifyError(QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code,
configuration.peerCertificateChain.value(currentError.depth))); configuration.peerCertificateChain.value(currentError.depth)));
if (q->state() != QAbstractSocket::ConnectedState) if (q->state() != QAbstractSocket::ConnectedState)
break; break;
@ -1538,7 +1482,7 @@ bool QSslSocketBackendPrivate::startHandshake()
// Translate errors from the error list into QSslErrors. // Translate errors from the error list into QSslErrors.
errors.reserve(errors.size() + errorList.size()); errors.reserve(errors.size() + errorList.size());
for (const auto &error : qAsConst(errorList)) for (const auto &error : qAsConst(errorList))
errors << _q_OpenSSL_to_QSslError(error.code, configuration.peerCertificateChain.value(error.depth)); errors << QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(error.code, configuration.peerCertificateChain.value(error.depth));
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
sslErrors = errors; sslErrors = errors;
@ -1589,10 +1533,10 @@ void QSslSocketBackendPrivate::storePeerCertificates()
// peer certificate and the chain may be empty if the peer didn't present // peer certificate and the chain may be empty if the peer didn't present
// any certificate. // any certificate.
X509 *x509 = q_SSL_get_peer_certificate(ssl); X509 *x509 = q_SSL_get_peer_certificate(ssl);
configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); configuration.peerCertificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509);
q_X509_free(x509); q_X509_free(x509);
if (configuration.peerCertificateChain.isEmpty()) { if (configuration.peerCertificateChain.isEmpty()) {
configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); configuration.peerCertificateChain = QSsl::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(q_SSL_get_peer_cert_chain(ssl));
if (!configuration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode) if (!configuration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
configuration.peerCertificateChain.prepend(configuration.peerCertificate); configuration.peerCertificateChain.prepend(configuration.peerCertificate);
} }
@ -1932,7 +1876,7 @@ bool QSslSocketBackendPrivate::checkOcspStatus()
matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer); matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer);
if (matchFound) { if (matchFound) {
if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) { if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) {
dResponse->signerCert = QSslCertificatePrivate::QSslCertificate_from_X509(issuer); dResponse->signerCert = QSsl::X509CertificateOpenSSL::certificateFromX509(issuer);
break; break;
} }
matchFound = false; matchFound = false;
@ -2033,10 +1977,10 @@ int QSslSocketBackendPrivate::emitErrorFromCallback(X509_STORE_CTX *ctx)
qCWarning(lcSsl, "Could not obtain the certificate (that failed to verify)"); qCWarning(lcSsl, "Could not obtain the certificate (that failed to verify)");
return 0; return 0;
} }
const QSslCertificate certificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
const auto errorAndDepth = QSslErrorEntry::fromStoreContext(ctx); const QSslCertificate certificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509);
const QSslError tlsError = _q_OpenSSL_to_QSslError(errorAndDepth.code, certificate); const auto errorAndDepth = QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx);
const QSslError tlsError = QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(errorAndDepth.code, certificate);
errorsReportedFromCallback = true; errorsReportedFromCallback = true;
handshakeInterrupted = true; handshakeInterrupted = true;
@ -2310,17 +2254,6 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
#endif #endif
} }
QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509)
{
ensureInitialized();
QList<QSslCertificate> certificates;
for (int i = 0; i < q_sk_X509_num(x509); ++i) {
if (X509 *entry = q_sk_X509_value(x509, i))
certificates << QSslCertificatePrivate::QSslCertificate_from_X509(entry);
}
return certificates;
}
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName) const QString &hostName)
{ {
@ -2334,79 +2267,6 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &
return QSsl::X509CertificateOpenSSL::verify(caCertificates, certificateChain, hostName); return QSsl::X509CertificateOpenSSL::verify(caCertificates, certificateChain, hostName);
} }
bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase)
{
if (!supportsSsl())
return false;
// These are required
Q_ASSERT(device);
Q_ASSERT(key);
Q_ASSERT(cert);
// Read the file into a BIO
QByteArray pkcs12data = device->readAll();
if (pkcs12data.size() == 0)
return false;
BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pkcs12data.constData()), pkcs12data.size());
// Create the PKCS#12 object
PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr);
if (!p12) {
qCWarning(lcSsl, "Unable to read PKCS#12 structure, %s",
q_ERR_error_string(q_ERR_get_error(), nullptr));
q_BIO_free(bio);
return false;
}
// Extract the data
EVP_PKEY *pkey = nullptr;
X509 *x509;
STACK_OF(X509) *ca = nullptr;
if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) {
qCWarning(lcSsl, "Unable to parse PKCS#12 structure, %s",
q_ERR_error_string(q_ERR_get_error(), nullptr));
q_PKCS12_free(p12);
q_BIO_free(bio);
return false;
}
// Convert to Qt types
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));
q_X509_free(x509);
q_EVP_PKEY_free(pkey);
q_PKCS12_free(p12);
q_BIO_free(bio);
return false;
}
*cert = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
if (caCertificates)
*caCertificates = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(ca);
// Clean up
q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
reinterpret_cast<void (*)(void *)>(q_X509_free));
q_X509_free(x509);
q_EVP_PKEY_free(pkey);
q_PKCS12_free(p12);
q_BIO_free(bio);
return true;
}
void QSslSocketPrivate::registerAdHocFactory() void QSslSocketPrivate::registerAdHocFactory()
{ {
// TLSTODO: this is a temporary solution, waiting for // TLSTODO: this is a temporary solution, waiting for

View File

@ -110,9 +110,8 @@ QT_BEGIN_NAMESPACE
struct QSslErrorEntry { struct QSslErrorEntry {
int code; int code;
int depth; int depth;
static QSslErrorEntry fromStoreContext(X509_STORE_CTX *ctx);
}; };
Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE);
class QSslSocketBackendPrivate : public QSslSocketPrivate class QSslSocketBackendPrivate : public QSslSocketPrivate
@ -179,16 +178,11 @@ public:
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher);
static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName); static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
static QList<QSslError> verify(const QList<QSslCertificate> &cas, const QList<QSslCertificate> &certificateChain, static QList<QSslError> verify(const QList<QSslCertificate> &cas, const QList<QSslCertificate> &certificateChain,
const QString &hostName); const QString &hostName);
static QString getErrorsFromOpenSsl(); static QString getErrorsFromOpenSsl();
static void logAndClearErrorQueue(); static void logAndClearErrorQueue();
static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase);
static QString msgErrorsDuringHandshake(); static QString msgErrorsDuringHandshake();
}; };

View File

@ -630,7 +630,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
PCCERT_CONTEXT pc = nullptr; PCCERT_CONTEXT pc = nullptr;
while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0, while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
CERT_FIND_ANY, nullptr, pc))) { CERT_FIND_ANY, nullptr, pc))) {
systemCerts.append(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc)); systemCerts.append(QSsl::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(pc));
} }
} }
return systemCerts; return systemCerts;
@ -1991,7 +1991,7 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
return QSslCertificate(); return QSslCertificate();
const CERT_CONTEXT *certContext = element->pCertContext; const CERT_CONTEXT *certContext = element->pCertContext;
return QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(certContext); return QSsl::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(certContext);
}; };
// Pick a chain to use as the certificate chain, if multiple are available: // Pick a chain to use as the certificate chain, if multiple are available:

View File

@ -38,7 +38,15 @@
****************************************************************************/ ****************************************************************************/
#include "qtlsbackend_p.h" #include "qtlsbackend_p.h"
#if QT_CONFIG(ssl)
#include "qsslsocket_p.h" #include "qsslsocket_p.h"
#include "qsslkey_p.h"
#include "qsslkey.h"
#else
#include "qtlsbackend_cert_p.h"
#endif
#include "qssl_p.h" #include "qssl_p.h"
#include <QtCore/private/qfactoryloader_p.h> #include <QtCore/private/qfactoryloader_p.h>
@ -93,8 +101,13 @@ public:
while (loader->instance(index)) while (loader->instance(index))
++index; ++index;
// TLSTODO: obviously, this one should go away: // TLSTODO: obviously, these two below should
// disappear as soon as plugins are in place.
#if QT_CONFIG(ssl)
QSslSocketPrivate::registerAdHocFactory(); QSslSocketPrivate::registerAdHocFactory();
#else
static QTlsBackendCertOnly certGenerator;
#endif // QT_CONFIG(ssl)
return loaded = true; return loaded = true;
} }
@ -180,6 +193,12 @@ QByteArray TlsKey::pemFooter() const
X509Certificate::~X509Certificate() = default; X509Certificate::~X509Certificate() = default;
TlsKey *X509Certificate::publicKey() const
{
// 'no-ssl' build has no key support either.
return nullptr;
}
} // namespace QSsl } // namespace QSsl
const QString QTlsBackend::builtinBackendNames[] = { const QString QTlsBackend::builtinBackendNames[] = {
@ -286,6 +305,9 @@ QString QTlsBackend::defaultBackendName()
if (names.contains(name)) if (names.contains(name))
return name; return name;
if (names.size())
return names[0];
return {}; return {};
} }
@ -301,6 +323,15 @@ QTlsBackend *QTlsBackend::findBackend(const QString &backendName)
return nullptr; return nullptr;
} }
QTlsBackend *QTlsBackend::activeOrAnyBackend()
{
#if QT_CONFIG(ssl)
return QSslSocketPrivate::tlsBackendInUse();
#else
return findBackend(defaultBackendName());
#endif // QT_CONFIG(ssl)
}
QList<QSsl::SslProtocol> QTlsBackend::supportedProtocols(const QString &backendName) QList<QSsl::SslProtocol> QTlsBackend::supportedProtocols(const QString &backendName)
{ {
if (!backends()) if (!backends())
@ -334,4 +365,14 @@ QList<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &bac
return {}; return {};
} }
void QTlsBackend::resetBackend(QSslKey &key, QSsl::TlsKey *keyBackend)
{
#if QT_CONFIG(ssl)
key.d->keyBackend.reset(keyBackend);
#else
Q_UNUSED(key);
Q_UNUSED(keyBackend);
#endif // QT_CONFIG(ssl)
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtlsbackend_cert_p.h"
#ifdef QT_NO_SSL
#include "qx509_generic_p.h"
#include <qssl.h>
#include <qlist.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.cert-only");
QString QTlsBackendCertOnly::backendName() const
{
return QStringLiteral("cert-only");
}
QList<QSsl::SslProtocol> QTlsBackendCertOnly::supportedProtocols() const
{
return {};
}
QList<QSsl::SupportedFeature> QTlsBackendCertOnly::supportedFeatures() const
{
return {};
}
QList<QSsl::ImplementedClass> QTlsBackendCertOnly::implementedClasses() const
{
QList<QSsl::ImplementedClass> classes;
classes << QSsl::ImplementedClass::Certificate;
return classes;
}
QSsl::X509Certificate *QTlsBackendCertOnly::createCertificate() const
{
return new QSsl::X509CertificateGeneric;
}
QSsl::X509PemReaderPtr QTlsBackendCertOnly::X509PemReader() const
{
return QSsl::X509CertificateGeneric::certificatesFromPem;
}
QSsl::X509DerReaderPtr QTlsBackendCertOnly::X509DerReader() const
{
return QSsl::X509CertificateGeneric::certificatesFromDer;
}
QT_END_NAMESPACE
#endif // QT_NO_SSL

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtNetwork module of the Qt Toolkit. ** This file is part of the QtNetwork module of the Qt Toolkit.
@ -37,26 +37,44 @@
** **
****************************************************************************/ ****************************************************************************/
#include "qsslcertificate.h" #ifndef QTLSBACKEND_CERT_P_H
#include "qsslcertificate_p.h" #define QTLSBACKEND_CERT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qtnetworkglobal_p.h>
#include "qtlsbackend_p.h"
#include <QtCore/qglobal.h>
#include <wincrypt.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext) class QTlsBackendCertOnly final : public QTlsBackend
{ {
QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded, public:
certificateContext->cbCertEncoded); private:
QString backendName() const override;
QSslCertificate certificate(derData, QSsl::Der); QList<QSsl::SslProtocol> supportedProtocols() const override;
certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext); QList<QSsl::SupportedFeature> supportedFeatures() const override;
return certificate; QList<QSsl::ImplementedClass> implementedClasses() const override;
}
Qt::HANDLE QSslCertificate::handle() const QSsl::X509Certificate *createCertificate() const override;
{ QSsl::X509PemReaderPtr X509PemReader() const override;
return Qt::HANDLE(d->certificateContext); QSsl::X509DerReaderPtr X509DerReader() const override;
} };
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QTLSBACKEND_CERT_P_H

View File

@ -51,10 +51,9 @@
// We mean it. // We mean it.
// //
#include <private/qtnetworkglobal_p.h> #include <QtNetwork/private/qtnetworkglobal_p.h>
#include <private/qsslkey_p.h> #include "qssl_p.h"
#include <private/qssl_p.h>
#include <QtNetwork/qsslcertificate.h> #include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslerror.h> #include <QtNetwork/qsslerror.h>
@ -76,6 +75,7 @@ QT_BEGIN_NAMESPACE
class QByteArray; class QByteArray;
class QIODevice; class QIODevice;
class QSslKey;
namespace QSsl { namespace QSsl {
@ -147,7 +147,8 @@ public:
virtual QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const = 0; virtual QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const = 0;
virtual QDateTime effectiveDate() const = 0; virtual QDateTime effectiveDate() const = 0;
virtual QDateTime expiryDate() const = 0; virtual QDateTime expiryDate() const = 0;
virtual TlsKey *publicKey() const = 0;
virtual TlsKey *publicKey() const;
// Extensions. Plugins do not expose internal representation // Extensions. Plugins do not expose internal representation
// and cannot rely on QSslCertificate's internals. // and cannot rely on QSslCertificate's internals.
@ -220,6 +221,7 @@ public:
static QList<QString> availableBackendNames(); static QList<QString> availableBackendNames();
static QString defaultBackendName(); static QString defaultBackendName();
static QTlsBackend *findBackend(const QString &backendName); static QTlsBackend *findBackend(const QString &backendName);
static QTlsBackend *activeOrAnyBackend();
static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName); static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName);
static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName); static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName);
@ -238,6 +240,8 @@ public:
return static_cast<DynamicType *>(o.backendImplementation()); return static_cast<DynamicType *>(o.backendImplementation());
} }
static void resetBackend(QSslKey &key, QSsl::TlsKey *keyBackend);
Q_DISABLE_COPY_MOVE(QTlsBackend) Q_DISABLE_COPY_MOVE(QTlsBackend)
}; };

View File

@ -101,28 +101,6 @@ public:
return pkcs8; 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: private:
QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase); QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);

View File

@ -37,7 +37,6 @@
** **
****************************************************************************/ ****************************************************************************/
#include "qtlskey_generic_p.h"
#include "qx509_generic_p.h" #include "qx509_generic_p.h"
#include "qasn1element_p.h" #include "qasn1element_p.h"

View File

@ -46,6 +46,14 @@ QT_BEGIN_NAMESPACE
namespace QSsl { namespace QSsl {
X509CertificateSchannel::X509CertificateSchannel() = default;
X509CertificateSchannel::~X509CertificateSchannel()
{
if (certificateContext)
CertFreeCertificateContext(certificateContext);
}
TlsKey *X509CertificateSchannel::publicKey() const TlsKey *X509CertificateSchannel::publicKey() const
{ {
auto key = std::make_unique<TlsKeySchannel>(PublicKey); auto key = std::make_unique<TlsKeySchannel>(PublicKey);
@ -55,6 +63,23 @@ TlsKey *X509CertificateSchannel::publicKey() const
return key.release(); return key.release();
} }
Qt::HANDLE X509CertificateSchannel::handle() const
{
return Qt::HANDLE(certificateContext);
}
QSslCertificate X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
{
QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
certificateContext->cbCertEncoded);
QSslCertificate certificate(derData, QSsl::Der);
auto *certBackend = QTlsBackend::backend<X509CertificateSchannel>(certificate);
Q_ASSERT(certBackend);
certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext);
return certificate;
}
} // namespace QSsl. } // namespace QSsl.
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -57,6 +57,9 @@
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include <QtCore/qt_windows.h>
#include <wincrypt.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace QSsl { namespace QSsl {
@ -64,7 +67,17 @@ namespace QSsl {
class X509CertificateSchannel final : public X509CertificateGeneric class X509CertificateSchannel final : public X509CertificateGeneric
{ {
public: public:
X509CertificateSchannel();
~X509CertificateSchannel();
TlsKey *publicKey() const override; TlsKey *publicKey() const override;
Qt::HANDLE handle() const override;
static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
private:
const CERT_CONTEXT *certificateContext = nullptr;
Q_DISABLE_COPY_MOVE(X509CertificateSchannel);
}; };
} // namespace QSsl. } // namespace QSsl.