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:
parent
0ea39a7c42
commit
41fc143635
@ -47,6 +47,10 @@ qt_internal_add_module(Network
|
||||
ssl/qsslcertificate.cpp ssl/qsslcertificate.h ssl/qsslcertificate_p.h
|
||||
ssl/qsslcertificateextension.cpp ssl/qsslcertificateextension.h ssl/qsslcertificateextension_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
|
||||
QT_NO_FOREACH
|
||||
QT_NO_USING_NAMESPACE
|
||||
@ -310,11 +314,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_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
|
||||
SOURCES
|
||||
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/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_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/qx509_base.cpp ssl/qx509_base_p.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
|
||||
SOURCES
|
||||
ssl/qsslcertificate_schannel.cpp
|
||||
ssl/qssldiffiehellmanparameters_dummy.cpp
|
||||
ssl/qsslellipticcurve_dummy.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/qtlskey_generic.cpp ssl/qtlskey_generic_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
|
||||
LIBRARIES
|
||||
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_st.cpp ssl/qtlskey_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
|
||||
)
|
||||
|
||||
@ -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
|
||||
SOURCES
|
||||
ssl/qsslcertificate_openssl.cpp
|
||||
ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h
|
||||
ssl/qssldiffiehellmanparameters_openssl.cpp
|
||||
ssl/qsslellipticcurve_openssl.cpp
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "qsslsocket_openssl_p.h"
|
||||
#include "qsslcertificate_p.h"
|
||||
#include "qdtls_openssl_p.h"
|
||||
#include "qx509_openssl_p.h"
|
||||
#include "qudpsocket.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);
|
||||
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
|
||||
@ -1263,9 +1264,6 @@ unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned c
|
||||
return pskLength;
|
||||
}
|
||||
|
||||
// The definition is located in qsslsocket_openssl.cpp.
|
||||
QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert);
|
||||
|
||||
bool QDtlsPrivateOpenSSL::verifyPeer()
|
||||
{
|
||||
// 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
|
||||
using CertClass = QSsl::X509CertificateOpenSSL;
|
||||
errors.reserve(errors.size() + opensslErrors.size());
|
||||
for (const auto &error : qAsConst(opensslErrors)) {
|
||||
errors << _q_OpenSSL_to_QSslError(error.code,
|
||||
dtlsConfiguration.peerCertificateChain.value(error.depth));
|
||||
const auto value = dtlsConfiguration.peerCertificateChain.value(error.depth);
|
||||
errors << CertClass::openSSLErrorToQSslError(error.code, value);
|
||||
}
|
||||
|
||||
tlsErrors = errors;
|
||||
@ -1318,11 +1317,11 @@ void QDtlsPrivateOpenSSL::storePeerCertificates()
|
||||
// peer certificate and the chain may be empty if the peer didn't present
|
||||
// any certificate.
|
||||
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);
|
||||
if (dtlsConfiguration.peerCertificateChain.isEmpty()) {
|
||||
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)
|
||||
dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate);
|
||||
}
|
||||
|
@ -131,22 +131,18 @@
|
||||
*/
|
||||
|
||||
#include <QtNetwork/qtnetworkglobal.h>
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include "qsslsocket_openssl_symbols_p.h"
|
||||
#endif
|
||||
#ifdef QT_SECURETRANSPORT
|
||||
#include "qsslsocket_mac_p.h"
|
||||
#endif
|
||||
#if QT_CONFIG(schannel)
|
||||
#include "qsslsocket_schannel_p.h"
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(regularexpression)
|
||||
#include "qregularexpression.h"
|
||||
#endif
|
||||
#include "qssl_p.h"
|
||||
#include "qsslcertificate.h"
|
||||
|
||||
#include "qsslcertificateextension_p.h"
|
||||
#include "qsslcertificate_p.h"
|
||||
#include "qsslcertificate.h"
|
||||
#include "qssl_p.h"
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
#include "qsslsocket_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
#endif
|
||||
|
||||
@ -156,6 +152,21 @@
|
||||
|
||||
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
|
||||
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)
|
||||
: d(new QSslCertificatePrivate)
|
||||
{
|
||||
#ifndef QT_NO_OPENSSL
|
||||
QSslSocketPrivate::ensureInitialized();
|
||||
if (device && QSslSocket::supportsSsl())
|
||||
#else
|
||||
if (device)
|
||||
#endif
|
||||
d->init(device->readAll(), format);
|
||||
if (device) {
|
||||
const auto data = device->readAll();
|
||||
if (data.isEmpty())
|
||||
return;
|
||||
|
||||
const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
|
||||
if (!tlsBackend)
|
||||
return;
|
||||
|
||||
auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader();
|
||||
if (!X509Reader) {
|
||||
qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER");
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QSslCertificate> certs = X509Reader(data, 1);
|
||||
if (!certs.isEmpty())
|
||||
d = certs.first().d;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -183,11 +206,22 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
|
||||
QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
|
||||
: d(new QSslCertificatePrivate)
|
||||
{
|
||||
#ifndef QT_NO_OPENSSL
|
||||
QSslSocketPrivate::ensureInitialized();
|
||||
if (QSslSocket::supportsSsl())
|
||||
#endif
|
||||
d->init(data, format);
|
||||
if (data.isEmpty())
|
||||
return;
|
||||
|
||||
const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
|
||||
if (!tlsBackend)
|
||||
return;
|
||||
|
||||
auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader();
|
||||
if (!X509Reader) {
|
||||
qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER");
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
bool QSslCertificate::operator==(const QSslCertificate &other) const
|
||||
{
|
||||
if (d == other.d)
|
||||
return true;
|
||||
|
||||
if (isNull() && other.isNull())
|
||||
return true;
|
||||
|
||||
if (d->backend.get() && other.d->backend.get())
|
||||
return d->backend->isEqual(*other.d->backend.get());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
|
||||
|
||||
@ -246,6 +294,13 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
|
||||
|
||||
\sa clear()
|
||||
*/
|
||||
bool QSslCertificate::isNull() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->isNull();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if this certificate is blacklisted; otherwise
|
||||
@ -268,6 +323,13 @@ bool QSslCertificate::isBlacklisted() const
|
||||
A certificate is considered self-signed its issuer and subject
|
||||
are identical.
|
||||
*/
|
||||
bool QSslCertificate::isSelfSigned() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->isSelfSigned();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Clears the contents of this certificate, making it a null
|
||||
@ -286,12 +348,26 @@ void QSslCertificate::clear()
|
||||
\fn QByteArray QSslCertificate::version() const
|
||||
Returns the certificate's version string.
|
||||
*/
|
||||
QByteArray QSslCertificate::version() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->version();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QByteArray QSslCertificate::serialNumber() const
|
||||
|
||||
Returns the certificate's serial number string in hexadecimal format.
|
||||
*/
|
||||
QByteArray QSslCertificate::serialNumber() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->serialNumber();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a cryptographic digest of this certificate. By default,
|
||||
@ -313,6 +389,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa subjectInfo()
|
||||
*/
|
||||
QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->issuerInfo(info);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
|
||||
@ -323,6 +406,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa subjectInfo()
|
||||
*/
|
||||
QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->issuerInfo(attribute);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
|
||||
@ -333,6 +423,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa issuerInfo()
|
||||
*/
|
||||
QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->subjectInfo(info);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
|
||||
@ -343,6 +440,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa issuerInfo()
|
||||
*/
|
||||
QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->subjectInfo(attribute);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
|
||||
@ -356,6 +460,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa subjectInfo()
|
||||
*/
|
||||
QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->subjectInfoAttributes();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
|
||||
@ -369,6 +480,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa subjectInfo()
|
||||
*/
|
||||
QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->issuerInfoAttributes();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
|
||||
@ -385,6 +503,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa subjectInfo()
|
||||
*/
|
||||
QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->subjectAlternativeNames();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QDateTime QSslCertificate::effectiveDate() const
|
||||
@ -394,6 +519,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa expiryDate()
|
||||
*/
|
||||
QDateTime QSslCertificate::effectiveDate() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->effectiveDate();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QDateTime QSslCertificate::expiryDate() const
|
||||
@ -403,6 +535,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\sa effectiveDate()
|
||||
*/
|
||||
QDateTime QSslCertificate::expiryDate() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->expiryDate();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn Qt::HANDLE QSslCertificate::handle() const
|
||||
@ -416,11 +555,29 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
non-portable, and its return value may vary from platform to
|
||||
platform or change from minor release to minor release.
|
||||
*/
|
||||
Qt::HANDLE QSslCertificate::handle() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->handle();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
/*!
|
||||
\fn QSslKey QSslCertificate::publicKey() const
|
||||
Returns the certificate subject's public key.
|
||||
*/
|
||||
QSslKey QSslCertificate::publicKey() const
|
||||
{
|
||||
QSslKey key;
|
||||
if (const auto *backend = d->backend.get())
|
||||
QTlsBackend::resetBackend(key, backend->publicKey());
|
||||
|
||||
return key;
|
||||
}
|
||||
#endif // QT_NO_SSL
|
||||
|
||||
|
||||
/*!
|
||||
\fn QList<QSslCertificateExtension> QSslCertificate::extensions() const
|
||||
@ -428,6 +585,10 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
Returns a list containing the X509 extensions of this certificate.
|
||||
\since 5.0
|
||||
*/
|
||||
QList<QSslCertificateExtension> QSslCertificate::extensions() const
|
||||
{
|
||||
return d->extensions();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QByteArray QSslCertificate::toPem() const
|
||||
@ -435,6 +596,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
Returns this certificate converted to a PEM (Base64) encoded
|
||||
representation.
|
||||
*/
|
||||
QByteArray QSslCertificate::toPem() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->toPem();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QByteArray QSslCertificate::toDer() const
|
||||
@ -442,6 +610,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
Returns this certificate converted to a DER (binary) encoded
|
||||
representation.
|
||||
*/
|
||||
QByteArray QSslCertificate::toDer() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->toDer();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QSslCertificate::toText() const
|
||||
@ -451,6 +626,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
|
||||
|
||||
\since 5.0
|
||||
*/
|
||||
QString QSslCertificate::toText() const
|
||||
{
|
||||
if (const auto *backend = d->backend.get())
|
||||
return backend->toText();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.15
|
||||
@ -576,13 +758,22 @@ QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::Enco
|
||||
*/
|
||||
QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
|
||||
{
|
||||
return (format == QSsl::Pem)
|
||||
? QSslCertificatePrivate::certificatesFromPem(data)
|
||||
: QSslCertificatePrivate::certificatesFromDer(data);
|
||||
const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
|
||||
if (!tlsBackend) {
|
||||
qCWarning(lcSsl, "No TLS backend is available");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader();
|
||||
if (!reader) {
|
||||
qCWarning(lcSsl, "The available TLS backend does not support reading PEM/DER");
|
||||
return {};
|
||||
}
|
||||
|
||||
return reader(data, -1);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
|
||||
/*!
|
||||
Verifies a certificate chain. The chain to be verified is passed in the
|
||||
\a certificateChain parameter. The first certificate in the list should
|
||||
@ -597,13 +788,19 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E
|
||||
|
||||
\since 5.0
|
||||
*/
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
|
||||
QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
|
||||
#else
|
||||
QList<QSslError> QSslCertificate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
|
||||
#endif
|
||||
{
|
||||
return QSslSocketBackendPrivate::verify(certificateChain, hostName);
|
||||
const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
|
||||
if (!tlsBackend) {
|
||||
qCWarning(lcSsl, "No TLS backend is available");
|
||||
return {};
|
||||
}
|
||||
auto verifyPtr = tlsBackend->X509Verifier();
|
||||
if (!verifyPtr) {
|
||||
qCWarning(lcSsl, "Available TLS backend does not support manual certificate verification");
|
||||
return {};
|
||||
}
|
||||
return verifyPtr(certificateChain, hostName);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -623,10 +820,43 @@ bool QSslCertificate::importPkcs12(QIODevice *device,
|
||||
QList<QSslCertificate> *caCertificates,
|
||||
const QByteArray &passPhrase)
|
||||
{
|
||||
return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase);
|
||||
}
|
||||
if (!device || !key || !certificate)
|
||||
return false;
|
||||
|
||||
#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
|
||||
// compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
|
||||
@ -757,6 +987,16 @@ QString QSslCertificate::subjectDisplayName() const
|
||||
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)
|
||||
|
||||
@ -764,6 +1004,14 @@ QString QSslCertificate::subjectDisplayName() const
|
||||
\since 5.4
|
||||
\relates QHash
|
||||
*/
|
||||
size_t qHash(const QSslCertificate &key, size_t seed) noexcept
|
||||
{
|
||||
if (const auto *backend = key.d->backend.get())
|
||||
return backend->hash(seed);
|
||||
|
||||
return seed;
|
||||
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
|
||||
|
@ -148,12 +148,7 @@ public:
|
||||
const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem);
|
||||
|
||||
#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());
|
||||
#else
|
||||
static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName = QString());
|
||||
#endif
|
||||
|
||||
static bool importPkcs12(QIODevice *device,
|
||||
QSslKey *key, QSslCertificate *cert,
|
||||
QList<QSslCertificate> *caCertificates = nullptr,
|
||||
@ -163,10 +158,7 @@ public:
|
||||
Qt::HANDLE handle() const;
|
||||
|
||||
private:
|
||||
QSsl::X509Certificate *backendImplementation() const
|
||||
{
|
||||
return nullptr; // TLSTODO
|
||||
}
|
||||
QSsl::X509Certificate *backendImplementation() const;
|
||||
QExplicitlySharedDataPointer<QSslCertificatePrivate> d;
|
||||
friend class QSslCertificatePrivate;
|
||||
friend class QSslSocketBackendPrivate;
|
||||
|
@ -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
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** 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.
|
||||
@ -38,11 +38,8 @@
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QSSLCERTIFICATE_OPENSSL_P_H
|
||||
#define QSSLCERTIFICATE_OPENSSL_P_H
|
||||
|
||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||
#include "qsslcertificate.h"
|
||||
#ifndef QSSLCERTIFICATE_P_H
|
||||
#define QSSLCERTIFICATE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
@ -55,99 +52,34 @@
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
#include "qsslsocket_p.h"
|
||||
#endif
|
||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||
|
||||
#include "qsslcertificateextension.h"
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qmap.h>
|
||||
#include "qsslcertificate.h"
|
||||
#include "qtlsbackend_p.h"
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include <openssl/x509.h>
|
||||
#else
|
||||
struct X509;
|
||||
struct X509_EXTENSION;
|
||||
struct ASN1_OBJECT;
|
||||
#endif
|
||||
#include <qlist.h>
|
||||
|
||||
#if QT_CONFIG(schannel)
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// forward declaration
|
||||
|
||||
class QSslCertificatePrivate
|
||||
{
|
||||
public:
|
||||
QSslCertificatePrivate()
|
||||
: null(true), x509(nullptr)
|
||||
{
|
||||
#ifndef QT_NO_SSL
|
||||
QSslSocketPrivate::ensureInitialized();
|
||||
#endif
|
||||
}
|
||||
QSslCertificatePrivate();
|
||||
~QSslCertificatePrivate();
|
||||
|
||||
~QSslCertificatePrivate()
|
||||
{
|
||||
#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);
|
||||
QList<QSslCertificateExtension> extensions() const;
|
||||
static bool isBlacklisted(const QSslCertificate &certificate);
|
||||
static QSslCertificateExtension convertExtension(X509_EXTENSION *ext);
|
||||
static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
|
||||
|
||||
friend class QSslSocketBackendPrivate;
|
||||
|
||||
QAtomicInt ref;
|
||||
|
||||
#if QT_CONFIG(schannel)
|
||||
const CERT_CONTEXT *certificateContext = nullptr;
|
||||
|
||||
static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
|
||||
#endif
|
||||
std::unique_ptr<QSsl::X509Certificate> backend;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSSLCERTIFICATE_OPENSSL_P_H
|
||||
#endif // QSSLCERTIFICATE_P_H
|
||||
|
@ -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
|
@ -1382,7 +1382,7 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
|
||||
// verify certificate chain
|
||||
QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
|
||||
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))
|
||||
CFArrayAppendValue(certArray, secRef);
|
||||
else
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#include "qssl_p.h"
|
||||
#include "qsslsocket.h"
|
||||
#include "qsslsocket_p.h"
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
# include "qsslsocket_openssl_p.h"
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include "qsslellipticcurve.h"
|
||||
#include "qsslpresharedkeyauthenticator.h"
|
||||
#include "qsslpresharedkeyauthenticator_p.h"
|
||||
#include "qtlsbackend_openssl_p.h"
|
||||
#include "qocspresponse_p.h"
|
||||
#include "qsslkey.h"
|
||||
#include "qtlsbackend_openssl_p.h"
|
||||
@ -463,14 +464,6 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER
|
||||
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)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
errors->append(QSslErrorEntry::fromStoreContext(ctx));
|
||||
errors->append(QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
|
||||
}
|
||||
// Always return OK to allow verification to continue. We handle the
|
||||
// errors gracefully after collecting all errors, after verification has
|
||||
@ -1334,55 +1327,6 @@ void QSslSocketBackendPrivate::transmit()
|
||||
} 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()
|
||||
{
|
||||
return QSslSocket::tr("Error during SSL handshake: %1")
|
||||
@ -1422,7 +1366,7 @@ bool QSslSocketBackendPrivate::startHandshake()
|
||||
|
||||
if (!errorsReportedFromCallback) {
|
||||
for (const auto ¤tError : qAsConst(lastErrors)) {
|
||||
emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code,
|
||||
emit q->peerVerifyError(QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code,
|
||||
configuration.peerCertificateChain.value(currentError.depth)));
|
||||
if (q->state() != QAbstractSocket::ConnectedState)
|
||||
break;
|
||||
@ -1538,7 +1482,7 @@ bool QSslSocketBackendPrivate::startHandshake()
|
||||
// Translate errors from the error list into QSslErrors.
|
||||
errors.reserve(errors.size() + errorList.size());
|
||||
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()) {
|
||||
sslErrors = errors;
|
||||
@ -1589,10 +1533,10 @@ void QSslSocketBackendPrivate::storePeerCertificates()
|
||||
// peer certificate and the chain may be empty if the peer didn't present
|
||||
// any certificate.
|
||||
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);
|
||||
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)
|
||||
configuration.peerCertificateChain.prepend(configuration.peerCertificate);
|
||||
}
|
||||
@ -1932,7 +1876,7 @@ bool QSslSocketBackendPrivate::checkOcspStatus()
|
||||
matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer);
|
||||
if (matchFound) {
|
||||
if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) {
|
||||
dResponse->signerCert = QSslCertificatePrivate::QSslCertificate_from_X509(issuer);
|
||||
dResponse->signerCert = QSsl::X509CertificateOpenSSL::certificateFromX509(issuer);
|
||||
break;
|
||||
}
|
||||
matchFound = false;
|
||||
@ -2033,10 +1977,10 @@ int QSslSocketBackendPrivate::emitErrorFromCallback(X509_STORE_CTX *ctx)
|
||||
qCWarning(lcSsl, "Could not obtain the certificate (that failed to verify)");
|
||||
return 0;
|
||||
}
|
||||
const QSslCertificate certificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
|
||||
|
||||
const auto errorAndDepth = QSslErrorEntry::fromStoreContext(ctx);
|
||||
const QSslError tlsError = _q_OpenSSL_to_QSslError(errorAndDepth.code, certificate);
|
||||
const QSslCertificate certificate = QSsl::X509CertificateOpenSSL::certificateFromX509(x509);
|
||||
const auto errorAndDepth = QSsl::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx);
|
||||
const QSslError tlsError = QSsl::X509CertificateOpenSSL::openSSLErrorToQSslError(errorAndDepth.code, certificate);
|
||||
|
||||
errorsReportedFromCallback = true;
|
||||
handshakeInterrupted = true;
|
||||
@ -2310,17 +2254,6 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
|
||||
#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,
|
||||
const QString &hostName)
|
||||
{
|
||||
@ -2334,79 +2267,6 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &
|
||||
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()
|
||||
{
|
||||
// TLSTODO: this is a temporary solution, waiting for
|
||||
|
@ -110,9 +110,8 @@ QT_BEGIN_NAMESPACE
|
||||
struct QSslErrorEntry {
|
||||
int code;
|
||||
int depth;
|
||||
|
||||
static QSslErrorEntry fromStoreContext(X509_STORE_CTX *ctx);
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE);
|
||||
|
||||
class QSslSocketBackendPrivate : public QSslSocketPrivate
|
||||
@ -179,16 +178,11 @@ public:
|
||||
|
||||
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
|
||||
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> &cas, const QList<QSslCertificate> &certificateChain,
|
||||
const QString &hostName);
|
||||
static QString getErrorsFromOpenSsl();
|
||||
static void logAndClearErrorQueue();
|
||||
static bool importPkcs12(QIODevice *device,
|
||||
QSslKey *key, QSslCertificate *cert,
|
||||
QList<QSslCertificate> *caCertificates,
|
||||
const QByteArray &passPhrase);
|
||||
static QString msgErrorsDuringHandshake();
|
||||
};
|
||||
|
||||
|
@ -630,7 +630,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
PCCERT_CONTEXT pc = nullptr;
|
||||
while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
|
||||
CERT_FIND_ANY, nullptr, pc))) {
|
||||
systemCerts.append(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc));
|
||||
systemCerts.append(QSsl::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(pc));
|
||||
}
|
||||
}
|
||||
return systemCerts;
|
||||
@ -1991,7 +1991,7 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
|
||||
return QSslCertificate();
|
||||
|
||||
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:
|
||||
|
@ -38,7 +38,15 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtlsbackend_p.h"
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
#include "qsslsocket_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
#include "qsslkey.h"
|
||||
#else
|
||||
#include "qtlsbackend_cert_p.h"
|
||||
#endif
|
||||
|
||||
#include "qssl_p.h"
|
||||
|
||||
#include <QtCore/private/qfactoryloader_p.h>
|
||||
@ -93,8 +101,13 @@ public:
|
||||
while (loader->instance(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();
|
||||
#else
|
||||
static QTlsBackendCertOnly certGenerator;
|
||||
#endif // QT_CONFIG(ssl)
|
||||
|
||||
return loaded = true;
|
||||
}
|
||||
@ -180,6 +193,12 @@ QByteArray TlsKey::pemFooter() const
|
||||
|
||||
X509Certificate::~X509Certificate() = default;
|
||||
|
||||
TlsKey *X509Certificate::publicKey() const
|
||||
{
|
||||
// 'no-ssl' build has no key support either.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace QSsl
|
||||
|
||||
const QString QTlsBackend::builtinBackendNames[] = {
|
||||
@ -286,6 +305,9 @@ QString QTlsBackend::defaultBackendName()
|
||||
if (names.contains(name))
|
||||
return name;
|
||||
|
||||
if (names.size())
|
||||
return names[0];
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -301,6 +323,15 @@ QTlsBackend *QTlsBackend::findBackend(const QString &backendName)
|
||||
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)
|
||||
{
|
||||
if (!backends())
|
||||
@ -334,4 +365,14 @@ QList<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &bac
|
||||
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
|
||||
|
96
src/network/ssl/qtlsbackend_cert.cpp
Normal file
96
src/network/ssl/qtlsbackend_cert.cpp
Normal 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
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** 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.
|
||||
@ -37,26 +37,44 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qsslcertificate.h"
|
||||
#include "qsslcertificate_p.h"
|
||||
#ifndef QTLSBACKEND_CERT_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
|
||||
|
||||
QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
|
||||
class QTlsBackendCertOnly final : public QTlsBackend
|
||||
{
|
||||
QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
|
||||
certificateContext->cbCertEncoded);
|
||||
public:
|
||||
private:
|
||||
QString backendName() const override;
|
||||
|
||||
QSslCertificate certificate(derData, QSsl::Der);
|
||||
certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext);
|
||||
return certificate;
|
||||
}
|
||||
QList<QSsl::SslProtocol> supportedProtocols() const override;
|
||||
QList<QSsl::SupportedFeature> supportedFeatures() const override;
|
||||
QList<QSsl::ImplementedClass> implementedClasses() const override;
|
||||
|
||||
Qt::HANDLE QSslCertificate::handle() const
|
||||
{
|
||||
return Qt::HANDLE(d->certificateContext);
|
||||
}
|
||||
QSsl::X509Certificate *createCertificate() const override;
|
||||
QSsl::X509PemReaderPtr X509PemReader() const override;
|
||||
QSsl::X509DerReaderPtr X509DerReader() const override;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTLSBACKEND_CERT_P_H
|
@ -51,10 +51,9 @@
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <private/qsslkey_p.h>
|
||||
#include <private/qssl_p.h>
|
||||
#include "qssl_p.h"
|
||||
|
||||
#include <QtNetwork/qsslcertificate.h>
|
||||
#include <QtNetwork/qsslerror.h>
|
||||
@ -76,6 +75,7 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
class QByteArray;
|
||||
class QIODevice;
|
||||
class QSslKey;
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
@ -147,7 +147,8 @@ public:
|
||||
virtual QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const = 0;
|
||||
virtual QDateTime effectiveDate() const = 0;
|
||||
virtual QDateTime expiryDate() const = 0;
|
||||
virtual TlsKey *publicKey() const = 0;
|
||||
|
||||
virtual TlsKey *publicKey() const;
|
||||
|
||||
// Extensions. Plugins do not expose internal representation
|
||||
// and cannot rely on QSslCertificate's internals.
|
||||
@ -220,6 +221,7 @@ public:
|
||||
static QList<QString> availableBackendNames();
|
||||
static QString defaultBackendName();
|
||||
static QTlsBackend *findBackend(const QString &backendName);
|
||||
static QTlsBackend *activeOrAnyBackend();
|
||||
|
||||
static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName);
|
||||
static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName);
|
||||
@ -238,6 +240,8 @@ public:
|
||||
return static_cast<DynamicType *>(o.backendImplementation());
|
||||
}
|
||||
|
||||
static void resetBackend(QSslKey &key, QSsl::TlsKey *keyBackend);
|
||||
|
||||
Q_DISABLE_COPY_MOVE(QTlsBackend)
|
||||
};
|
||||
|
||||
|
@ -101,28 +101,6 @@ public:
|
||||
return pkcs8;
|
||||
}
|
||||
|
||||
QByteArray decrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const override
|
||||
{
|
||||
// The real implementation is to be provided by Schannel or SecureTransport.
|
||||
Q_UNUSED(cipher)
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(key)
|
||||
Q_UNUSED(iv)
|
||||
|
||||
return {};
|
||||
}
|
||||
QByteArray encrypt(Cipher cipher, const QByteArray &data,
|
||||
const QByteArray &key, const QByteArray &iv) const override
|
||||
{
|
||||
// The real implementation is to be provided by Schannel or SecureTransport.
|
||||
Q_UNUSED(cipher)
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(key)
|
||||
Q_UNUSED(iv)
|
||||
|
||||
return {};
|
||||
}
|
||||
private:
|
||||
QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtlskey_generic_p.h"
|
||||
#include "qx509_generic_p.h"
|
||||
#include "qasn1element_p.h"
|
||||
|
||||
|
@ -46,6 +46,14 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
|
||||
X509CertificateSchannel::X509CertificateSchannel() = default;
|
||||
|
||||
X509CertificateSchannel::~X509CertificateSchannel()
|
||||
{
|
||||
if (certificateContext)
|
||||
CertFreeCertificateContext(certificateContext);
|
||||
}
|
||||
|
||||
TlsKey *X509CertificateSchannel::publicKey() const
|
||||
{
|
||||
auto key = std::make_unique<TlsKeySchannel>(PublicKey);
|
||||
@ -55,6 +63,23 @@ TlsKey *X509CertificateSchannel::publicKey() const
|
||||
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.
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -57,6 +57,9 @@
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QSsl {
|
||||
@ -64,7 +67,17 @@ namespace QSsl {
|
||||
class X509CertificateSchannel final : public X509CertificateGeneric
|
||||
{
|
||||
public:
|
||||
X509CertificateSchannel();
|
||||
~X509CertificateSchannel();
|
||||
|
||||
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user