diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/network/ssl | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/network/ssl')
25 files changed, 10041 insertions, 0 deletions
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp new file mode 100644 index 0000000..e2d0743 --- /dev/null +++ b/src/network/ssl/qssl.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslkey.h" + +QT_BEGIN_NAMESPACE + +/*! \namespace QSsl + + \brief The QSsl namespace declares enums common to all SSL classes in QtNetwork. + \since 4.3 + + \ingroup io + \inmodule QtNetwork +*/ + +/*! + \enum QSsl::KeyType + + Describes the two types of keys QSslKey supports. + + \value PrivateKey A private key. + \value PublicKey A public key. +*/ + +/*! + \enum QSsl::KeyAlgorithm + + Describes the different key algorithms supported by QSslKey. + + \value Rsa The RSA algorithm. + \value Dsa The DSA algorithm. +*/ + +/*! + \enum QSsl::EncodingFormat + + Describes supported encoding formats for certificates and keys. + + \value Pem The PEM format. + \value Der The DER format. +*/ + +/*! + \enum QSsl::AlternateNameEntryType + + Describes the key types for alternate name entries in QSslCertificate. + + \value EmailEntry An email entry; the entry contains an email address that + the certificate is valid for. + + \value DnsEntry A DNS host name entry; the entry contains a host name + entry that the certificate is valid for. The entry may contain wildcards. + + \sa QSslCertificate::alternateSubjectNames() + +*/ + +/*! + \enum QSsl::SslProtocol + + Describes the protocol of the cipher. + + \value SslV3 SSLv3 - the default protocol. + \value SslV2 SSLv2 + \value TlsV1 TLSv1 + \value UnknownProtocol The cipher's protocol cannot be determined. + \value AnyProtocol The socket understands SSLv2, SSLv3, and TLSv1. This + value is used by QSslSocket only. + + Note: most servers using SSL understand both versions (2 and 3), + but it is recommended to use the latest version only for security + reasons. However, SSL and TLS are not compatible with each other: + if you get unexpected handshake failures, verify that you chose + the correct setting for your protocol. +*/ + +QT_END_NAMESPACE diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h new file mode 100644 index 0000000..bd0d943 --- /dev/null +++ b/src/network/ssl/qssl.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSL_H +#define QSSL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +namespace QSsl { + enum KeyType { + PrivateKey, + PublicKey + }; + + enum EncodingFormat { + Pem, + Der + }; + + enum KeyAlgorithm { + Rsa, + Dsa + }; + + enum AlternateNameEntryType { + EmailEntry, + DnsEntry + }; + + enum SslProtocol { + SslV3, + SslV2, + TlsV1, + AnyProtocol, + UnknownProtocol = -1 + }; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSSL_H diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp new file mode 100644 index 0000000..a2ba644 --- /dev/null +++ b/src/network/ssl/qsslcertificate.cpp @@ -0,0 +1,795 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslCertificate + \brief The QSslCertificate class provides a convenient API for an X509 certificate. + \since 4.3 + + \reentrant + \ingroup io + \ingroup ssl + \inmodule QtNetwork + + QSslCertificate stores an X509 certificate, and is commonly used + to verify the identity and store information about the local host, + a remotely connected peer, or a trusted third party Certificate + Authority. + + There are many ways to construct a QSslCertificate. The most + common way is to call QSslSocket::peerCertificate(), which returns + a QSslCertificate object, or QSslSocket::peerCertificateChain(), + which returns a list of them. You can also load certificates from + a DER (binary) or PEM (Base64) encoded bundle, typically stored as + one or more local files, or in a Qt Resource. + + You can call isNull() to check if your certificate is null. By + default, QSslCertificate constructs a null certificate. To check + if the certificate is valid, call isValid(). A null certificate is + invalid, but an invalid certificate is not necessarily null. If + you want to reset all contents in a certificate, call clear(). + + After loading a certificate, you can find information about the + certificate, its subject, and its issuer, by calling one of the + many accessor functions, including version(), serialNumber(), + issuerInfo() and subjectInfo(). You can call notValidBefore() and + notValidAfter() to check when the certificate was issued, and when + it expires. The publicKey() function returns the certificate + subject's public key as a QSslKey. You can call issuerInfo() or + subjectInfo() to get detailed information about the certificate + issuer and its subject. + + Internally, QSslCertificate is stored as an X509 structure. You + can access this handle by calling handle(), but the results are + likely to not be portable. + + \sa QSslSocket, QSslKey, QSslCipher, QSslError +*/ + +/*! + \enum QSslCertificate::SubjectInfo + + Describes keys that you can pass to QSslCertificate::issuerInfo() or + QSslCertificate::subjectInfo() to get information about the certificate + issuer or subject. + + \value Organization "O" The name of the organization. + + \value CommonName "CN" The common name; most often this is used to store + the host name. + + \value LocalityName "L" The locality. + + \value OrganizationalUnitName "OU" The organizational unit name. + + \value CountryName "C" The country. + + \value StateOrProvinceName "ST" The state or province. +*/ + +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslcertificate.h" +#include "qsslcertificate_p.h" +#include "qsslkey.h" +#include "qsslkey_p.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> +#include <QtCore/qdir.h> +#include <QtCore/qdiriterator.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qmap.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +/*! + Constructs a QSslCertificate by reading \a format encoded data + from \a device and using the first certificate found. You can + later call isNull() to see if \a device contained a certificate, + and if this certificate was loaded successfully. +*/ +QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) + : d(new QSslCertificatePrivate) +{ + QSslSocketPrivate::ensureInitialized(); + if (device) + d->init(device->readAll(), format); +} + +/*! + Constructs a QSslCertificate by parsing the \a format encoded + \a data and using the first available certificate found. You can + later call isNull() to see if \a data contained a certificate, + and if this certificate was loaded successfully. +*/ +QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format) + : d(new QSslCertificatePrivate) +{ + QSslSocketPrivate::ensureInitialized(); + d->init(data, format); +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslCertificate::QSslCertificate(const QSslCertificate &other) : d(other.d) +{ + d->ref.ref(); +} + +/*! + Destroys the QSslCertificate. +*/ +QSslCertificate::~QSslCertificate() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Copies the contents of \a other into this certificate, making the two + certificates identical. +*/ +QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Returns true if this certificate is the same as \a other; otherwise + returns false. +*/ +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) + return q_X509_cmp(d->x509, other.d->x509) == 0; + return false; +} + +/*! + \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const + + Returns true if this certificate is not the same as \a other; otherwise + returns false. +*/ + +/*! + Returns true if this is a null certificate (i.e., a certificate + with no contents); otherwise returns false. + + By default, QSslCertificate constructs a null certificate. + + \sa isValid(), clear() +*/ +bool QSslCertificate::isNull() const +{ + return d->null; +} + +/*! + Returns true if this certificate is valid; otherwise returns + false. + + Note: Currently, this function only checks that the current + data-time is within the date-time range during which the + certificate is considered valid. No other checks are + currently performed. + + \sa isNull() +*/ +bool QSslCertificate::isValid() const +{ + const QDateTime currentTime = QDateTime::currentDateTime(); + return currentTime >= d->notValidBefore && currentTime <= d->notValidAfter; +} + +/*! + Clears the contents of this certificate, making it a null + certificate. + + \sa isNull() +*/ +void QSslCertificate::clear() +{ + if (isNull()) + return; + if (d->ref == 1) + delete d; + else + d->ref.deref(); + + d = new QSslCertificatePrivate; +} + +/*! + Returns the certificate's version string. +*/ +QByteArray QSslCertificate::version() const +{ + return d->versionString; +} + +/*! + Returns the certificate's serial number string. +*/ +QByteArray QSslCertificate::serialNumber() const +{ + return d->serialNumberString; +} + +/*! + Returns a cryptographic digest of this certificate. By default, + and MD5 digest will be generated, but you can also specify a + custom \a algorithm. +*/ +QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const +{ + return QCryptographicHash::hash(toDer(), algorithm); +} + +static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info) +{ + QString str; + switch (info) { + case QSslCertificate::Organization: str = QLatin1String("O"); break; + case QSslCertificate::CommonName: str = QLatin1String("CN"); break; + case QSslCertificate::LocalityName: str = QLatin1String("L"); break; + case QSslCertificate::OrganizationalUnitName: str = QLatin1String("OU"); break; + case QSslCertificate::CountryName: str = QLatin1String("C"); break; + case QSslCertificate::StateOrProvinceName: str = QLatin1String("ST"); break; + } + return str; +} + +/*! + \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const + + Returns the issuer information for the \a subject from the + certificate, or an empty string if there is no information for + \a subject in the certificate. + + \sa subjectInfo() +*/ +QString QSslCertificate::issuerInfo(SubjectInfo info) const +{ + return d->issuerInfo.value(_q_SubjectInfoToString(info)); +} + +/*! + Returns the issuer information for \a tag from the certificate, + or an empty string if there is no information for \a tag in the + certificate. + + \sa subjectInfo() +*/ +QString QSslCertificate::issuerInfo(const QByteArray &tag) const +{ + // ### Use a QByteArray for the keys in the map + return d->issuerInfo.value(QString::fromLatin1(tag)); +} + +/*! + + \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const + + Returns the information for the \a subject, or an empty string if + there is no information for \a subject in the certificate. + + \sa issuerInfo() +*/ +QString QSslCertificate::subjectInfo(SubjectInfo info) const +{ + return d->subjectInfo.value(_q_SubjectInfoToString(info)); +} + +/*! + Returns the subject information for \a tag, or an empty string if + there is no information for \a tag in the certificate. + + \sa issuerInfo() +*/ +QString QSslCertificate::subjectInfo(const QByteArray &tag) const +{ + // ### Use a QByteArray for the keys in the map + return d->subjectInfo.value(QString::fromLatin1(tag)); +} + +/*! + Returns the list of alternative subject names for this + certificate. The alternate subject names typically contain host + names, optionally with wildcards, that are valid for this + certificate. + + These names are tested against the connected peer's host name, if + either the subject information for \l CommonName doesn't define a + valid host name, or the subject info name doesn't match the peer's + host name. + + \sa subjectInfo() +*/ +QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const +{ + QMultiMap<QSsl::AlternateNameEntryType, QString> result; + + if (!d->x509) + return result; + + STACK *altNames = (STACK *)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0); + + 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) + continue; + + int len = q_ASN1_STRING_length(genName->d.ia5); + if (len < 0 || len >= 8192) { + // broken name + continue; + } + + const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_data(genName->d.ia5)); + const QString altName = QLatin1String(QByteArray(altNameStr, len)); + if (genName->type == GEN_DNS) + result.insert(QSsl::DnsEntry, altName); + else if (genName->type == GEN_EMAIL) + result.insert(QSsl::EmailEntry, altName); + } + q_sk_free(altNames); + } + + return result; +} + +/*! + Returns the date-time that the certificate becomes valid, or an + empty QDateTime if this is a null certificate. + + \sa expiryDate() +*/ +QDateTime QSslCertificate::effectiveDate() const +{ + return d->notValidBefore; +} + +/*! + Returns the date-time that the certificate expires, or an empty + QDateTime if this is a null certificate. + + \sa effectiveDate() +*/ +QDateTime QSslCertificate::expiryDate() const +{ + return d->notValidAfter; +} + +/*! + Returns a pointer to the native certificate handle, if there is + one, or a null pointer otherwise. + + You can use this handle, together with the native API, to access + extended information about the certificate. + + \warning Use of this function has a high probability of being + 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 +{ + return Qt::HANDLE(d->x509); +} + +/*! + Returns the certificate subject's public key. +*/ +QSslKey QSslCertificate::publicKey() const +{ + if (!d->x509) + return QSslKey(); + + QSslKey key; + + key.d->type = QSsl::PublicKey; + X509_PUBKEY *xkey = d->x509->cert_info->key; + EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey); + Q_ASSERT(pkey); + + if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) { + key.d->rsa = q_EVP_PKEY_get1_RSA(pkey); + key.d->algorithm = QSsl::Rsa; + key.d->isNull = false; + } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) { + key.d->dsa = q_EVP_PKEY_get1_DSA(pkey); + key.d->algorithm = QSsl::Dsa; + key.d->isNull = false; + } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) { + // DH unsupported + } else { + // error? + } + + q_EVP_PKEY_free(pkey); + return key; +} + +/*! + Returns this certificate converted to a PEM (Base64) encoded + representation. +*/ +QByteArray QSslCertificate::toPem() const +{ + if (!d->x509) + return QByteArray(); + return d->QByteArray_from_X509(d->x509, QSsl::Pem); +} + +/*! + Returns this certificate converted to a DER (binary) encoded + representation. +*/ +QByteArray QSslCertificate::toDer() const +{ + if (!d->x509) + return QByteArray(); + return d->QByteArray_from_X509(d->x509, QSsl::Der); +} + +/*! + Searches all files in the \a path for certificates encoded in the + specified \a format and returns them in a list. \e must be a file or a + pattern matching one or more files, as specified by \a syntax. + + Example: + + \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0 + + \sa fromData() +*/ +QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, + QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax) +{ + // $, (,), *, +, ., ?, [, ,], ^, {, | and }. + int pos = -1; + if (syntax == QRegExp::Wildcard) + pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\*\\?\\[\\]]"))); + else if (syntax != QRegExp::FixedString) + pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"))); + QString pathPrefix = path.left(pos); // == path if pos < 0 + if (pos != -1) + pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/'))); + + // Special case - if the prefix ends up being nothing, use "." instead and + // chop off the first two characters from the glob'ed paths. + int startIndex = 0; + if (pathPrefix.trimmed().isEmpty()) { + startIndex = 2; + pathPrefix = QLatin1String("."); + } + + // The path is a file. + if (pos == -1 && QFileInfo(pathPrefix).isFile()) { + QFile file(pathPrefix); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + return QSslCertificate::fromData(file.readAll(),format); + return QList<QSslCertificate>(); + } + + // The path can be a file or directory. + QList<QSslCertificate> certs; + QRegExp pattern(path, Qt::CaseSensitive, syntax); + QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + while (it.hasNext()) { + QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex); + if (!pattern.exactMatch(filePath)) + continue; + + QFile file(filePath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + certs += QSslCertificate::fromData(file.readAll(),format); + } + return certs; +} + +/*! + Searches for and parses all certificates in \a device that are + encoded in the specified \a format and returns them in a list of + certificates. + + \sa fromData() +*/ +QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format) +{ + if (!device) { + qWarning("QSslCertificate::fromDevice: cannot read from a null device"); + return QList<QSslCertificate>(); + } + return fromData(device->readAll(), format); +} + +/*! + Searches for and parses all certificates in \a data that are + encoded in the specified \a format and returns them in a list of + certificates. + + \sa fromDevice() +*/ +QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format) +{ + return (format == QSsl::Pem) + ? QSslCertificatePrivate::certificatesFromPem(data) + : QSslCertificatePrivate::certificatesFromDer(data); +} + +void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) +{ + if (!data.isEmpty()) { + 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); + } + } +} + +#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" +#define ENDCERTSTRING "-----END CERTIFICATE-----" + +// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations) +QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format) +{ + if (!x509) { + qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509"); + return QByteArray(); + } + + // Use i2d_X509 to convert the X509 to an array. + int length = q_i2d_X509(x509, 0); + 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"; +} + +static QMap<QString, QString> _q_mapFromOnelineName(char *name) +{ + QMap<QString, QString> info; + QString infoStr = QString::fromLocal8Bit(name); + q_CRYPTO_free(name); + + // ### The right-hand encoding seems to allow hex (Regulierungsbeh\xC8orde) + //entry.replace(QLatin1String("\\x"), QLatin1String("%")); + //entry = QUrl::fromPercentEncoding(entry.toLatin1()); + // ### See RFC-4630 for more details! + + QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)")); + + int pos = 0; + while ((pos = rx.indexIn(infoStr, pos)) != -1) { + const QString name = rx.cap(1); + + QString value = rx.cap(2); + const int valuePos = rx.pos(2); + + const int next = rx.indexIn(value); + if (next == -1) { + info.insert(name, value); + break; + } + + value = value.left(next); + info.insert(name, value); + pos = valuePos + value.length(); + } + + return info; +} + +QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509) +{ + QSslCertificate certificate; + if (!x509 || !QSslSocket::supportsSsl()) + return certificate; + + certificate.d->issuerInfo = + _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(x509), 0, 0)); + certificate.d->subjectInfo = + _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(x509), 0, 0)); + + ASN1_TIME *nbef = q_X509_get_notBefore(x509); + ASN1_TIME *naft = q_X509_get_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 = pem.at(*offset); + + // ignore extra whitespace at the end of the line + while (ch == ' ' && *offset < pem.size()) + ch = pem.at(++*offset); + + if (ch == '\n') { + *offset++; + 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 (!matchLineFeed(pem, &offset)) + break; + + QByteArray decoded = QByteArray::fromBase64( + QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + const unsigned char *data = (const unsigned char *)decoded.data(); +#else + unsigned char *data = (unsigned char *)decoded.data(); +#endif + + if (X509 *x509 = q_d2i_X509(0, &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(); + + +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + const unsigned char *data = (const unsigned char *)der.data(); +#else + unsigned char *data = (unsigned char *)der.data(); +#endif + int size = der.size(); + + while (count == -1 || certificates.size() < count) { + if (X509 *x509 = q_d2i_X509(0, &data, size)) { + certificates << QSslCertificate_from_X509(x509); + q_X509_free(x509); + } else { + break; + } + size -= ((char *)data - der.data()); + } + + return certificates; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QSslCertificate &certificate) +{ + debug << "QSslCertificate(" + << certificate.version() + << "," << certificate.serialNumber() + << "," << certificate.digest().toBase64() + << "," << certificate.issuerInfo(QSslCertificate::Organization) + << "," << certificate.subjectInfo(QSslCertificate::Organization) + << "," << certificate.alternateSubjectNames() +#ifndef QT_NO_TEXTSTREAM + << "," << certificate.effectiveDate() + << "," << certificate.expiryDate() +#endif + << ")"; + return debug; +} +QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info) +{ + switch (info) { + case QSslCertificate::Organization: debug << "Organization"; break; + case QSslCertificate::CommonName: debug << "CommonName"; break; + case QSslCertificate::CountryName: debug << "CountryName"; break; + case QSslCertificate::LocalityName: debug << "LocalityName"; break; + case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break; + case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break; + } + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h new file mode 100644 index 0000000..d4597cd --- /dev/null +++ b/src/network/ssl/qsslcertificate.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCERTIFICATE_H +#define QSSLCERTIFICATE_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qcryptographichash.h> +#include <QtCore/qregexp.h> +#include <QtNetwork/qssl.h> + +typedef struct x509_st X509; // ### check if this works + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QDateTime; +class QIODevice; +class QSslKey; +class QStringList; +template <typename T, typename U> class QMultiMap; + +class QSslCertificatePrivate; +class Q_NETWORK_EXPORT QSslCertificate +{ +public: + enum SubjectInfo { + Organization, + CommonName, + LocalityName, + OrganizationalUnitName, + CountryName, + StateOrProvinceName + }; + + QSslCertificate(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); + QSslCertificate( // ### s/encoded/data (to be consistent with signature in .cpp file) ? + const QByteArray &encoded = QByteArray(), QSsl::EncodingFormat format = QSsl::Pem); + QSslCertificate(const QSslCertificate &other); + ~QSslCertificate(); + QSslCertificate &operator=(const QSslCertificate &other); + bool operator==(const QSslCertificate &other) const; + inline bool operator!=(const QSslCertificate &other) const { return !operator==(other); } + + bool isNull() const; + bool isValid() const; + void clear(); + + // Certificate info + QByteArray version() const; + QByteArray serialNumber() const; + QByteArray digest(QCryptographicHash::Algorithm algorithm = QCryptographicHash::Md5) const; + QString issuerInfo(SubjectInfo info) const; + QString issuerInfo(const QByteArray &tag) const; + QString subjectInfo(SubjectInfo info) const; + QString subjectInfo(const QByteArray &tag) const; + QMultiMap<QSsl::AlternateNameEntryType, QString> alternateSubjectNames() const; + QDateTime effectiveDate() const; + QDateTime expiryDate() const; + QSslKey publicKey() const; + + QByteArray toPem() const; + QByteArray toDer() const; + + static QList<QSslCertificate> fromPath( + const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QRegExp::PatternSyntax syntax = QRegExp::FixedString); + static QList<QSslCertificate> fromDevice( + QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); + static QList<QSslCertificate> fromData( + const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); + + Qt::HANDLE handle() const; + +private: + QSslCertificatePrivate *d; + friend class QSslCertificatePrivate; + friend class QSslSocketBackendPrivate; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCertificate &certificate); +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h new file mode 100644 index 0000000..cbf374b --- /dev/null +++ b/src/network/ssl/qsslcertificate_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCERTIFICATE_P_H +#define QSSLCERTIFICATE_P_H + +#include "qsslcertificate.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslsocket_p.h" +#include <QtCore/qdatetime.h> +#include <QtCore/qmap.h> + +#include <openssl/x509.h> + +QT_BEGIN_NAMESPACE + +class QSslCertificatePrivate +{ +public: + QSslCertificatePrivate() + : null(true), x509(0) + { + QSslSocketPrivate::ensureInitialized(); + ref = 1; + } + + ~QSslCertificatePrivate() + { + if (x509) + q_X509_free(x509); + } + + bool null; + QByteArray versionString; + QByteArray serialNumberString; + + QMap<QString, QString> issuerInfo; + QMap<QString, QString> subjectInfo; + QDateTime notValidAfter; + QDateTime notValidBefore; + + X509 *x509; + + void init(const QByteArray &data, QSsl::EncodingFormat format); + + static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format); + 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); + + friend class QSslSocketBackendPrivate; + + QAtomicInt ref; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp new file mode 100644 index 0000000..505c662 --- /dev/null +++ b/src/network/ssl/qsslcipher.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslCipher + \brief The QSslCipher class represents an SSL cryptographic cipher. + \since 4.3 + + \reentrant + \ingroup io + \ingroup ssl + \inmodule QtNetwork + + QSslCipher stores information about one cryptographic cipher. It + is most commonly used with QSslSocket, either for configuring + which ciphers the socket can use, or for displaying the socket's + ciphers to the user. + + \sa QSslSocket, QSslKey +*/ + +#include "qsslcipher.h" +#include "qsslcipher_p.h" +#include "qsslsocket.h" + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +#endif + +/*! + Constructs an empty QSslCipher object. +*/ +QSslCipher::QSslCipher() + : d(new QSslCipherPrivate) +{ +} + +/*! + Constructs a QSslCipher object for the cipher determined by \a + name and \a protocol. The constructor accepts only supported + ciphers (i.e., the \a name and \a protocol must identify a cipher + in the list of ciphers returned by + QSslSocket::supportedCiphers()). + + You can call isNull() after construction to check if \a name and + \a protocol correctly identified a supported cipher. +*/ +QSslCipher::QSslCipher(const QString &name, QSsl::SslProtocol protocol) + : d(new QSslCipherPrivate) +{ + foreach (const QSslCipher &cipher, QSslSocket::supportedCiphers()) { + if (cipher.name() == name && cipher.protocol() == protocol) { + *this = cipher; + return; + } + } +} + +/*! + Constructs an identical copy of the \a other cipher. +*/ +QSslCipher::QSslCipher(const QSslCipher &other) + : d(new QSslCipherPrivate) +{ + *d = *other.d; +} + +/*! + Destroys the QSslCipher object. +*/ +QSslCipher::~QSslCipher() +{ + delete d; +} + +/*! + Copies the contents of \a other into this cipher, making the two + ciphers identical. +*/ +QSslCipher &QSslCipher::operator=(const QSslCipher &other) +{ + *d = *other.d; + return *this; +} + +/*! + Returns true if this cipher is the same as \a other; otherwise, + false is returned. +*/ +bool QSslCipher::operator==(const QSslCipher &other) const +{ + return d->name == other.d->name && d->protocol == other.d->protocol; +} + +/*! + \fn bool QSslCipher::operator!=(const QSslCipher &other) const + + Returns true if this cipher is not the same as \a other; + otherwise, false is returned. +*/ + +/*! + Returns true if this is a null cipher; otherwise returns false. +*/ +bool QSslCipher::isNull() const +{ + return d->isNull; +} + +/*! + Returns the name of the cipher, or an empty QString if this is a null + cipher. + + \sa isNull() +*/ +QString QSslCipher::name() const +{ + return d->name; +} + +/*! + Returns the number of bits supported by the cipher. + + \sa usedBits() +*/ +int QSslCipher::supportedBits()const +{ + return d->supportedBits; +} + +/*! + Returns the number of bits used by the cipher. + + \sa supportedBits() +*/ +int QSslCipher::usedBits() const +{ + return d->bits; +} + +/*! + Returns the cipher's key exchange method as a QString. +*/ +QString QSslCipher::keyExchangeMethod() const +{ + return d->keyExchangeMethod; +} + +/*! + Returns the cipher's authentication method as a QString. +*/ +QString QSslCipher::authenticationMethod() const +{ + return d->authenticationMethod; +} + +/*! + Returns the cipher's encryption method as a QString. +*/ +QString QSslCipher::encryptionMethod() const +{ + return d->encryptionMethod; +} + +/*! + Returns the cipher's protocol as a QString. + + \sa protocol() +*/ +QString QSslCipher::protocolString() const +{ + return d->protocolString; +} + +/*! + Returns the cipher's protocol type, or \l QSsl::UnknownProtocol if + QSslCipher is unable to determine the protocol (protocolString() may + contain more information). + + \sa protocolString() +*/ +QSsl::SslProtocol QSslCipher::protocol() const +{ + return d->protocol; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QSslCipher &cipher) +{ + debug << "QSslCipher(name=" << qPrintable(cipher.name()) + << ", bits=" << cipher.usedBits() + << ", proto=" << qPrintable(cipher.protocolString()) + << ")"; + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h new file mode 100644 index 0000000..404dc3d --- /dev/null +++ b/src/network/ssl/qsslcipher.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCIPHER_H +#define QSSLCIPHER_H + +#include <QtCore/qstring.h> +#include <QtNetwork/qssl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QSslCipherPrivate; +class Q_NETWORK_EXPORT QSslCipher +{ +public: + QSslCipher(); + QSslCipher(const QString &name, QSsl::SslProtocol protocol); + QSslCipher(const QSslCipher &other); + ~QSslCipher(); + QSslCipher &operator=(const QSslCipher &other); + bool operator==(const QSslCipher &other) const; + inline bool operator!=(const QSslCipher &other) const { return !operator==(other); } + + bool isNull() const; + QString name() const; + int supportedBits() const; + int usedBits() const; + + QString keyExchangeMethod() const; + QString authenticationMethod() const; + QString encryptionMethod() const; + QString protocolString() const; + QSsl::SslProtocol protocol() const; + +private: + QSslCipherPrivate *d; + friend class QSslSocketBackendPrivate; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCipher &cipher); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/network/ssl/qsslcipher_p.h b/src/network/ssl/qsslcipher_p.h new file mode 100644 index 0000000..46e5054 --- /dev/null +++ b/src/network/ssl/qsslcipher_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslcipher.h" + +QT_BEGIN_NAMESPACE + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +class QSslCipherPrivate +{ +public: + QSslCipherPrivate() + : isNull(true), supportedBits(0), bits(0), + exportable(false), protocol(QSsl::UnknownProtocol) + { + } + + bool isNull; + QString name; + int supportedBits; + int bits; + QString keyExchangeMethod; + QString authenticationMethod; + QString encryptionMethod; + bool exportable; + QString protocolString; + QSsl::SslProtocol protocol; +}; + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp new file mode 100644 index 0000000..4a0f226 --- /dev/null +++ b/src/network/ssl/qsslconfiguration.cpp @@ -0,0 +1,545 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslconfiguration.h" +#include "qsslconfiguration_p.h" +#include "qsslsocket.h" +#include "qmutex.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +template<> void QSharedDataPointer<QSslConfigurationPrivate>::detach() +{ + if (d && d->ref == 1) + return; + QSslConfigurationPrivate *x = (d ? new QSslConfigurationPrivate(*d) + : new QSslConfigurationPrivate); + x->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = x; +} + +/*! + \class QSslConfiguration + \brief The QSslConfiguration class holds the configuration and state of an SSL connection + \since 4.4 + + \reentrant + \inmodule QtNetwork + \ingroup io + \ingroup ssl + + QSslConfiguration is used by Qt networking classes to relay + information about an open SSL connection and to allow the + application to control certain features of that connection. + + The settings that QSslConfiguration currently supports are: + + \list + \o The SSL/TLS protocol to be used + \o The certificate to be presented to the peer during connection + and its associated private key + \o The ciphers allowed to be used for encrypting the connection + \o The list of Certificate Authorities certificates that are + used to validate the peer's certificate + \endlist + + These settings are applied only during the connection + handshake. Setting them after the connection has been established + has no effect. + + The state that QSslConfiguration supports are: + \list + \o The certificate the peer presented during handshake, along + with the chain leading to a CA certificate + \o The cipher used to encrypt this session + \endlist + + The state can only be obtained once the SSL connection starts, but + not necessarily before it's done. Some settings may change during + the course of the SSL connection without need to restart it (for + instance, the cipher can be changed over time). + + State in QSslConfiguration objects cannot be changed. + + QSslConfiguration can be used with QSslSocket and the Network + Access API. + + Note that changing settings in QSslConfiguration is not enough to + change the settings in the related SSL connection. You must call + setSslConfiguration on a modified QSslConfiguration object to + achieve that. The following example illustrates how to change the + protocol to TLSv1 in a QSslSocket object: + + \snippet doc/src/snippets/code/src_network_ssl_qsslconfiguration.cpp 0 + + \sa QSsl::SslProtocol, QSslCertificate, QSslCipher, QSslKey + QSslSocket, QNetworkAccessManager, + QSslSocket::sslConfiguration(), QSslSocket::setSslConfiguration() +*/ + +/*! + Constructs an empty SSL configuration. This configuration contains + no valid settings and the state will be empty. isNull() will + return true after this constructor is called. + + Once any setter methods are called, isNull() will return false. +*/ +QSslConfiguration::QSslConfiguration() + : d(0) +{ +} + +/*! + Copies the configuration and state of \a other. If \a other is + null, this object will be null too. +*/ +QSslConfiguration::QSslConfiguration(const QSslConfiguration &other) + : d(other.d) +{ +} + +/*! + Releases any resources held by QSslConfiguration. +*/ +QSslConfiguration::~QSslConfiguration() +{ + // QSharedDataPointer deletes d for us if necessary +} + +/*! + Copies the configuration and state of \a other. If \a other is + null, this object will be null too. +*/ +QSslConfiguration &QSslConfiguration::operator=(const QSslConfiguration &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QSslConfiguration object is equal to \a + other. + + Two QSslConfiguration objects are considered equal if they have + the exact same settings and state. + + \sa operator!=() +*/ +bool QSslConfiguration::operator==(const QSslConfiguration &other) const +{ + if (d == other.d) + return true; + return d->peerCertificate == other.d->peerCertificate && + d->peerCertificateChain == other.d->peerCertificateChain && + d->localCertificate == other.d->localCertificate && + d->privateKey == other.d->privateKey && + d->sessionCipher == other.d->sessionCipher && + d->ciphers == other.d->ciphers && + d->caCertificates == d->caCertificates && + d->protocol == other.d->protocol && + d->peerVerifyMode == other.d->peerVerifyMode && + d->peerVerifyDepth == other.d->peerVerifyDepth; +} + +/*! + \fn QSslConfiguration::operator!=(const QSslConfiguration &other) const + + Returns true if this QSslConfiguration differs from \a other. Two + QSslConfiguration objects are considered different if any state or + setting is different. + + \sa operator==() +*/ + +/*! + Returns true if this is a null QSslConfiguration object. + + A QSslConfiguration object is null if it has been + default-constructed and no setter methods have been called. + + \sa setProtocol(), setLocalCertificate(), setPrivateKey(), + setCiphers(), setCaCertificates() +*/ +bool QSslConfiguration::isNull() const +{ + return d == 0; +} + +/*! + Returns the protocol setting for this SSL configuration. + + \sa setProtocol() +*/ +QSsl::SslProtocol QSslConfiguration::protocol() const +{ + return d ? d->protocol : QSsl::SslV3; +} + +/*! + Sets the protocol setting for this configuration to be \a + protocol. + + Setting the protocol once the connection has already been + established has no effect. + + \sa protocol() +*/ +void QSslConfiguration::setProtocol(QSsl::SslProtocol protocol) +{ + d->protocol = protocol; +} + +/*! + Returns the verify mode. This mode decides whether QSslSocket should + request a certificate from the peer (i.e., the client requests a + certificate from the server, or a server requesting a certificate from the + client), and whether it should require that this certificate is valid. + + The default mode is AutoVerifyPeer, which tells QSslSocket to use + VerifyPeer for clients, QueryPeer for clients. + + \sa setPeerVerifyMode() +*/ +QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const +{ + return d ? d->peerVerifyMode : QSslSocket::AutoVerifyPeer; +} + +/*! + Sets the verify mode to \a mode. This mode decides whether QSslSocket + should request a certificate from the peer (i.e., the client requests a + certificate from the server, or a server requesting a certificate from the + client), and whether it should require that this certificate is valid. + + The default mode is AutoVerifyPeer, which tells QSslSocket to use + VerifyPeer for clients, QueryPeer for clients. + + \sa peerVerifyMode() +*/ +void QSslConfiguration::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode) +{ + d->peerVerifyMode = mode; +} + + +/*! + Returns the maximum number of certificates in the peer's certificate chain + to be checked during the SSL handshake phase, or 0 (the default) if no + maximum depth has been set, indicating that the whole certificate chain + should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa setPeerVerifyDepth(), peerVerifyMode() +*/ +int QSslConfiguration::peerVerifyDepth() const +{ + return d ? d->peerVerifyDepth : 0; +} + +/*! + Sets the maximum number of certificates in the peer's certificate chain to + be checked during the SSL handshake phase, to \a depth. Setting a depth of + 0 means that no maximum depth is set, indicating that the whole + certificate chain should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa peerVerifyDepth(), setPeerVerifyMode() +*/ +void QSslConfiguration::setPeerVerifyDepth(int depth) +{ + if (depth < 0) { + qWarning("QSslConfiguration::setPeerVerifyDepth: cannot set negative depth of %d", depth); + return; + } + d->peerVerifyDepth = depth; +} + +/*! + Returns the certificate to be presented to the peer during the SSL + handshake process. + + \sa setLocalCertificate() +*/ +QSslCertificate QSslConfiguration::localCertificate() const +{ + return d ? d->localCertificate : QSslCertificate(); +} + +/*! + Sets the certificate to be presented to the peer during SSL + handshake to be \a certificate. + + Setting the certificate once the connection has been established + has no effect. + + A certificate is the means of identification used in the SSL + process. The local certificate is used by the remote end to verify + the local user's identity against its list of Certification + Authorities. In most cases, such as in HTTP web browsing, only + servers identify to the clients, so the client does not send a + certificate. + + \sa localCertificate() +*/ +void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate) +{ + d->localCertificate = certificate; +} + +/*! + Returns the peer's digital certificate (i.e., the immediate + certificate of the host you are connected to), or a null + certificate, if the peer has not assigned a certificate. + + The peer certificate is checked automatically during the + handshake phase, so this function is normally used to fetch + the certificate for display or for connection diagnostic + purposes. It contains information about the peer, including + its host name, the certificate issuer, and the peer's public + key. + + Because the peer certificate is set during the handshake phase, it + is safe to access the peer certificate from a slot connected to + the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors() + signal, or the QSslSocket::encrypted() signal. + + If a null certificate is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to check the peer's complete chain of certificates, + use peerCertificateChain() to get them all at once. + + \sa peerCertificateChain(), + QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(), + QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors() +*/ +QSslCertificate QSslConfiguration::peerCertificate() const +{ + return d ? d->peerCertificate : QSslCertificate(); +} + +/*! + Returns the peer's chain of digital certificates, starting with + the peer's immediate certificate and ending with the CA's + certificate. + + Peer certificates are checked automatically during the handshake + phase. This function is normally used to fetch certificates for + display, or for performing connection diagnostics. Certificates + contain information about the peer and the certificate issuers, + including host name, issuer names, and issuer public keys. + + Because the peer certificate is set during the handshake phase, it + is safe to access the peer certificate from a slot connected to + the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors() + signal, or the QSslSocket::encrypted() signal. + + If an empty list is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to get only the peer's immediate certificate, use + peerCertificate(). + + \sa peerCertificate(), + QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(), + QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors() +*/ +QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const +{ + return d ? d->peerCertificateChain : QList<QSslCertificate>(); +} + +/*! + Returns the socket's cryptographic \l {QSslCipher} {cipher}, or a + null cipher if the connection isn't encrypted. The socket's cipher + for the session is set during the handshake phase. The cipher is + used to encrypt and decrypt data transmitted through the socket. + + The SSL infrastructure also provides functions for setting the + ordered list of ciphers from which the handshake phase will + eventually select the session cipher. This ordered list must be in + place before the handshake phase begins. + + \sa ciphers(), setCiphers(), QSslSocket::supportedCiphers() +*/ +QSslCipher QSslConfiguration::sessionCipher() const +{ + return d ? d->sessionCipher : QSslCipher(); +} + +/*! + Returns the \l {QSslKey} {SSL key} assigned to this connection or + a null key if none has been assigned yet. + + \sa setPrivateKey(), localCertificate() +*/ +QSslKey QSslConfiguration::privateKey() const +{ + return d ? d->privateKey : QSslKey(); +} + +/*! + Sets the connection's private \l {QSslKey} {key} to \a key. The + private key and the local \l {QSslCertificate} {certificate} are + used by clients and servers that must prove their identity to + SSL peers. + + Both the key and the local certificate are required if you are + creating an SSL server socket. If you are creating an SSL client + socket, the key and local certificate are required if your client + must identify itself to an SSL server. + + \sa privateKey(), setLocalCertificate() +*/ +void QSslConfiguration::setPrivateKey(const QSslKey &key) +{ + d->privateKey = key; +} + +/*! + Returns this connection's current cryptographic cipher suite. This + list is used during the handshake phase for choosing a + session cipher. The returned list of ciphers is ordered by + descending preference. (i.e., the first cipher in the list is the + most preferred cipher). The session cipher will be the first one + in the list that is also supported by the peer. + + By default, the handshake phase can choose any of the ciphers + supported by this system's SSL libraries, which may vary from + system to system. The list of ciphers supported by this system's + SSL libraries is returned by QSslSocket::supportedCiphers(). You can restrict + the list of ciphers used for choosing the session cipher for this + socket by calling setCiphers() with a subset of the supported + ciphers. You can revert to using the entire set by calling + setCiphers() with the list returned by QSslSocket::supportedCiphers(). + + \sa setCiphers(), QSslSocket::supportedCiphers() +*/ +QList<QSslCipher> QSslConfiguration::ciphers() const +{ + return d ? d->ciphers : QList<QSslCipher>(); +} + +/*! + Sets the cryptographic cipher suite for this socket to \a ciphers, + which must contain a subset of the ciphers in the list returned by + supportedCiphers(). + + Restricting the cipher suite must be done before the handshake + phase, where the session cipher is chosen. + + \sa ciphers(), QSslSocket::supportedCiphers() +*/ +void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers) +{ + d->ciphers = ciphers; +} + +/*! + Returns this connection's CA certificate database. The CA certificate + database is used by the socket during the handshake phase to + validate the peer's certificate. It can be moodified prior to the + handshake with addCaCertificate(), addCaCertificates(), and + setCaCertificates(). + + \sa setCaCertificates() +*/ +QList<QSslCertificate> QSslConfiguration::caCertificates() const +{ + return d ? d->caCertificates : QList<QSslCertificate>(); +} + +/*! + Sets this socket's CA certificate database to be \a certificates. + The certificate database must be set prior to the SSL handshake. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + \sa caCertificates() +*/ +void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certificates) +{ + d->caCertificates = certificates; +} + +/*! + Returns the default SSL configuration to be used in new SSL + connections. + + The default SSL configuration consists of: + + \list + \o no local certificate and no private key + \o protocol SSLv3 + \o the system's default CA certificate list + \o the cipher list equal to the list of the SSL libraries' + supported SSL ciphers + \endlist + + \sa QSslSocket::supportedCiphers(), setDefaultConfiguration() +*/ +QSslConfiguration QSslConfiguration::defaultConfiguration() +{ + return QSslConfigurationPrivate::defaultConfiguration(); +} + +/*! + Sets the default SSL configuration to be used in new SSL + connections to be \a configuration. Existing connections are not + affected by this call. + + \sa QSslSocket::supportedCiphers(), defaultConfiguration() +*/ +void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configuration) +{ + QSslConfigurationPrivate::setDefaultConfiguration(configuration); +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h new file mode 100644 index 0000000..fa3d646 --- /dev/null +++ b/src/network/ssl/qsslconfiguration.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, Trolltech gives permission to link +** the code of its release of Qt with the OpenSSL project's "OpenSSL" library +** (or modified versions of the "OpenSSL" library that use the same license +** as the original version), and distribute the linked executables. +** +** You must comply with the GNU General Public License version 2 in all +** respects for all of the code used other than the "OpenSSL" code. If you +** modify this file, you may extend this exception to your version of the file, +** but you are not obligated to do so. If you do not wish to do so, delete +** this exception statement from your version of this file. +** +****************************************************************************/ + +#ifndef QSSLCONFIGURATION_H +#define QSSLCONFIGURATION_H + +#include <QtCore/qshareddata.h> +#include <QtNetwork/qsslsocket.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +template<typename T> class QList; +class QSslCertificate; +class QSslCipher; +class QSslKey; + +class QSslConfigurationPrivate; +class Q_NETWORK_EXPORT QSslConfiguration +{ +public: + QSslConfiguration(); + QSslConfiguration(const QSslConfiguration &other); + ~QSslConfiguration(); + QSslConfiguration &operator=(const QSslConfiguration &other); + + bool operator==(const QSslConfiguration &other) const; + inline bool operator!=(const QSslConfiguration &other) const + { return !(*this == other); } + + bool isNull() const; + + QSsl::SslProtocol protocol() const; + void setProtocol(QSsl::SslProtocol protocol); + + // Verification + QSslSocket::PeerVerifyMode peerVerifyMode() const; + void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode); + + int peerVerifyDepth() const; + void setPeerVerifyDepth(int depth); + + // Certificate & cipher configuration + QSslCertificate localCertificate() const; + void setLocalCertificate(const QSslCertificate &certificate); + + QSslCertificate peerCertificate() const; + QList<QSslCertificate> peerCertificateChain() const; + QSslCipher sessionCipher() const; + + // Private keys, for server sockets + QSslKey privateKey() const; + void setPrivateKey(const QSslKey &key); + + // Cipher settings + QList<QSslCipher> ciphers() const; + void setCiphers(const QList<QSslCipher> &ciphers); + + // Certificate Authority (CA) settings + QList<QSslCertificate> caCertificates() const; + void setCaCertificates(const QList<QSslCertificate> &certificates); + + static QSslConfiguration defaultConfiguration(); + static void setDefaultConfiguration(const QSslConfiguration &configuration); + +private: + friend class QSslSocket; + friend class QSslConfigurationPrivate; + QSslConfiguration(QSslConfigurationPrivate *dd); + QSharedDataPointer<QSslConfigurationPrivate> d; +}; + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h new file mode 100644 index 0000000..3c4dbee --- /dev/null +++ b/src/network/ssl/qsslconfiguration_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, Trolltech gives permission to link +** the code of its release of Qt with the OpenSSL project's "OpenSSL" library +** (or modified versions of the "OpenSSL" library that use the same license +** as the original version), and distribute the linked executables. +** +** You must comply with the GNU General Public License version 2 in all +** respects for all of the code used other than the "OpenSSL" code. If you +** modify this file, you may extend this exception to your version of the file, +** but you are not obligated to do so. If you do not wish to do so, delete +** this exception statement from your version of this file. +** +****************************************************************************/ + +#ifndef QSSLCONFIGURATION_P_H +#define QSSLCONFIGURATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QSslSocket API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslconfiguration.h" +#include "qlist.h" +#include "qsslcertificate.h" +#include "qsslcipher.h" +#include "qsslkey.h" + +QT_BEGIN_NAMESPACE + +class QSslConfigurationPrivate: public QSharedData +{ +public: + QSslConfigurationPrivate() + : protocol(QSsl::SslV3), + peerVerifyMode(QSslSocket::AutoVerifyPeer), + peerVerifyDepth(0) + { } + + QSslCertificate peerCertificate; + QList<QSslCertificate> peerCertificateChain; + QSslCertificate localCertificate; + + QSslKey privateKey; + QSslCipher sessionCipher; + QList<QSslCipher> ciphers; + QList<QSslCertificate> caCertificates; + + QSsl::SslProtocol protocol; + QSslSocket::PeerVerifyMode peerVerifyMode; + int peerVerifyDepth; + + // in qsslsocket.cpp: + static QSslConfiguration defaultConfiguration(); + static void setDefaultConfiguration(const QSslConfiguration &configuration); + static void deepCopyDefaultConfiguration(QSslConfigurationPrivate *config); +}; + +// implemented here for inlining purposes +inline QSslConfiguration::QSslConfiguration(QSslConfigurationPrivate *dd) + : d(dd) +{ +} + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp new file mode 100644 index 0000000..daa6533 --- /dev/null +++ b/src/network/ssl/qsslerror.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslError + \brief The QSslError class provides an SSL error. + \since 4.3 + + \reentrant + \ingroup io + \ingroup ssl + \inmodule QtNetwork + + QSslError provides a simple API for managing errors during QSslSocket's + SSL handshake. + + \sa QSslSocket, QSslCertificate, QSslCipher +*/ + +/*! + \enum QSslError::SslError + + Describes all recognized errors that can occur during an SSL handshake. + + \value NoError + \value UnableToGetIssuerCertificate + \value UnableToDecryptCertificateSignature + \value UnableToDecodeIssuerPublicKey + \value CertificateSignatureFailed + \value CertificateNotYetValid + \value CertificateExpired + \value InvalidNotBeforeField + \value InvalidNotAfterField + \value SelfSignedCertificate + \value SelfSignedCertificateInChain + \value UnableToGetLocalIssuerCertificate + \value UnableToVerifyFirstCertificate + \value CertificateRevoked + \value InvalidCaCertificate + \value PathLengthExceeded + \value InvalidPurpose + \value CertificateUntrusted + \value CertificateRejected + \value SubjectIssuerMismatch + \value AuthorityIssuerSerialNumberMismatch + \value NoPeerCertificate + \value HostNameMismatch + \value UnspecifiedError + \value NoSslSupport + + \sa QSslError::errorString() +*/ + +#include "qsslerror.h" +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +#endif + +class QSslErrorPrivate +{ +public: + QSslError::SslError error; + QSslCertificate certificate; +}; + +/*! + Constructs a QSslError object. The two optional arguments specify the \a + error that occurred, and which \a certificate the error relates to. + + \sa QSslCertificate +*/ +QSslError::QSslError(SslError error, const QSslCertificate &certificate) + : d(new QSslErrorPrivate) +{ + d->error = error; + d->certificate = certificate; +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslError::QSslError(const QSslError &other) + : d(new QSslErrorPrivate) +{ + *d = *other.d; +} + +/*! + Destroys the QSslError object. +*/ +QSslError::~QSslError() +{ + delete d; +} + +/*! + \since 4.4 + + Assigns the contents of \a other to this error. +*/ +QSslError &QSslError::operator=(const QSslError &other) +{ + *d = *other.d; + return *this; +} + +/*! + \since 4.4 + + Returns true if this error is equal to \a other; otherwise returns false. +*/ +bool QSslError::operator==(const QSslError &other) const +{ + return d->error == other.d->error + && d->certificate == other.d->certificate; +} + +/*! + \fn bool QSslError::operator!=(const QSslError &other) const + \since 4.4 + + Returns true if this error is not equal to \a other; otherwise returns + false. +*/ + +/*! + Returns the type of the error. + + \sa errorString(), certificate() +*/ +QSslError::SslError QSslError::error() const +{ + return d->error; +} + +/*! + Returns a short localized human-readable description of the error. + + \sa error(), certificate() +*/ +QString QSslError::errorString() const +{ + QString errStr; + switch (d->error) { + case NoError: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "No error")); + break; + case UnableToGetIssuerCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The issuer certificate could not be found")); + break; + case UnableToDecryptCertificateSignature: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate signature could not be decrypted")); + break; + case UnableToDecodeIssuerPublicKey: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The public key in the certificate could not be read")); + break; + case CertificateSignatureFailed: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The signature of the certificate is invalid")); + break; + case CertificateNotYetValid: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate is not yet valid")); + break; + case CertificateExpired: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate has expired")); + break; + case InvalidNotBeforeField: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate's notBefore field contains an invalid time")); + break; + case InvalidNotAfterField: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate's notAfter field contains an invalid time")); + break; + case SelfSignedCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate is self-signed, and untrusted")); + break; + case SelfSignedCertificateInChain: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root certificate of the certificate chain is self-signed, and untrusted")); + break; + case UnableToGetLocalIssuerCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The issuer certificate of a locally looked up certificate could not be found")); + break; + case UnableToVerifyFirstCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "No certificates could be verified")); + break; + case InvalidCaCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "One of the CA certificates is invalid")); + break; + case PathLengthExceeded: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The basicConstraints pathlength parameter has been exceeded")); + break; + case InvalidPurpose: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The supplied certificate is unsuited for this purpose")); + break; + case CertificateUntrusted: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root CA certificate is not trusted for this purpose")); + break; + case CertificateRejected: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root CA certificate is marked to reject the specified purpose")); + break; + case SubjectIssuerMismatch: // hostname mismatch + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, + "The current candidate issuer certificate was rejected because its" + " subject name did not match the issuer name of the current certificate")); + break; + case AuthorityIssuerSerialNumberMismatch: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The current candidate issuer certificate was rejected because" + " its issuer name and serial number was present and did not match the" + " authority key identifier of the current certificate")); + break; + case NoPeerCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The peer did not present any certificate")); + break; + case HostNameMismatch: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, + "The host name did not match any of the valid hosts" + " for this certificate")); + break; + case NoSslSupport: + break; + default: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "Unknown error")); + break; + } + + return errStr; +} + +/*! + Returns the certificate associated with this error, or a null certificate + if the error does not relate to any certificate. + + \sa error(), errorString() +*/ +QSslCertificate QSslError::certificate() const +{ + return d->certificate; +} + +#ifndef QT_NO_DEBUG_STREAM +//class QDebug; +QDebug operator<<(QDebug debug, const QSslError &error) +{ + debug << error.errorString(); + return debug; +} +QDebug operator<<(QDebug debug, const QSslError::SslError &error) +{ + debug << QSslError(error).errorString(); + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h new file mode 100644 index 0000000..c120215 --- /dev/null +++ b/src/network/ssl/qsslerror.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLERROR_H +#define QSSLERROR_H + +#include <QtCore/qvariant.h> +#include <QtNetwork/qsslcertificate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QSslErrorPrivate; +class Q_NETWORK_EXPORT QSslError +{ +public: + enum SslError { + NoError, + UnableToGetIssuerCertificate, + UnableToDecryptCertificateSignature, + UnableToDecodeIssuerPublicKey, + CertificateSignatureFailed, + CertificateNotYetValid, + CertificateExpired, + InvalidNotBeforeField, + InvalidNotAfterField, + SelfSignedCertificate, + SelfSignedCertificateInChain, + UnableToGetLocalIssuerCertificate, + UnableToVerifyFirstCertificate, + CertificateRevoked, + InvalidCaCertificate, + PathLengthExceeded, + InvalidPurpose, + CertificateUntrusted, + CertificateRejected, + SubjectIssuerMismatch, // hostname mismatch? + AuthorityIssuerSerialNumberMismatch, + NoPeerCertificate, + HostNameMismatch, + NoSslSupport, + UnspecifiedError = -1 + }; + + QSslError(SslError error = NoError, const QSslCertificate &certificate = QSslCertificate()); + QSslError(const QSslError &other); + ~QSslError(); + QSslError &operator=(const QSslError &other); + bool operator==(const QSslError &other) const; + inline bool operator!=(const QSslError &other) const + { return !(*this == other); } + + SslError error() const; + QString errorString() const; + QSslCertificate certificate() const; + +private: + QSslErrorPrivate *d; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError &error); +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError::SslError &error); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslkey.cpp b/src/network/ssl/qsslkey.cpp new file mode 100644 index 0000000..8d550c0 --- /dev/null +++ b/src/network/ssl/qsslkey.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslKey + \brief The QSslKey class provides an interface for private and public keys. + \since 4.3 + + \reentrant + \ingroup io + \ingroup ssl + \inmodule QtNetwork + + QSslKey provides a simple API for managing keys. + + \sa QSslSocket, QSslCertificate, QSslCipher +*/ + +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslkey.h" +#include "qsslkey_p.h" +#include "qsslsocket.h" +#include "qsslsocket_p.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qiodevice.h> +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +#endif + + +/*! + \internal + */ +void QSslKeyPrivate::clear(bool deep) +{ + isNull = true; + if (!QSslSocket::supportsSsl()) + return; + if (rsa) { + if (deep) + q_RSA_free(rsa); + rsa = 0; + } + if (dsa) { + if (deep) + q_DSA_free(dsa); + dsa = 0; + } +} + +/*! + \internal + + Allocates a new rsa or dsa struct and decodes \a pem into it + according to the current algorithm and type. + + If \a deepClear is true, the rsa/dsa struct is freed if it is was + already allocated, otherwise we "leak" memory (which is exactly + what we want for copy construction). + + If \a passPhrase is non-empty, it will be used for decrypting + \a pem. +*/ +void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, + bool deepClear) +{ + if (pem.isEmpty()) + return; + + clear(deepClear); + + if (!QSslSocket::supportsSsl()) + return; + + BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size()); + if (!bio) + return; + + void *phrase = passPhrase.isEmpty() + ? (void *)0 + : (void *)passPhrase.constData(); + + if (algorithm == QSsl::Rsa) { + RSA *result = (type == QSsl::PublicKey) + ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase) + : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase); + if (rsa && rsa == result) + isNull = false; + } else { + DSA *result = (type == QSsl::PublicKey) + ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase) + : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase); + if (dsa && dsa == result) + isNull = false; + } + + q_BIO_free(bio); +} + +/*! + Constructs a null key. + + \sa isNull() +*/ +QSslKey::QSslKey() + : d(new QSslKeyPrivate) +{ +} + +/*! + \internal +*/ +QByteArray QSslKeyPrivate::pemHeader() const +{ + // ### use QByteArray::fromRawData() instead + if (type == QSsl::PublicKey) + return QByteArray("-----BEGIN PUBLIC KEY-----\n"); + else if (algorithm == QSsl::Rsa) + return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n"); + return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n"); +} + +/*! + \internal +*/ +QByteArray QSslKeyPrivate::pemFooter() const +{ + // ### use QByteArray::fromRawData() instead + if (type == QSsl::PublicKey) + return QByteArray("-----END PUBLIC KEY-----\n"); + else if (algorithm == QSsl::Rsa) + return QByteArray("-----END RSA PRIVATE KEY-----\n"); + return QByteArray("-----END DSA PRIVATE KEY-----\n"); +} + +/*! + \internal + + Returns a DER key formatted as PEM. +*/ +QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const +{ + QByteArray pem(der.toBase64()); + + const int lineWidth = 64; // RFC 1421 + const int newLines = pem.size() / lineWidth; + const bool rem = pem.size() % lineWidth; + + // ### optimize + for (int i = 0; i < newLines; ++i) + pem.insert((i + 1) * lineWidth + i, '\n'); + if (rem) + pem.append('\n'); // ### + + pem.prepend(pemHeader()); + pem.append(pemFooter()); + + return pem; +} + +/*! + \internal + + Returns a PEM key formatted as DER. +*/ +QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const +{ + const QByteArray header = pemHeader(); + const QByteArray footer = pemFooter(); + + QByteArray der(pem); + + const int headerIndex = der.indexOf(header); + const int footerIndex = der.indexOf(footer); + if (headerIndex == -1 || footerIndex == -1) + return QByteArray(); + + der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size())); + + return QByteArray::fromBase64(der); // ignores newlines +} + +/*! + Constructs a QSslKey by decoding the string in the byte array + \a encoded using a specified \a algorithm and \a encoding format. + If the encoded key is encrypted, \a passPhrase is used to decrypt + it. \a type specifies whether the key is public or private. + + After construction, use isNull() to check if \a encoded contained + a valid key. +*/ +QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase) + : d(new QSslKeyPrivate) +{ + d->type = type; + d->algorithm = algorithm; + d->decodePem((encoding == QSsl::Der) + ? d->pemFromDer(encoded) : encoded, + passPhrase); +} + +/*! + Constructs a QSslKey by reading and decoding data from a + \a device using a specified \a algorithm and \a encoding format. + If the encoded key is encrypted, \a passPhrase is used to decrypt + it. \a type specifies whether the key is public or private. + + After construction, use isNull() to check if \a device provided + a valid key. +*/ +QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding, + QSsl::KeyType type, const QByteArray &passPhrase) + : d(new QSslKeyPrivate) +{ + QByteArray encoded; + if (device) + encoded = device->readAll(); + d->type = type; + d->algorithm = algorithm; + d->decodePem((encoding == QSsl::Der) ? + d->pemFromDer(encoded) : encoded, + passPhrase); +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslKey::QSslKey(const QSslKey &other) : d(other.d) +{ + d->ref.ref(); +} + +/*! + Destroys the QSslKey object. +*/ +QSslKey::~QSslKey() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Copies the contents of \a other into this key, making the two keys + identical. + + Returns a reference to this QSslKey. +*/ +QSslKey &QSslKey::operator=(const QSslKey &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Returns true if this is a null key; otherwise false. + + \sa clear() +*/ +bool QSslKey::isNull() const +{ + return d->isNull; +} + +/*! + Clears the contents of this key, making it a null key. + + \sa isNull() +*/ +void QSslKey::clear() +{ + if (!d->ref.deref()) { + delete d; + d = new QSslKeyPrivate; + } +} + +/*! + Returns the length of the key in bits, or -1 if the key is null. +*/ +int QSslKey::length() const +{ + if (d->isNull) + return -1; + return (d->algorithm == QSsl::Rsa) + ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p); +} + +/*! + Returns the type of the key (i.e., PublicKey or PrivateKey). +*/ +QSsl::KeyType QSslKey::type() const +{ + return d->type; +} + +/*! + Returns the key algorithm. +*/ +QSsl::KeyAlgorithm QSslKey::algorithm() const +{ + return d->algorithm; +} + +/*! + Returns the key in DER encoding. The result is encrypted with + \a passPhrase if the key is a private key and \a passPhrase is + non-empty. +*/ +// ### autotest failure for non-empty passPhrase and private key +QByteArray QSslKey::toDer(const QByteArray &passPhrase) const +{ + if (d->isNull) + return QByteArray(); + return d->derFromPem(toPem(passPhrase)); +} + +/*! + Returns the key in PEM encoding. The result is encrypted with + \a passPhrase if the key is a private key and \a passPhrase is + non-empty. +*/ +QByteArray QSslKey::toPem(const QByteArray &passPhrase) const +{ + if (!QSslSocket::supportsSsl() || d->isNull) + return QByteArray(); + + BIO *bio = q_BIO_new(q_BIO_s_mem()); + if (!bio) + return QByteArray(); + + bool fail = false; + + if (d->algorithm == QSsl::Rsa) { + if (d->type == QSsl::PublicKey) { + if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa)) + fail = true; + } else { + if (!q_PEM_write_bio_RSAPrivateKey( + bio, d->rsa, + // ### the cipher should be selectable in the API: + passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(), + (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) { + fail = true; + } + } + } else { + if (d->type == QSsl::PublicKey) { + if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa)) + fail = true; + } else { + if (!q_PEM_write_bio_DSAPrivateKey( + bio, d->dsa, + // ### the cipher should be selectable in the API: + passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(), + (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) { + fail = true; + } + } + } + + QByteArray pem; + if (!fail) { + char *data; + long size = q_BIO_get_mem_data(bio, &data); + pem = QByteArray(data, size); + } + q_BIO_free(bio); + return pem; +} + +/*! + Returns a pointer to the native key handle, if it is available; + otherwise a null pointer is returned. + + You can use this handle together with the native API to access + extended information about the key. + + \warning Use of this function has a high probability of being + non-portable, and its return value may vary across platforms, and + between minor Qt releases. +*/ +Qt::HANDLE QSslKey::handle() const +{ + return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa); +} + +/*! + Returns true if this key is equal to \a other; otherwise returns false. +*/ +bool QSslKey::operator==(const QSslKey &other) const +{ + if (isNull()) + return other.isNull(); + if (other.isNull()) + return isNull(); + if (algorithm() != other.algorithm()) + return false; + if (type() != other.type()) + return false; + if (length() != other.length()) + return false; + return toDer() == other.toDer(); +} + +/*! \fn bool QSslKey::operator!=(const QSslKey &other) const + + Returns true if this key is not equal to key \a other; otherwise + returns false. +*/ + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +QDebug operator<<(QDebug debug, const QSslKey &key) +{ + debug << "QSslKey(" + << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey") + << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA") + << ", " << key.length() + << ")"; + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h new file mode 100644 index 0000000..45d03f7 --- /dev/null +++ b/src/network/ssl/qsslkey.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLKEY_H +#define QSSLKEY_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qbytearray.h> +#include <QtNetwork/qssl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +template <typename A, typename B> struct QPair; + +class QIODevice; + +class QSslKeyPrivate; +class Q_NETWORK_EXPORT QSslKey +{ +public: + QSslKey(); + QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat format = QSsl::Pem, + QSsl::KeyType type = QSsl::PrivateKey, + const QByteArray &passPhrase = QByteArray()); + QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat format = QSsl::Pem, + QSsl::KeyType type = QSsl::PrivateKey, + const QByteArray &passPhrase = QByteArray()); + QSslKey(const QSslKey &other); + ~QSslKey(); + QSslKey &operator=(const QSslKey &other); + + bool isNull() const; + void clear(); + + int length() const; + QSsl::KeyType type() const; + QSsl::KeyAlgorithm algorithm() const; + + QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const; + QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const; + + Qt::HANDLE handle() const; + + bool operator==(const QSslKey &key) const; + inline bool operator!=(const QSslKey &key) const { return !operator==(key); } + +private: + QSslKeyPrivate *d; + friend class QSslCertificate; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslKey &key); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h new file mode 100644 index 0000000..c809015 --- /dev/null +++ b/src/network/ssl/qsslkey_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLKEY_P_H +#define QSSLKEY_P_H + +#include "qsslkey.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qsslcertificate.cpp. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <openssl/rsa.h> +#include <openssl/dsa.h> + +QT_BEGIN_NAMESPACE + +class QSslKeyPrivate +{ +public: + inline QSslKeyPrivate() + : rsa(0) + , dsa(0) + { + clear(); + ref = 1; + } + + inline ~QSslKeyPrivate() + { clear(); } + + void clear(bool deep = true); + + void decodePem(const QByteArray &pem, const QByteArray &passPhrase, + bool deepClear = true); + QByteArray pemHeader() const; + QByteArray pemFooter() const; + QByteArray pemFromDer(const QByteArray &der) const; + QByteArray derFromPem(const QByteArray &pem) const; + + bool isNull; + QSsl::KeyType type; + QSsl::KeyAlgorithm algorithm; + RSA *rsa; + DSA *dsa; + + QAtomicInt ref; +}; + +QT_END_NAMESPACE + +#endif // QSSLKEY_P_H diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp new file mode 100644 index 0000000..ea64042 --- /dev/null +++ b/src/network/ssl/qsslsocket.cpp @@ -0,0 +1,2045 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +//#define QSSLSOCKET_DEBUG + +/*! + \class QSslSocket + \brief The QSslSocket class provides an SSL encrypted socket for both + clients and servers. + \since 4.3 + + \reentrant + \ingroup io + \ingroup ssl + \inmodule QtNetwork + + QSslSocket establishes a secure, encrypted TCP connection you can + use for transmitting encrypted data. It can operate in both client + and server mode, and it supports modern SSL protocols, including + SSLv3 and TLSv1. By default, QSslSocket uses SSLv3, but you can + change the SSL protocol by calling setProtocol() as long as you do + it before the handshake has started. + + SSL encryption operates on top of the existing TCP stream after + the socket enters the ConnectedState. There are two simple ways to + establish a secure connection using QSslSocket: With an immediate + SSL handshake, or with a delayed SSL handshake occurring after the + connection has been established in unencrypted mode. + + The most common way to use QSslSocket is to construct an object + and start a secure connection by calling connectToHostEncrypted(). + This method starts an immediate SSL handshake once the connection + has been established. + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 0 + + As with a plain QTcpSocket, QSslSocket enters the HostLookupState, + ConnectingState, and finally the ConnectedState, if the connection + is successful. The handshake then starts automatically, and if it + succeeds, the encrypted() signal is emitted to indicate the socket + has entered the encrypted state and is ready for use. + + Note that data can be written to the socket immediately after the + return from connectToHostEncrypted() (i.e., before the encrypted() + signal is emitted). The data is queued in QSslSocket until after + the encrypted() signal is emitted. + + An example of using the delayed SSL handshake to secure an + existing connection is the case where an SSL server secures an + incoming connection. Suppose you create an SSL server class as a + subclass of QTcpServer. You would override + QTcpServer::incomingConnection() with something like the example + below, which first constructs an instance of QSslSocket and then + calls setSocketDescriptor() to set the new socket's descriptor to + the existing one passed in. It then initiates the SSL handshake + by calling startServerEncryption(). + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 1 + + If an error occurs, QSslSocket emits the sslErrors() signal. In this + case, if no action is taken to ignore the error(s), the connection + is dropped. To continue, despite the occurrence of an error, you + can call ignoreSslErrors(), either from within this slot after the + error occurs, or any time after construction of the QSslSocket and + before the connection is attempted. This will allow QSslSocket to + ignore the errors it encounters when establishing the identity of + the peer. Ignoring errors during an SSL handshake should be used + with caution, since a fundamental characteristic of secure + connections is that they should be established with a successful + handshake. + + Once encrypted, you use QSslSocket as a regular QTcpSocket. When + readyRead() is emitted, you can call read(), canReadLine() and + readLine(), or getChar() to read decrypted data from QSslSocket's + internal buffer, and you can call write() or putChar() to write + data back to the peer. QSslSocket will automatically encrypt the + written data for you, and emit bytesWritten() once the data has + been written to the peer. + + As a convenience, QSslSocket supports QTcpSocket's blocking + functions waitForConnected(), waitForReadyRead(), + waitForBytesWritten(), and waitForDisconnected(). It also provides + waitForEncrypted(), which will block the calling thread until an + encrypted connection has been established. + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 2 + + QSslSocket provides an extensive, easy-to-use API for handling + cryptographic ciphers, private keys, and local, peer, and + Certification Authority (CA) certificates. It also provides an API + for handling errors that occur during the handshake phase. + + The following features can also be customized: + + \list + \o The socket's cryptographic cipher suite can be customized before + the handshake phase with setCiphers() and setDefaultCiphers(). + \o The socket's local certificate and private key can be customized + before the handshake phase with setLocalCertificate() and + setPrivateKey(). + \o The CA certificate database can be extended and customized with + addCaCertificate(), addCaCertificates(), setCaCertificates(), + addDefaultCaCertificate(), addDefaultCaCertificates(), and + setDefaultCaCertificates(). + \endlist + + For more information about ciphers and certificates, refer to QSslCipher and + QSslCertificate. + + This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (\l{http://www.openssl.org/}). + + \sa QSslCertificate, QSslCipher, QSslError +*/ + +/*! + \enum QSslSocket::SslMode + + Describes the connection modes available for QSslSocket. + + \value UnencryptedMode The socket is unencrypted. Its + behavior is identical to QTcpSocket. + + \value SslClientMode The socket is a client-side SSL socket. + It is either alreayd encrypted, or it is in the SSL handshake + phase (see QSslSocket::isEncrypted()). + + \value SslServerMode The socket is a server-side SSL socket. + It is either already encrypted, or it is in the SSL handshake + phase (see QSslSocket::isEncrypted()). +*/ + +/*! + \enum QSslSocket::PeerVerifyMode + \since 4.4 + + Describes the peer verification modes for QSslSocket. The default mode is + AutoVerifyPeer, which selects an appropriate mode depending on the + socket's QSocket::SslMode. + + \value VerifyNone QSslSocket will not request a certificate from the + peer. You can set this mode if you are not interested in the identity of + the other side of the connection. The connection will still be encrypted, + and your socket will still send its local certificate to the peer if it's + requested. + + \value QueryPeer QSslSocket will request a certificate from the peer, but + does not require this certificate to be valid. This is useful when you + want to display peer certificate details to the user without affecting the + actual SSL handshake. This mode is the default for servers. + + \value VerifyPeer QSslSocket will request a certificate from the peer + during the SSL handshake phase, and requires that this certificate is + valid. On failure, QSslSocket will emit the QSslSocket::sslErrors() + signal. This mode is the default for clients. + + \value AutoVerifyPeer QSslSocket will automaticaly use QueryPeer for + server sockets and VerifyPeer for client sockets. + + \sa QSslSocket::peerVerifyMode() +*/ + +/*! + \fn QSslSocket::encrypted() + + This signal is emitted when QSslSocket enters encrypted mode. After this + signal has been emitted, QSslSocket::isEncrypted() will return true, and + all further transmissions on the socket will be encrypted. + + \sa QSslSocket::connectToHostEncrypted(), QSslSocket::isEncrypted() +*/ + +/*! + \fn QSslSocket::modeChanged(QSslSocket::SslMode mode) + + This signal is emitted when QSslSocket changes from \l + QSslSocket::UnencryptedMode to either \l QSslSocket::SslClientMode or \l + QSslSocket::SslServerMode. \a mode is the new mode. + + \sa QSslSocket::mode() +*/ + +/*! + \fn QSslSocket::encryptedBytesWritten(qint64 written) + \since 4.4 + + This signal is emitted when QSslSocket writes its encrypted data to the + network. The \a written parameter contains the number of bytes that were + successfully written. + + \sa QIODevice::bytesWritten() +*/ + +/*! + \fn void QSslSocket::peerVerifyError(const QSslError &error) + \since 4.4 + + QSslSocket can emit this signal several times during the SSL handshake, + before encryption has been established, to indicate that an error has + occurred while establishing the identity of the peer. The \a error is + usually an indication that QSslSocket is unable to securely identify the + peer. + + This signal provides you with an early indication when something's wrong. + By connecting to this signal, you can manually choose to tear down the + connection from inside the connected slot before the handshake has + completed. If no action is taken, QSslSocket will proceed to emitting + QSslSocket::sslErrors(). + + \sa sslErrors() +*/ + +/*! + \fn void QSslSocket::sslErrors(const QList<QSslError> &errors); + + QSslSocket emits this signal after the SSL handshake to indicate that one + or more errors have occurred while establishing the identity of the + peer. The errors are usually an indication that QSslSocket is unable to + securely identify the peer. Unless any action is taken, the connection + will be dropped after this signal has been emitted. + + If you want to continue connecting despite the errors that have occurred, + you must call QSslSocket::ignoreSslErrors() from inside a slot connected to + this signal. If you need to access the error list at a later point, you + can call sslErrors() (without arguments). + + \a errors contains one or more errors that prevent QSslSocket from + verifying the identity of the peer. + + Note: You cannot use Qt::QueuedConnection when connecting to this signal, + or calling QSslSocket::ignoreSslErrors() will have no effect. + + \sa peerVerifyError() +*/ + +#include "qsslcipher.h" +#include "qsslsocket.h" +#include "qsslsocket_openssl_p.h" +#include "qsslconfiguration_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qdir.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qmutex.h> +#include <QtNetwork/qhostaddress.h> +#include <QtNetwork/qhostinfo.h> + +QT_BEGIN_NAMESPACE + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int qt_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) + return -1; + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +class QSslSocketGlobalData +{ +public: + QSslSocketGlobalData() : config(new QSslConfigurationPrivate) {} + + QMutex mutex; + QList<QSslCipher> supportedCiphers; + QExplicitlySharedDataPointer<QSslConfigurationPrivate> config; +}; +Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData) + +/*! + Constructs a QSslSocket object. \a parent is passed to QObject's + constructor. The new socket's \l {QSslCipher} {cipher} suite is + set to the one returned by the static method defaultCiphers(). +*/ +QSslSocket::QSslSocket(QObject *parent) + : QTcpSocket(*new QSslSocketBackendPrivate, parent) +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::QSslSocket(" << parent << "), this =" << (void *)this; +#endif + d->q_ptr = this; + d->init(); +} + +/*! + Destroys the QSslSocket. +*/ +QSslSocket::~QSslSocket() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::~QSslSocket(), this =" << (void *)this; +#endif + delete d->plainSocket; + d->plainSocket = 0; +} + +/*! + Starts an encrypted connection to the device \a hostName on \a + port, using \a mode as the \l OpenMode. This is equivalent to + calling connectToHost() to establish the connection, followed by a + call to startClientEncryption(). + + QSslSocket first enters the HostLookupState. Then, after entering + either the event loop or one of the waitFor...() functions, it + enters the ConnectingState, emits connected(), and then initiates + the SSL client handshake. At each state change, QSslSocket emits + signal stateChanged(). + + After initiating the SSL client handshake, if the identity of the + peer can't be established, signal sslErrors() is emitted. If you + want to ignore the errors and continue connecting, you must call + ignoreSslErrors(), either from inside a slot function connected to + the sslErrors() signal, or prior to entering encrypted mode. If + ignoreSslErrors is not called, the connection is dropped, signal + disconnected() is emitted, and QSslSocket returns to the + UnconnectedState. + + If the SSL handshake is successful, QSslSocket emits encrypted(). + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 3 + + \bold{Note:} The example above shows that text can be written to + the socket immediately after requesting the encrypted connection, + before the encrypted() signal has been emitted. In such cases, the + text is queued in the object and written to the socket \e after + the connection is established and the encrypted() signal has been + emitted. + + The default for \a mode is \l ReadWrite. + + If you want to create a QSslSocket on the server side of a connection, you + should instead call startServerEncryption() upon receiving the incoming + connection through QTcpServer. + + \sa connectToHost(), startClientEncryption(), waitForConnected(), waitForEncrypted() +*/ +void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode) +{ + Q_D(QSslSocket); + if (d->state == ConnectedState || d->state == ConnectingState) { + qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected"); + return; + } + + d->init(); + d->autoStartHandshake = true; + d->initialized = true; + + // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs) + // establish the connection immediately (i.e., first attempt). + connectToHost(hostName, port, mode); +} + +/*! + Initializes QSslSocket with the native socket descriptor \a + socketDescriptor. Returns true if \a socketDescriptor is accepted + as a valid socket descriptor; otherwise returns false. + The socket is opened in the mode specified by \a openMode, and + enters the socket state specified by \a state. + + \bold{Note:} It is not possible to initialize two sockets with the same + native socket descriptor. + + \sa socketDescriptor() +*/ +bool QSslSocket::setSocketDescriptor(int socketDescriptor, SocketState state, OpenMode openMode) +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::setSocketDescriptor(" << socketDescriptor << "," + << state << "," << openMode << ")"; +#endif + if (!d->plainSocket) + d->createPlainSocket(openMode); + bool retVal = d->plainSocket->setSocketDescriptor(socketDescriptor, state, openMode); + d->cachedSocketDescriptor = d->plainSocket->socketDescriptor(); + setSocketError(d->plainSocket->error()); + setSocketState(state); + setOpenMode(openMode); + setLocalPort(d->plainSocket->localPort()); + setLocalAddress(d->plainSocket->localAddress()); + setPeerPort(d->plainSocket->peerPort()); + setPeerAddress(d->plainSocket->peerAddress()); + setPeerName(d->plainSocket->peerName()); + return retVal; +} + +/*! + Returns the current mode for the socket; either UnencryptedMode, where + QSslSocket behaves identially to QTcpSocket, or one of SslClientMode or + SslServerMode, where the client is either negotiating or in encrypted + mode. + + When the mode changes, QSslSocket emits modeChanged() + + \sa SslMode +*/ +QSslSocket::SslMode QSslSocket::mode() const +{ + Q_D(const QSslSocket); + return d->mode; +} + +/*! + Returns true if the socket is encrypted; otherwise, false is returned. + + An encrypted socket encrypts all data that is written by calling write() + or putChar() before the data is written to the network, and descrypts all + incoming data as the data is received from the network, before you call + read(), readLine() or getChar(). + + QSslSocket emits encrypted() when it enters encrypted mode. + + You can call sessionCipher() to find which cryptographic cipher is used to + encrypt and decrypt your data. + + \sa mode() +*/ +bool QSslSocket::isEncrypted() const +{ + Q_D(const QSslSocket); + return d->connectionEncrypted; +} + +/*! + Returns the socket's SSL protocol. By default, \l QSsl::SslV3 is used. + + \sa setProtocol() +*/ +QSsl::SslProtocol QSslSocket::protocol() const +{ + Q_D(const QSslSocket); + return d->configuration.protocol; +} + +/*! + Sets the socket's SSL protocol to \a protocol. This will affect the next + initiated handshake; calling this function on an already-encrypted socket + will not affect the socket's protocol. +*/ +void QSslSocket::setProtocol(QSsl::SslProtocol protocol) +{ + Q_D(QSslSocket); + d->configuration.protocol = protocol; +} + +/*! + \since 4.4 + + Returns the socket's verify mode. This mode mode decides whether + QSslSocket should request a certificate from the peer (i.e., the client + requests a certificate from the server, or a server requesting a + certificate from the client), and whether it should require that this + certificate is valid. + + The default mode is AutoVerifyPeer, which tells QSslSocket to use + VerifyPeer for clients, QueryPeer for clients. + + \sa setPeerVerifyMode(), peerVerifyDepth(), mode() +*/ +QSslSocket::PeerVerifyMode QSslSocket::peerVerifyMode() const +{ + Q_D(const QSslSocket); + return d->configuration.peerVerifyMode; +} + +/*! + \since 4.4 + + Sets the socket's verify mode to \a mode. This mode decides whether + QSslSocket should request a certificate from the peer (i.e., the client + requests a certificate from the server, or a server requesting a + certificate from the client), and whether it should require that this + certificate is valid. + + The default mode is AutoVerifyPeer, which tells QSslSocket to use + VerifyPeer for clients, QueryPeer for clients. + + Setting this mode after encryption has started has no effect on the + current connection. + + \sa peerVerifyMode(), setPeerVerifyDepth(), mode() +*/ +void QSslSocket::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode) +{ + Q_D(QSslSocket); + d->configuration.peerVerifyMode = mode; +} + +/*! + \since 4.4 + + Returns the maximum number of certificates in the peer's certificate chain + to be checked during the SSL handshake phase, or 0 (the default) if no + maximum depth has been set, indicating that the whole certificate chain + should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa setPeerVerifyDepth(), peerVerifyMode() +*/ +int QSslSocket::peerVerifyDepth() const +{ + Q_D(const QSslSocket); + return d->configuration.peerVerifyDepth; +} + +/*! + \since 4.4 + + Sets the maximum number of certificates in the peer's certificate chain to + be checked during the SSL handshake phase, to \a depth. Setting a depth of + 0 means that no maximum depth is set, indicating that the whole + certificate chain should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa peerVerifyDepth(), setPeerVerifyMode() +*/ +void QSslSocket::setPeerVerifyDepth(int depth) +{ + Q_D(QSslSocket); + if (depth < 0) { + qWarning("QSslSocket::setPeerVerifyDepth: cannot set negative depth of %d", depth); + return; + } + d->configuration.peerVerifyDepth = depth; +} + +/*! + \reimp + + Returns the number of decrypted bytes that are immediately available for + reading. +*/ +qint64 QSslSocket::bytesAvailable() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return QIODevice::bytesAvailable() + (d->plainSocket ? d->plainSocket->bytesAvailable() : 0); + return QIODevice::bytesAvailable() + d->readBuffer.size(); +} + +/*! + \reimp + + Returns the number of unencrypted bytes that are waiting to be encrypted + and written to the network. +*/ +qint64 QSslSocket::bytesToWrite() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return d->plainSocket ? d->plainSocket->bytesToWrite() : 0; + return d->writeBuffer.size(); +} + +/*! + \since 4.4 + + Returns the number of encrypted bytes that are awaiting decryption. + Normally, this function will return 0 because QSslSocket decrypts its + incoming data as soon as it can. +*/ +qint64 QSslSocket::encryptedBytesAvailable() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return 0; + return d->plainSocket->bytesAvailable(); +} + +/*! + \since 4.4 + + Returns the number of encrypted bytes that are waiting to be written to + the network. +*/ +qint64 QSslSocket::encryptedBytesToWrite() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return 0; + return d->plainSocket->bytesToWrite(); +} + +/*! + \reimp + + Returns true if you can read one while line (terminated by a single ASCII + '\n' character) of decrypted characters; otherwise, false is returned. +*/ +bool QSslSocket::canReadLine() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return QIODevice::canReadLine() || (d->plainSocket && d->plainSocket->canReadLine()); + return QIODevice::canReadLine() || (!d->readBuffer.isEmpty() && d->readBuffer.canReadLine()); +} + +/*! + \reimp +*/ +void QSslSocket::close() +{ +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::close()"; +#endif + QTcpSocket::close(); +} + +/*! + \reimp +*/ +bool QSslSocket::atEnd() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return QIODevice::atEnd() && (!d->plainSocket || d->plainSocket->atEnd()); + return QIODevice::atEnd() && d->readBuffer.isEmpty(); +} + +/*! + This function writes as much as possible from the internal write buffer to + the underlying network socket, without blocking. If any data was written, + this function returns true; otherwise false is returned. + + Call this function if you need QSslSocket to start sending buffered data + immediately. The number of bytes successfully written depends on the + operating system. In most cases, you do not need to call this function, + because QAbstractSocket will start sending data automatically once control + goes back to the event loop. In the absence of an event loop, call + waitForBytesWritten() instead. + + \sa write(), waitForBytesWritten() +*/ +// Note! docs copied from QAbstractSocket::flush() +bool QSslSocket::flush() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::flush()"; +#endif + if (d->mode != UnencryptedMode) + // encrypt any unencrypted bytes in our buffer + d->transmit(); + + return d->plainSocket ? d->plainSocket->flush() : false; +} + +/*! + \since 4.4 + + Sets the size of QSslSocket's internal read buffer to be \a size bytes. +*/ +void QSslSocket::setReadBufferSize(qint64 size) +{ + Q_D(QSslSocket); + d->readBufferMaxSize = size; + + // set the plain socket's buffer size to 1k if we have a limit + // see also the same logic in QSslSocketPrivate::createPlainSocket + if (d->plainSocket) { + if (d->mode == UnencryptedMode) + d->plainSocket->setReadBufferSize(size); + else + d->plainSocket->setReadBufferSize(size ? 1024 : 0); + } +} + +/*! + Aborts the current connection and resets the socket. Unlike + disconnectFromHost(), this function immediately closes the socket, + clearing any pending data in the write buffer. + + \sa disconnectFromHost(), close() +*/ +void QSslSocket::abort() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::abort()"; +#endif + if (d->plainSocket) + d->plainSocket->abort(); + close(); +} + +/*! + \since 4.4 + + Returns the socket's SSL configuration state. The default SSL + configuration of a socket is to use the default ciphers, + default CA certificates, no local private key or certificate. + + The SSL configuration also contains fields that can change with + time without notice. + + \sa localCertificate(), peerCertificate(), peerCertificateChain(), + sessionCipher(), privateKey(), ciphers(), caCertificates() +*/ +QSslConfiguration QSslSocket::sslConfiguration() const +{ + Q_D(const QSslSocket); + + // create a deep copy of our configuration + QSslConfigurationPrivate *copy = new QSslConfigurationPrivate(d->configuration); + copy->ref = 0; // the QSslConfiguration constructor refs up + copy->sessionCipher = d->sessionCipher(); + + return QSslConfiguration(copy); +} + +/*! + \since 4.4 + + Sets the socket's SSL configuration to be the contents of \a configuration. + This function sets the local certificate, the ciphers, the private key and the CA + certificates to those stored in \a configuration. + + It is not possible to set the SSL-state related fields. + + \sa setLocalCertificate(), setPrivateKey(), setCaCertificates(), setCiphers() +*/ +void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) +{ + Q_D(QSslSocket); + d->configuration.localCertificate = configuration.localCertificate(); + d->configuration.privateKey = configuration.privateKey(); + d->configuration.ciphers = configuration.ciphers(); + d->configuration.caCertificates = configuration.caCertificates(); + d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); + d->configuration.peerVerifyMode = configuration.peerVerifyMode(); + d->configuration.protocol = configuration.protocol(); +} + +/*! + Sets the socket's local certificate to \a certificate. The local + certificate is necessary if you need to confirm your identity to the + peer. It is used together with the private key; if you set the local + certificate, you must also set the private key. + + The local certificate and private key are always necessary for server + sockets, but are also rarely used by client sockets if the server requires + the client to authenticate. + + \sa localCertificate(), setPrivateKey() +*/ +void QSslSocket::setLocalCertificate(const QSslCertificate &certificate) +{ + Q_D(QSslSocket); + d->configuration.localCertificate = certificate; +} + +/*! + \overload + + Sets the socket's local \l {QSslCertificate} {certificate} to the + first one found in file \a path, which is parsed according to the + specified \a format. +*/ +void QSslSocket::setLocalCertificate(const QString &path, + QSsl::EncodingFormat format) +{ + Q_D(QSslSocket); + QFile file(path); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + d->configuration.localCertificate = QSslCertificate(file.readAll(), format); +} + +/*! + Returns the socket's local \l {QSslCertificate} {certificate}, or + an empty certificate if no local certificate has been assigned. + + \sa setLocalCertificate(), privateKey() +*/ +QSslCertificate QSslSocket::localCertificate() const +{ + Q_D(const QSslSocket); + return d->configuration.localCertificate; +} + +/*! + Returns the peer's digital certificate (i.e., the immediate + certificate of the host you are connected to), or a null + certificate, if the peer has not assigned a certificate. + + The peer certificate is checked automatically during the + handshake phase, so this function is normally used to fetch + the certificate for display or for connection diagnostic + purposes. It contains information about the peer, including + its host name, the certificate issuer, and the peer's public + key. + + Because the peer certificate is set during the handshake phase, it + is safe to access the peer certificate from a slot connected to + the sslErrors() signal or the encrypted() signal. + + If a null certificate is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to check the peer's complete chain of certificates, + use peerCertificateChain() to get them all at once. + + \sa peerCertificateChain() +*/ +QSslCertificate QSslSocket::peerCertificate() const +{ + Q_D(const QSslSocket); + return d->configuration.peerCertificate; +} + +/*! + Returns the peer's chain of digital certificates, or an empty list + of certificates. + + Peer certificates are checked automatically during the handshake + phase. This function is normally used to fetch certificates for + display, or for performing connection diagnostics. Certificates + contain information about the peer and the certificate issuers, + including host name, issuer names, and issuer public keys. + + The peer certificates are set in QSslSocket during the handshake + phase, so it is safe to call this function from a slot connected + to the sslErrors() signal or the encrypted() signal. + + If an empty list is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to get only the peer's immediate certificate, use + peerCertificate(). + + \sa peerCertificate() +*/ +QList<QSslCertificate> QSslSocket::peerCertificateChain() const +{ + Q_D(const QSslSocket); + return d->configuration.peerCertificateChain; +} + +/*! + Returns the socket's cryptographic \l {QSslCipher} {cipher}, or a + null cipher if the connection isn't encrypted. The socket's cipher + for the session is set during the handshake phase. The cipher is + used to encrypt and decrypt data transmitted through the socket. + + QSslSocket also provides functions for setting the ordered list of + ciphers from which the handshake phase will eventually select the + session cipher. This ordered list must be in place before the + handshake phase begins. + + \sa ciphers(), setCiphers(), setDefaultCiphers(), defaultCiphers(), + supportedCiphers() +*/ +QSslCipher QSslSocket::sessionCipher() const +{ + Q_D(const QSslSocket); + return d->sessionCipher(); +} + +/*! + Sets the socket's private \l {QSslKey} {key} to \a key. The + private key and the local \l {QSslCertificate} {certificate} are + used by clients and servers that must prove their identity to + SSL peers. + + Both the key and the local certificate are required if you are + creating an SSL server socket. If you are creating an SSL client + socket, the key and local certificate are required if your client + must identify itself to an SSL server. + + \sa privateKey(), setLocalCertificate() +*/ +void QSslSocket::setPrivateKey(const QSslKey &key) +{ + Q_D(QSslSocket); + d->configuration.privateKey = key; +} + +/*! + \overload + + Reads the string in file \a fileName and decodes it using + a specified \a algorithm and encoding \a format to construct + an \l {QSslKey} {SSL key}. If the encoded key is encrypted, + \a passPhrase is used to decrypt it. + + The socket's private key is set to the constructed key. The + private key and the local \l {QSslCertificate} {certificate} are + used by clients and servers that must prove their identity to SSL + peers. + + Both the key and the local certificate are required if you are + creating an SSL server socket. If you are creating an SSL client + socket, the key and local certificate are required if your client + must identify itself to an SSL server. + + \sa privateKey(), setLocalCertificate() +*/ +void QSslSocket::setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat format, const QByteArray &passPhrase) +{ + Q_D(QSslSocket); + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + d->configuration.privateKey = QSslKey(file.readAll(), algorithm, + format, QSsl::PrivateKey, passPhrase); + } +} + +/*! + Returns this socket's private key. + + \sa setPrivateKey(), localCertificate() +*/ +QSslKey QSslSocket::privateKey() const +{ + Q_D(const QSslSocket); + return d->configuration.privateKey; +} + +/*! + Returns this socket's current cryptographic cipher suite. This + list is used during the socket's handshake phase for choosing a + session cipher. The returned list of ciphers is ordered by + descending preference. (i.e., the first cipher in the list is the + most preferred cipher). The session cipher will be the first one + in the list that is also supported by the peer. + + By default, the handshake phase can choose any of the ciphers + supported by this system's SSL libraries, which may vary from + system to system. The list of ciphers supported by this system's + SSL libraries is returned by supportedCiphers(). You can restrict + the list of ciphers used for choosing the session cipher for this + socket by calling setCiphers() with a subset of the supported + ciphers. You can revert to using the entire set by calling + setCiphers() with the list returned by supportedCiphers(). + + You can restrict the list of ciphers used for choosing the session + cipher for \e all sockets by calling setDefaultCiphers() with a + subset of the supported ciphers. You can revert to using the + entire set by calling setCiphers() with the list returned by + supportedCiphers(). + + \sa setCiphers(), defaultCiphers(), setDefaultCiphers(), supportedCiphers() +*/ +QList<QSslCipher> QSslSocket::ciphers() const +{ + Q_D(const QSslSocket); + return d->configuration.ciphers; +} + +/*! + Sets the cryptographic cipher suite for this socket to \a ciphers, + which must contain a subset of the ciphers in the list returned by + supportedCiphers(). + + Restricting the cipher suite must be done before the handshake + phase, where the session cipher is chosen. + + \sa ciphers(), setDefaultCiphers(), supportedCiphers() +*/ +void QSslSocket::setCiphers(const QList<QSslCipher> &ciphers) +{ + Q_D(QSslSocket); + d->configuration.ciphers = ciphers; +} + +/*! + Sets the cryptographic cipher suite for this socket to \a ciphers, which + is a colon-separated list of cipher suite names. The ciphers are listed in + order of preference, starting with the most preferred cipher. For example: + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 4 + + Each cipher name in \a ciphers must be the name of a cipher in the + list returned by supportedCiphers(). Restricting the cipher suite + must be done before the handshake phase, where the session cipher + is chosen. + + \sa ciphers(), setDefaultCiphers(), supportedCiphers() +*/ +void QSslSocket::setCiphers(const QString &ciphers) +{ + Q_D(QSslSocket); + d->configuration.ciphers.clear(); + foreach (QString cipherName, ciphers.split(QLatin1String(":"),QString::SkipEmptyParts)) { + for (int i = 0; i < 3; ++i) { + // ### Crude + QSslCipher cipher(cipherName, QSsl::SslProtocol(i)); + if (!cipher.isNull()) + d->configuration.ciphers << cipher; + } + } +} + +/*! + Sets the default cryptographic cipher suite for all sockets in + this application to \a ciphers, which must contain a subset of the + ciphers in the list returned by supportedCiphers(). + + Restricting the default cipher suite only affects SSL sockets + that perform their handshake phase after the default cipher + suite has been changed. + + \sa setCiphers(), defaultCiphers(), supportedCiphers() +*/ +void QSslSocket::setDefaultCiphers(const QList<QSslCipher> &ciphers) +{ + QSslSocketPrivate::setDefaultCiphers(ciphers); +} + +/*! + Returns the default cryptographic cipher suite for all sockets in + this application. This list is used during the socket's handshake + phase when negotiating with the peer to choose a session cipher. + The list is ordered by preference (i.e., the first cipher in the + list is the most preferred cipher). + + By default, the handshake phase can choose any of the ciphers + supported by this system's SSL libraries, which may vary from + system to system. The list of ciphers supported by this system's + SSL libraries is returned by supportedCiphers(). + + \sa supportedCiphers() +*/ +QList<QSslCipher> QSslSocket::defaultCiphers() +{ + return QSslSocketPrivate::defaultCiphers(); +} + +/*! + Returns the list of cryptographic ciphers supported by this + system. This list is set by the system's SSL libraries and may + vary from system to system. + + \sa defaultCiphers(), ciphers(), setCiphers() +*/ +QList<QSslCipher> QSslSocket::supportedCiphers() +{ + return QSslSocketPrivate::supportedCiphers(); +} + +/*! + Searches all files in the \a path for certificates encoded in the + specified \a format and adds them to this socket's CA certificate + database. \a path can be explicit, or it can contain wildcards in + the format specified by \a syntax. Returns true if one or more + certificates are added to the socket's CA certificate database; + otherwise returns false. + + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + For more precise control, use addCaCertificate(). + + \sa addCaCertificate(), QSslCertificate::fromPath() +*/ +bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax) +{ + Q_D(QSslSocket); + QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax); + if (certs.isEmpty()) + return false; + + d->configuration.caCertificates += certs; + return true; +} + +/*! + Adds the \a certificate to this socket's CA certificate database. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + To add multiple certificates, use addCaCertificates(). + + \sa caCertificates(), setCaCertificates() +*/ +void QSslSocket::addCaCertificate(const QSslCertificate &certificate) +{ + Q_D(QSslSocket); + d->configuration.caCertificates += certificate; +} + +/*! + Adds the \a certificates to this socket's CA certificate database. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + For more precise control, use addCaCertificate(). + + \sa caCertificates(), addDefaultCaCertificate() +*/ +void QSslSocket::addCaCertificates(const QList<QSslCertificate> &certificates) +{ + Q_D(QSslSocket); + d->configuration.caCertificates += certificates; +} + +/*! + Sets this socket's CA certificate database to be \a certificates. + The certificate database must be set prior to the SSL handshake. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + The CA certificate database can be reset to the current default CA + certificate database by calling this function with the list of CA + certificates returned by defaultCaCertificates(). + + \sa defaultCaCertificates() +*/ +void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates) +{ + Q_D(QSslSocket); + d->configuration.caCertificates = certificates; +} + +/*! + Returns this socket's CA certificate database. The CA certificate + database is used by the socket during the handshake phase to + validate the peer's certificate. It can be moodified prior to the + handshake with addCaCertificate(), addCaCertificates(), and + setCaCertificates(). + + \sa addCaCertificate(), addCaCertificates(), setCaCertificates() +*/ +QList<QSslCertificate> QSslSocket::caCertificates() const +{ + Q_D(const QSslSocket); + return d->configuration.caCertificates; +} + +/*! + Searches all files in the \a path for certificates with the + specified \a encoding and adds them to the default CA certificate + database. \a path can be an explicit file, or it can contain + wildcards in the format specified by \a syntax. Returns true if + any CA certificates are added to the default database. + + Each SSL socket's CA certificate database is initialized to the + default CA certificate database. + + \sa defaultCaCertificates(), addCaCertificates(), addDefaultCaCertificate() +*/ +bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding, + QRegExp::PatternSyntax syntax) +{ + return QSslSocketPrivate::addDefaultCaCertificates(path, encoding, syntax); +} + +/*! + Adds \a certificate to the default CA certificate database. Each + SSL socket's CA certificate database is initialized to the default + CA certificate database. + + \sa defaultCaCertificates(), addCaCertificates() +*/ +void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate) +{ + QSslSocketPrivate::addDefaultCaCertificate(certificate); +} + +/*! + Adds \a certificates to the default CA certificate database. Each + SSL socket's CA certificate database is initialized to the default + CA certificate database. + + \sa defaultCaCertificates(), addCaCertificates() +*/ +void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certificates) +{ + QSslSocketPrivate::addDefaultCaCertificates(certificates); +} + +/*! + Sets the default CA certificate database to \a certificates. The + default CA certificate database is originally set to your system's + default CA certificate database. If no system default database is + found, Qt will provide its own default database. You can override + the default CA certificate database with your own CA certificate + database using this function. + + Each SSL socket's CA certificate database is initialized to the + default CA certificate database. + + \sa addDefaultCaCertificate() +*/ +void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certificates) +{ + QSslSocketPrivate::setDefaultCaCertificates(certificates); +} + +/*! + Returns the current default CA certificate database. This database + is originally set to your system's default CA certificate database. + If no system default database is found, Qt will provide its own + default database. You can override the default CA certificate database + with your own CA certificate database using setDefaultCaCertificates(). + + Each SSL socket's CA certificate database is initialized to the + default CA certificate database. + + \sa caCertificates() +*/ +QList<QSslCertificate> QSslSocket::defaultCaCertificates() +{ + return QSslSocketPrivate::defaultCaCertificates(); +} + +/*! + Returns the system default CA certificate database for your + system. This database is normally found in a standard place for + your system. If it is not found there, Qt will provide its own + default CA certificate database. The CA certificate database + returned by this function is used to initialize the database + returned by defaultCaCertificates(). You can replace that database + with your own with setDefaultCaCertificates(). + + \sa caCertificates(), defaultCaCertificates(), setDefaultCaCertificates() +*/ +QList<QSslCertificate> QSslSocket::systemCaCertificates() +{ + QSslSocketPrivate::ensureInitialized(); + return QSslSocketPrivate::systemCaCertificates(); +} + +/*! + Waits until the socket is connected, or \a msecs milliseconds, + whichever happens first. If the connection has been established, + this function returns true; otherwise it returns false. + + \sa QAbstractSocket::waitForConnected() +*/ +bool QSslSocket::waitForConnected(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket) + return false; + bool retVal = d->plainSocket->waitForConnected(msecs); + if (!retVal) { + setSocketState(d->plainSocket->state()); + setSocketError(d->plainSocket->error()); + setErrorString(d->plainSocket->errorString()); + } + return retVal; +} + +/*! + Waits until the socket has completed the SSL handshake and has + emitted encrypted(), or \a msecs milliseconds, whichever comes + first. If encrypted() has been emitted, this function returns + true; otherwise (e.g., the socket is disconnected, or the SSL + handshake fails), false is returned. + + The following example waits up to one second for the socket to be + encrypted: + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 5 + + If msecs is -1, this function will not time out. + + \sa startClientEncryption(), startServerEncryption(), encrypted(), isEncrypted() +*/ +bool QSslSocket::waitForEncrypted(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket || d->connectionEncrypted) + return false; + if (d->mode == UnencryptedMode && !d->autoStartHandshake) + return false; + + QTime stopWatch; + stopWatch.start(); + + if (d->plainSocket->state() != QAbstractSocket::ConnectedState) { + // Wait until we've entered connected state. + if (!d->plainSocket->waitForConnected(msecs)) + return false; + } + + while (!d->connectionEncrypted) { + // Start the handshake, if this hasn't been started yet. + if (d->mode == UnencryptedMode) + startClientEncryption(); + // Loop, waiting until the connection has been encrypted or an error + // occurs. + if (!d->plainSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) + return false; + } + return d->connectionEncrypted; +} + +/*! + \reimp +*/ +bool QSslSocket::waitForReadyRead(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket) + return false; + if (d->mode == UnencryptedMode && !d->autoStartHandshake) + return d->plainSocket->waitForReadyRead(msecs); + + // This function must return true if and only if readyRead() *was* emitted. + // So we initialize "readyReadEmitted" to false and check if it was set to true. + // waitForReadyRead() could be called recursively, so we can't use the same variable + // (the inner waitForReadyRead() may fail, but the outer one still succeeded) + bool readyReadEmitted = false; + bool *previousReadyReadEmittedPointer = d->readyReadEmittedPointer; + d->readyReadEmittedPointer = &readyReadEmitted; + + QTime stopWatch; + stopWatch.start(); + + if (!d->connectionEncrypted) { + // Wait until we've entered encrypted mode, or until a failure occurs. + if (!waitForEncrypted(msecs)) { + d->readyReadEmittedPointer = previousReadyReadEmittedPointer; + return false; + } + } + + if (!d->writeBuffer.isEmpty()) { + // empty our cleartext write buffer first + d->transmit(); + } + + // test readyReadEmitted first because either operation above + // (waitForEncrypted or transmit) may have set it + while (!readyReadEmitted && + d->plainSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) { + } + + d->readyReadEmittedPointer = previousReadyReadEmittedPointer; + return readyReadEmitted; +} + +/*! + \reimp +*/ +bool QSslSocket::waitForBytesWritten(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket) + return false; + if (d->mode == UnencryptedMode) + return d->plainSocket->waitForBytesWritten(msecs); + + QTime stopWatch; + stopWatch.start(); + + if (!d->connectionEncrypted) { + // Wait until we've entered encrypted mode, or until a failure occurs. + if (!waitForEncrypted(msecs)) + return false; + } + if (!d->writeBuffer.isEmpty()) { + // empty our cleartext write buffer first + d->transmit(); + } + + return d->plainSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed())); +} + +/*! + Waits until the socket has disconnected or \a msecs milliseconds, + whichever comes first. If the connection has been disconnected, + this function returns true; otherwise it returns false. + + \sa QAbstractSocket::waitForDisconnected() +*/ +bool QSslSocket::waitForDisconnected(int msecs) +{ + Q_D(QSslSocket); + + // require calling connectToHost() before waitForDisconnected() + if (state() == UnconnectedState) { + qWarning("QSslSocket::waitForDisconnected() is not allowed in UnconnectedState"); + return false; + } + + if (!d->plainSocket) + return false; + if (d->mode == UnencryptedMode) + return d->plainSocket->waitForDisconnected(msecs); + + QTime stopWatch; + stopWatch.start(); + + if (!d->connectionEncrypted) { + // Wait until we've entered encrypted mode, or until a failure occurs. + if (!waitForEncrypted(msecs)) + return false; + } + bool retVal = d->plainSocket->waitForDisconnected(qt_timeout_value(msecs, stopWatch.elapsed())); + if (!retVal) { + setSocketState(d->plainSocket->state()); + setSocketError(d->plainSocket->error()); + setErrorString(d->plainSocket->errorString()); + } + return retVal; +} + +/*! + Returns a list of the last SSL errors that occurred. This is the + same list as QSslSocket passes via the sslErrors() signal. If the + connection has been encrypted with no errors, this function will + return an empty list. + + \sa connectToHostEncrypted() +*/ +QList<QSslError> QSslSocket::sslErrors() const +{ + Q_D(const QSslSocket); + return d->sslErrors; +} + +/*! + Returns true if this platform supports SSL; otherwise, returns + false. If the platform doesn't support SSL, the socket will fail + in the connection phase. +*/ +bool QSslSocket::supportsSsl() +{ + return QSslSocketPrivate::ensureInitialized(); +} + +/*! + Starts a delayed SSL handshake for a client connection. This + function can be called when the socket is in the \l ConnectedState + but still in the \l UnencryptedMode. If it is not yet connected, + or if it is already encrypted, this function has no effect. + + Clients that implement STARTTLS functionality often make use of + delayed SSL handshakes. Most other clients can avoid calling this + function directly by using connectToHostEncrypted() instead, which + automatically performs the handshake. + + \sa connectToHostEncrypted(), startServerEncryption() +*/ +void QSslSocket::startClientEncryption() +{ + Q_D(QSslSocket); + if (d->mode != UnencryptedMode) { + qWarning("QSslSocket::startClientEncryption: cannot start handshake on non-plain connection"); + return; + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::startClientEncryption()"; +#endif + d->mode = SslClientMode; + emit modeChanged(d->mode); + d->startClientEncryption(); +} + +/*! + Starts a delayed SSL handshake for a server connection. This + function can be called when the socket is in the \l ConnectedState + but still in \l UnencryptedMode. If it is not connected or it is + already encrypted, the function has no effect. + + For server sockets, calling this function is the only way to + initiate the SSL handshake. Most servers will call this function + immediately upon receiving a connection, or as a result of having + received a protocol-specific command to enter SSL mode (e.g, the + server may respond to receiving the string "STARTTLS\r\n" by + calling this function). + + The most common way to implement an SSL server is to create a + subclass of QTcpServer and reimplement + QTcpServer::incomingConnection(). The returned socket descriptor + is then passed to QSslSocket::setSocketDescriptor(). + + \sa connectToHostEncrypted(), startClientEncryption() +*/ +void QSslSocket::startServerEncryption() +{ + Q_D(QSslSocket); + if (d->mode != UnencryptedMode) { + qWarning("QSslSocket::startClientEncryption: cannot start handshake on non-plain connection"); + return; + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::startServerEncryption()"; +#endif + d->mode = SslServerMode; + emit modeChanged(d->mode); + d->startServerEncryption(); +} + +/*! + This slot tells QSslSocket to ignore errors during QSslSocket's + handshake phase and continue connecting. If you want to continue + with the connection even if errors occur during the handshake + phase, then you must call this slot, either from a slot connected + to sslErrors(), or before the handshake phase. If you don't call + this slot, either in response to errors or before the handshake, + the connection will be dropped after the sslErrors() signal has + been emitted. + + If there are no errors during the SSL handshake phase (i.e., the + identity of the peer is established with no problems), QSslSocket + will not emit the sslErrors() signal, and it is unnecessary to + call this function. + + Ignoring errors that occur during an SSL handshake should be done + with caution. A fundamental characteristic of secure connections + is that they should be established with an error free handshake. + + \sa sslErrors() +*/ +void QSslSocket::ignoreSslErrors() +{ + Q_D(QSslSocket); + d->ignoreSslErrors = true; +} + +/*! + \internal +*/ +void QSslSocket::connectToHostImplementation(const QString &hostName, quint16 port, + OpenMode openMode) +{ + Q_D(QSslSocket); + if (!d->initialized) + d->init(); + d->initialized = false; + +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::connectToHostImplementation(" + << hostName << "," << port << "," << openMode << ")"; +#endif + if (!d->plainSocket) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "\tcreating internal plain socket"; +#endif + d->createPlainSocket(openMode); + } +#ifndef QT_NO_NETWORKPROXY + d->plainSocket->setProxy(proxy()); +#endif + QIODevice::open(openMode); + d->plainSocket->connectToHost(hostName, port, openMode); + d->cachedSocketDescriptor = d->plainSocket->socketDescriptor(); +} + +/*! + \internal +*/ +void QSslSocket::disconnectFromHostImplementation() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::disconnectFromHostImplementation()"; +#endif + if (!d->plainSocket) + return; + if (d->state == UnconnectedState) + return; + if (d->mode == UnencryptedMode && !d->autoStartHandshake) { + d->plainSocket->disconnectFromHost(); + return; + } + if (d->state <= ConnectingState) { + d->pendingClose = true; + return; + } + + // Perhaps emit closing() + if (d->state != ClosingState) { + d->state = ClosingState; + emit stateChanged(d->state); + } + + if (!d->writeBuffer.isEmpty()) + return; + + if (d->mode == UnencryptedMode) { + d->plainSocket->disconnectFromHost(); + } else { + d->disconnectFromHost(); + } +} + +/*! + \reimp +*/ +qint64 QSslSocket::readData(char *data, qint64 maxlen) +{ + Q_D(QSslSocket); + qint64 readBytes = 0; + + if (d->mode == UnencryptedMode && !d->autoStartHandshake) { + readBytes = d->plainSocket->read(data, maxlen); + } else { + do { + const char *readPtr = d->readBuffer.readPointer(); + int bytesToRead = qMin<int>(maxlen - readBytes, d->readBuffer.nextDataBlockSize()); + ::memcpy(data + readBytes, readPtr, bytesToRead); + readBytes += bytesToRead; + d->readBuffer.free(bytesToRead); + } while (!d->readBuffer.isEmpty() && readBytes < maxlen); + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::readData(" << (void *)data << "," << maxlen << ") ==" << readBytes; +#endif + return readBytes; +} + +/*! + \reimp +*/ +qint64 QSslSocket::writeData(const char *data, qint64 len) +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::writeData(" << (void *)data << "," << len << ")"; +#endif + if (d->mode == UnencryptedMode && !d->autoStartHandshake) + return d->plainSocket->write(data, len); + + char *writePtr = d->writeBuffer.reserve(len); + ::memcpy(writePtr, data, len); + + // make sure we flush to the plain socket's buffer + QMetaObject::invokeMethod(this, "_q_flushWriteBuffer", Qt::QueuedConnection); + + return len; +} + +/*! + \internal +*/ +QSslSocketPrivate::QSslSocketPrivate() + : initialized(false), readyReadEmittedPointer(0), plainSocket(0) +{ + QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); +} + +/*! + \internal +*/ +QSslSocketPrivate::~QSslSocketPrivate() +{ +} + +/*! + \internal +*/ +void QSslSocketPrivate::init() +{ + mode = QSslSocket::UnencryptedMode; + autoStartHandshake = false; + connectionEncrypted = false; + ignoreSslErrors = false; + + readBuffer.clear(); + writeBuffer.clear(); + configuration.peerCertificate.clear(); + configuration.peerCertificateChain.clear(); +} + +/*! + \internal +*/ +QList<QSslCipher> QSslSocketPrivate::defaultCiphers() +{ + QMutexLocker locker(&globalData()->mutex); + return globalData()->config->ciphers; +} + +/*! + \internal +*/ +QList<QSslCipher> QSslSocketPrivate::supportedCiphers() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return globalData()->supportedCiphers; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultCiphers(const QList<QSslCipher> &ciphers) +{ + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->ciphers = ciphers; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers) +{ + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->supportedCiphers = ciphers; +} + +/*! + \internal +*/ +QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return globalData()->config->caCertificates; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &certs) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates = certs; +} + +/*! + \internal +*/ +bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax) +{ + QSslSocketPrivate::ensureInitialized(); + QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax); + if (certs.isEmpty()) + return false; + + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates += certs; + return true; +} + +/*! + \internal +*/ +void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates += cert; +} + +/*! + \internal +*/ +void QSslSocketPrivate::addDefaultCaCertificates(const QList<QSslCertificate> &certs) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates += certs; +} + +/*! + \internal +*/ +QSslConfiguration QSslConfigurationPrivate::defaultConfiguration() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return QSslConfiguration(globalData()->config.data()); +} + +/*! + \internal +*/ +void QSslConfigurationPrivate::setDefaultConfiguration(const QSslConfiguration &configuration) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + if (globalData()->config == configuration.d) + return; // nothing to do + + globalData()->config = const_cast<QSslConfigurationPrivate*>(configuration.d.constData()); +} + +/*! + \internal +*/ +void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPrivate *ptr) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + const QSslConfigurationPrivate *global = globalData()->config.constData(); + + ptr->ref = 1; + ptr->peerCertificate = global->peerCertificate; + ptr->peerCertificateChain = global->peerCertificateChain; + ptr->localCertificate = global->localCertificate; + ptr->privateKey = global->privateKey; + ptr->sessionCipher = global->sessionCipher; + ptr->ciphers = global->ciphers; + ptr->caCertificates = global->caCertificates; + ptr->protocol = global->protocol; + ptr->peerVerifyMode = global->peerVerifyMode; + ptr->peerVerifyDepth = global->peerVerifyDepth; +} + +/*! + \internal +*/ +void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode) +{ + Q_Q(QSslSocket); + q->setOpenMode(openMode); // <- from QIODevice + q->setSocketState(QAbstractSocket::UnconnectedState); + q->setSocketError(QAbstractSocket::UnknownSocketError); + q->setLocalPort(0); + q->setLocalAddress(QHostAddress()); + q->setPeerPort(0); + q->setPeerAddress(QHostAddress()); + q->setPeerName(QString()); + + plainSocket = new QTcpSocket(q); + q->connect(plainSocket, SIGNAL(connected()), + q, SLOT(_q_connectedSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(hostFound()), + q, SLOT(_q_hostFoundSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(disconnected()), + q, SLOT(_q_disconnectedSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + q, SLOT(_q_stateChangedSlot(QAbstractSocket::SocketState)), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(error(QAbstractSocket::SocketError)), + q, SLOT(_q_errorSlot(QAbstractSocket::SocketError)), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(readyRead()), + q, SLOT(_q_readyReadSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(bytesWritten(qint64)), + q, SLOT(_q_bytesWrittenSlot(qint64)), + Qt::DirectConnection); +#ifndef QT_NO_NETWORKPROXY + q->connect(plainSocket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); +#endif + + readBuffer.clear(); + writeBuffer.clear(); + connectionEncrypted = false; + configuration.peerCertificate.clear(); + configuration.peerCertificateChain.clear(); + mode = QSslSocket::UnencryptedMode; + q->setReadBufferSize(readBufferMaxSize); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_connectedSlot() +{ + Q_Q(QSslSocket); + q->setLocalPort(plainSocket->localPort()); + q->setLocalAddress(plainSocket->localAddress()); + q->setPeerPort(plainSocket->peerPort()); + q->setPeerAddress(plainSocket->peerAddress()); + q->setPeerName(plainSocket->peerName()); + cachedSocketDescriptor = plainSocket->socketDescriptor(); + +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_connectedSlot()"; + qDebug() << "\tstate =" << q->state(); + qDebug() << "\tpeer =" << q->peerName() << q->peerAddress() << q->peerPort(); + qDebug() << "\tlocal =" << QHostInfo::fromName(q->localAddress().toString()).hostName() + << q->localAddress() << q->localPort(); +#endif + emit q->connected(); + + if (autoStartHandshake) { + q->startClientEncryption(); + } else if (pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_hostFoundSlot() +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_hostFoundSlot()"; + qDebug() << "\tstate =" << q->state(); +#endif + emit q->hostFound(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_disconnectedSlot() +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_disconnectedSlot()"; + qDebug() << "\tstate =" << q->state(); +#endif + disconnected(); + emit q->disconnected(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_stateChangedSlot(QAbstractSocket::SocketState state) +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_stateChangedSlot(" << state << ")"; +#endif + q->setSocketState(state); + emit q->stateChanged(state); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error) +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_errorSlot(" << error << ")"; + qDebug() << "\tstate =" << q->state(); + qDebug() << "\terrorString =" << q->errorString(); +#endif + q->setSocketError(plainSocket->error()); + q->setErrorString(plainSocket->errorString()); + emit q->error(error); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_readyReadSlot() +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_readyReadSlot() -" << plainSocket->bytesAvailable() << "bytes available"; +#endif + if (mode == QSslSocket::UnencryptedMode) { + if (readyReadEmittedPointer) + *readyReadEmittedPointer = true; + emit q->readyRead(); + return; + } + + transmit(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_bytesWrittenSlot(qint64 written) +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_bytesWrittenSlot(" << written << ")"; +#endif + + if (mode == QSslSocket::UnencryptedMode) + emit q->bytesWritten(written); + else + emit q->encryptedBytesWritten(written); + if (state == QAbstractSocket::ClosingState && writeBuffer.isEmpty()) + q->disconnectFromHost(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_flushWriteBuffer() +{ + Q_Q(QSslSocket); + if (!writeBuffer.isEmpty()) + q->flush(); +} + +QT_END_NAMESPACE + +// For private slots +#define d d_ptr +#include "moc_qsslsocket.cpp" diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h new file mode 100644 index 0000000..b8db654 --- /dev/null +++ b/src/network/ssl/qsslsocket.h @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_H +#define QSSLSOCKET_H + +#include <QtCore/qlist.h> +#include <QtCore/qregexp.h> +#ifndef QT_NO_OPENSSL +# include <QtNetwork/qtcpsocket.h> +# include <QtNetwork/qsslerror.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QDir; +class QSslCipher; +class QSslCertificate; +class QSslConfiguration; + +class QSslSocketPrivate; +class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket +{ + Q_OBJECT +public: + enum SslMode { + UnencryptedMode, + SslClientMode, + SslServerMode + }; + + enum PeerVerifyMode { + VerifyNone, + QueryPeer, + VerifyPeer, + AutoVerifyPeer + }; + + QSslSocket(QObject *parent = 0); + ~QSslSocket(); + + // Autostarting the SSL client handshake. + void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite); + bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState, + OpenMode openMode = ReadWrite); + + SslMode mode() const; + bool isEncrypted() const; + + QSsl::SslProtocol protocol() const; + void setProtocol(QSsl::SslProtocol protocol); + + QSslSocket::PeerVerifyMode peerVerifyMode() const; + void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode); + + int peerVerifyDepth() const; + void setPeerVerifyDepth(int depth); + + // From QIODevice + qint64 bytesAvailable() const; + qint64 bytesToWrite() const; + bool canReadLine() const; + void close(); + bool atEnd() const; + bool flush(); + void abort(); + + // From QAbstractSocket: + void setReadBufferSize(qint64 size); + + // Similar to QIODevice's: + qint64 encryptedBytesAvailable() const; + qint64 encryptedBytesToWrite() const; + + // SSL configuration + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration &config); + + // Certificate & cipher accessors. + void setLocalCertificate(const QSslCertificate &certificate); + void setLocalCertificate(const QString &fileName, QSsl::EncodingFormat format = QSsl::Pem); + QSslCertificate localCertificate() const; + QSslCertificate peerCertificate() const; + QList<QSslCertificate> peerCertificateChain() const; + QSslCipher sessionCipher() const; + + // Private keys, for server sockets. + void setPrivateKey(const QSslKey &key); + void setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm = QSsl::Rsa, + QSsl::EncodingFormat format = QSsl::Pem, + const QByteArray &passPhrase = QByteArray()); + QSslKey privateKey() const; + + // Cipher settings. + QList<QSslCipher> ciphers() const; + void setCiphers(const QList<QSslCipher> &ciphers); + void setCiphers(const QString &ciphers); + static void setDefaultCiphers(const QList<QSslCipher> &ciphers); + static QList<QSslCipher> defaultCiphers(); + static QList<QSslCipher> supportedCiphers(); + + // CA settings. + bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QRegExp::PatternSyntax syntax = QRegExp::FixedString); + void addCaCertificate(const QSslCertificate &certificate); + void addCaCertificates(const QList<QSslCertificate> &certificates); + void setCaCertificates(const QList<QSslCertificate> &certificates); + QList<QSslCertificate> caCertificates() const; + static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QRegExp::PatternSyntax syntax = QRegExp::FixedString); + static void addDefaultCaCertificate(const QSslCertificate &certificate); + static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates); + static void setDefaultCaCertificates(const QList<QSslCertificate> &certificates); + static QList<QSslCertificate> defaultCaCertificates(); + static QList<QSslCertificate> systemCaCertificates(); + + bool waitForConnected(int msecs = 30000); + bool waitForEncrypted(int msecs = 30000); + bool waitForReadyRead(int msecs = 30000); + bool waitForBytesWritten(int msecs = 30000); + bool waitForDisconnected(int msecs = 30000); + + QList<QSslError> sslErrors() const; + + static bool supportsSsl(); + +public Q_SLOTS: + void startClientEncryption(); + void startServerEncryption(); + void ignoreSslErrors(); + +Q_SIGNALS: + void encrypted(); + void peerVerifyError(const QSslError &error); + void sslErrors(const QList<QSslError> &errors); + void modeChanged(QSslSocket::SslMode newMode); + void encryptedBytesWritten(qint64 totalBytes); + +protected Q_SLOTS: + void connectToHostImplementation(const QString &hostName, quint16 port, + OpenMode openMode); + void disconnectFromHostImplementation(); + +protected: + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + +private: + Q_DECLARE_PRIVATE(QSslSocket) + Q_DISABLE_COPY(QSslSocket) + Q_PRIVATE_SLOT(d_func(), void _q_connectedSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_hostFoundSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_disconnectedSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_stateChangedSlot(QAbstractSocket::SocketState)) + Q_PRIVATE_SLOT(d_func(), void _q_errorSlot(QAbstractSocket::SocketError)) + Q_PRIVATE_SLOT(d_func(), void _q_readyReadSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_bytesWrittenSlot(qint64)) + Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer()) + friend class QSslSocketBackendPrivate; +}; + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +#ifndef QT_NO_OPENSSL +Q_DECLARE_METATYPE(QList<QSslError>) +#endif + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp new file mode 100644 index 0000000..6f8cf42 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -0,0 +1,929 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QSSLSOCKET_DEBUG + +#include "qsslsocket_openssl_p.h" +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslsocket.h" +#include "qsslcertificate_p.h" +#include "qsslcipher_p.h" + +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> +#include <QtCore/qdir.h> +#include <QtCore/qdiriterator.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qmutex.h> +#include <QtCore/qthread.h> +#include <QtCore/qurl.h> +#include <QtCore/qvarlengtharray.h> + +static void initNetworkResources() +{ + // Initialize resources + Q_INIT_RESOURCE(network); +} + +QT_BEGIN_NAMESPACE + +// Useful defines +#define SSL_ERRORSTR() QString::fromLocal8Bit(q_ERR_error_string(q_ERR_get_error(), NULL)) + +/* \internal + + From OpenSSL's thread(3) manual page: + + OpenSSL can safely be used in multi-threaded applications provided that at + least two callback functions are set. + + locking_function(int mode, int n, const char *file, int line) is needed to + perform locking on shared data structures. (Note that OpenSSL uses a + number of global data structures that will be implicitly shared + when-whenever ever multiple threads use OpenSSL.) Multi-threaded + applications will crash at random if it is not set. ... + ... + id_function(void) is a function that returns a thread ID. It is not + needed on Windows nor on platforms where getpid() returns a different + ID for each thread (most notably Linux) +*/ +class QOpenSslLocks +{ +public: + inline QOpenSslLocks() + : initLocker(QMutex::Recursive), + locksLocker(QMutex::Recursive) + { + QMutexLocker locker(&locksLocker); + int numLocks = q_CRYPTO_num_locks(); + locks = new QMutex *[numLocks]; + memset(locks, 0, numLocks * sizeof(QMutex *)); + } + inline ~QOpenSslLocks() + { + QMutexLocker locker(&locksLocker); + for (int i = 0; i < q_CRYPTO_num_locks(); ++i) + delete locks[i]; + delete [] locks; + + QSslSocketPrivate::deinitialize(); + } + inline QMutex *lock(int num) + { + QMutexLocker locker(&locksLocker); + QMutex *tmp = locks[num]; + if (!tmp) + tmp = locks[num] = new QMutex(QMutex::Recursive); + return tmp; + } + + QMutex *globalLock() + { + return &locksLocker; + } + + QMutex *initLock() + { + return &initLocker; + } + +private: + QMutex initLocker; + QMutex locksLocker; + QMutex **locks; +}; +Q_GLOBAL_STATIC(QOpenSslLocks, openssl_locks) + +extern "C" { +static void locking_function(int mode, int lockNumber, const char *, int) +{ + QMutex *mutex = openssl_locks()->lock(lockNumber); + + // Lock or unlock it + if (mode & CRYPTO_LOCK) + mutex->lock(); + else + mutex->unlock(); +} +static unsigned long id_function() +{ + return (unsigned long)QThread::currentThreadId(); +} +} // extern "C" + +QSslSocketBackendPrivate::QSslSocketBackendPrivate() + : ssl(0), + ctx(0), + readBio(0), + writeBio(0), + session(0) +{ + // Calls SSL_library_init(). + ensureInitialized(); +} + +QSslSocketBackendPrivate::~QSslSocketBackendPrivate() +{ +} + +QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher) +{ + QSslCipher ciph; + + char buf [256]; + QString descriptionOneLine = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf))); + + QStringList descriptionList = descriptionOneLine.split(QLatin1String(" "), QString::SkipEmptyParts); + if (descriptionList.size() > 5) { + // ### crude code. + ciph.d->isNull = false; + ciph.d->name = descriptionList.at(0); + + QString protoString = descriptionList.at(1); + ciph.d->protocolString = protoString; + ciph.d->protocol = QSsl::UnknownProtocol; + if (protoString == QLatin1String("SSLv3")) + ciph.d->protocol = QSsl::SslV3; + else if (protoString == QLatin1String("SSLv2")) + ciph.d->protocol = QSsl::SslV2; + else if (protoString == QLatin1String("TLSv1")) + ciph.d->protocol = QSsl::TlsV1; + + if (descriptionList.at(2).startsWith(QLatin1String("Kx="))) + ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3); + if (descriptionList.at(3).startsWith(QLatin1String("Au="))) + ciph.d->authenticationMethod = descriptionList.at(3).mid(3); + if (descriptionList.at(4).startsWith(QLatin1String("Enc="))) + ciph.d->encryptionMethod = descriptionList.at(4).mid(4); + ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export")); + + ciph.d->bits = cipher->strength_bits; + ciph.d->supportedBits = cipher->alg_bits; + + } + return ciph; +} + +// ### This list is shared between all threads, and protected by a +// mutex. Investigate using thread local storage instead. +struct QSslErrorList +{ + QMutex mutex; + QList<QPair<int, int> > errors; +}; +Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList) +static int q_X509Callback(int ok, X509_STORE_CTX *ctx) +{ + if (!ok) { + // Store the error and at which depth the error was detected. + _q_sslErrorList()->errors << qMakePair<int, int>(ctx->error, ctx->error_depth); + } + // Always return OK to allow verification to continue. We're handle the + // errors gracefully after collecting all errors, after verification has + // completed. + return 1; +} + +bool QSslSocketBackendPrivate::initSslContext() +{ + Q_Q(QSslSocket); + + // Create and initialize SSL context. Accept SSLv2, SSLv3 and TLSv1. + bool client = (mode == QSslSocket::SslClientMode); + + bool reinitialized = false; +init_context: + switch (configuration.protocol) { + case QSsl::SslV2: + ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method()); + break; + case QSsl::SslV3: + ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method()); + break; + case QSsl::AnyProtocol: + default: + ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); + break; + case QSsl::TlsV1: + ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method()); + break; + } + if (!ctx) { + // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them + // by re-initializing the library. + if (!reinitialized) { + reinitialized = true; + if (q_SSL_library_init() == 1) + goto init_context; + } + + // ### Bad error code + q->setErrorString(QSslSocket::tr("Error creating SSL context (%1)").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Enable all bug workarounds. + q_SSL_CTX_set_options(ctx, SSL_OP_ALL); + + // Initialize ciphers + QByteArray cipherString; + int first = true; + QList<QSslCipher> ciphers = configuration.ciphers; + if (ciphers.isEmpty()) + ciphers = defaultCiphers(); + foreach (const QSslCipher &cipher, ciphers) { + if (first) + first = false; + else + cipherString.append(":"); + cipherString.append(cipher.name().toLatin1()); + } + + if (!q_SSL_CTX_set_cipher_list(ctx, cipherString.data())) { + // ### Bad error code + q->setErrorString(QSslSocket::tr("Invalid or empty cipher list (%1)").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Add all our CAs to this store. + foreach (const QSslCertificate &caCertificate, q->caCertificates()) + q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); + + // Register a custom callback to get all verification errors. + X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback); + + if (!configuration.localCertificate.isNull()) { + // Require a private key as well. + if (configuration.privateKey.isNull()) { + q->setErrorString(QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(SSL_ERRORSTR())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Load certificate + if (!q_SSL_CTX_use_certificate(ctx, (X509 *)configuration.localCertificate.handle())) { + q->setErrorString(QSslSocket::tr("Error loading local certificate, %1").arg(SSL_ERRORSTR())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Load private key + EVP_PKEY *pkey = q_EVP_PKEY_new(); + if (configuration.privateKey.algorithm() == QSsl::Rsa) + q_EVP_PKEY_assign_RSA(pkey, (RSA *)configuration.privateKey.handle()); + else + q_EVP_PKEY_assign_DSA(pkey, (DSA *)configuration.privateKey.handle()); + if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) { + q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(SSL_ERRORSTR())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Check if the certificate matches the private key. + if (!q_SSL_CTX_check_private_key(ctx)) { + q->setErrorString(QSslSocket::tr("Private key does not certificate public key, %1").arg(SSL_ERRORSTR())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + } + + // Initialize peer verification. + if (configuration.peerVerifyMode == QSslSocket::VerifyNone) { + q_SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0); + } else { + q_SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, q_X509Callback); + } + + // Set verification depth. + if (configuration.peerVerifyDepth != 0) + q_SSL_CTX_set_verify_depth(ctx, configuration.peerVerifyDepth); + + // Create and initialize SSL session + if (!(ssl = q_SSL_new(ctx))) { + // ### Bad error code + q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Clear the session. + q_SSL_clear(ssl); + errorList.clear(); + + // Initialize memory BIOs for encryption and decryption. + readBio = q_BIO_new(q_BIO_s_mem()); + writeBio = q_BIO_new(q_BIO_s_mem()); + if (!readBio || !writeBio) { + // ### Bad error code + q->setErrorString(QSslSocket::tr("Error creating SSL session: %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Assign the bios. + q_SSL_set_bio(ssl, readBio, writeBio); + + if (mode == QSslSocket::SslClientMode) + q_SSL_set_connect_state(ssl); + else + q_SSL_set_accept_state(ssl); + + return true; +} + +/*! + \internal +*/ +void QSslSocketPrivate::deinitialize() +{ + q_CRYPTO_set_id_callback(0); + q_CRYPTO_set_locking_callback(0); +} + +/*! + \internal + + Declared static in QSslSocketPrivate, makes sure the SSL libraries have + been initialized. +*/ +bool QSslSocketPrivate::ensureInitialized() +{ + if (!q_resolveOpenSslSymbols()) + return false; + + // Check if the library itself needs to be initialized. + QMutexLocker locker(openssl_locks()->initLock()); + static int q_initialized = false; + if (!q_initialized) { + q_initialized = true; + + // Initialize resources + initNetworkResources(); + + // Initialize OpenSSL. + q_CRYPTO_set_id_callback(id_function); + q_CRYPTO_set_locking_callback(locking_function); + if (q_SSL_library_init() != 1) + return false; + q_SSL_load_error_strings(); + q_OpenSSL_add_all_algorithms(); + + // Initialize OpenSSL's random seed. + if (!q_RAND_status()) { + struct { + int msec; + int sec; + void *stack; + } randomish; + + int attempts = 500; + do { + if (attempts < 500) { +#ifdef Q_OS_UNIX + struct timespec ts = {0, 33333333}; + nanosleep(&ts, 0); +#else + Sleep(3); +#endif + randomish.msec = attempts; + } + randomish.stack = (void *)&randomish; + randomish.msec = QTime::currentTime().msec(); + randomish.sec = QTime::currentTime().second(); + q_RAND_seed((const char *)&randomish, sizeof(randomish)); + } while (!q_RAND_status() && --attempts); + if (!attempts) + return false; + } + + resetDefaultCiphers(); + setDefaultCaCertificates(systemCaCertificates()); + } + return true; +} + +/*! + \internal + + Declared static in QSslSocketPrivate, backend-dependent loading of + application-wide global ciphers. +*/ +void QSslSocketPrivate::resetDefaultCiphers() +{ + SSL_CTX *myCtx = q_SSL_CTX_new(q_SSLv23_client_method()); + SSL *mySsl = q_SSL_new(myCtx); + + QList<QSslCipher> ciphers; + + STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl); + for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { + if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { + if (cipher->valid) { + QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); + if (!ciph.isNull()) { + if (!ciph.name().toLower().startsWith(QLatin1String("adh"))) + ciphers << ciph; + } + } + } + } + + q_SSL_CTX_free(myCtx); + q_SSL_free(mySsl); + + setDefaultSupportedCiphers(ciphers); + setDefaultCiphers(ciphers); +} + +QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() +{ +#ifdef QQ_OS_UNIX + // Check known locations for the system's default bundle. ### On Windows, + // we should use CAPI to find the bundle, and not rely on default unix + // locations. + const char *standardLocations[] = {"/etc/ssl/certs/", +#if 0 + // KDE uses KConfig for its SSL store, + // but it also stores the bundle at + // this location + "$HOME/.kde/share/apps/kssl/ca-bundle.crt", +#endif + 0}; + const char **it = standardLocations; + QStringList nameFilter; + nameFilter << QLatin1String("*.pem") << QLatin1String("*.crt"); + while (*it) { + if (QDirIterator(QLatin1String(*it), nameFilter).hasNext()) + return certificatesFromPath(QLatin1String(*it)); + ++it; + } +#endif + + // Qt provides a default bundle when we cannot detect the system's default + // bundle. + QFile caBundle(QLatin1String(":/trolltech/network/ssl/qt-ca-bundle.crt")); + if (caBundle.open(QIODevice::ReadOnly | QIODevice::Text)) + return QSslCertificate::fromDevice(&caBundle); + + // Unreachable; return no bundle. + return QList<QSslCertificate>(); +} + +void QSslSocketBackendPrivate::startClientEncryption() +{ + if (!initSslContext()) { + // ### report error: internal OpenSSL failure + return; + } + + // Start connecting. This will place outgoing data in the BIO, so we + // follow up with calling transmit(). + testConnection(); + transmit(); +} + +void QSslSocketBackendPrivate::startServerEncryption() +{ + if (!initSslContext()) { + // ### report error: internal OpenSSL failure + return; + } + + // Start connecting. This will place outgoing data in the BIO, so we + // follow up with calling transmit(). + testConnection(); + transmit(); +} + +/*! + \internal + + Transmits encrypted data between the BIOs and the socket. +*/ +void QSslSocketBackendPrivate::transmit() +{ + Q_Q(QSslSocket); + + // If we don't have any SSL context, don't bother transmitting. + if (!ssl) + return; + + bool transmitting; + do { + transmitting = false; + + // If the connection is secure, we can transfer data from the write + // buffer (in plain text) to the write BIO through SSL_write. + if (connectionEncrypted && !writeBuffer.isEmpty()) { + qint64 totalBytesWritten = 0; + int nextDataBlockSize; + while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) { + int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize); + if (writtenBytes <= 0) { + // ### Better error handling. + q->setErrorString(QSslSocket::tr("Unable to write data: %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return; + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: encrypted" << writtenBytes << "bytes"; +#endif + writeBuffer.free(writtenBytes); + totalBytesWritten += writtenBytes; + } + + if (totalBytesWritten > 0) { + // Don't emit bytesWritten() recursively. + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q->bytesWritten(totalBytesWritten); + emittedBytesWritten = false; + } + } + } + + // Check if we've got any data to be written to the socket. + QVarLengthArray<char, 4096> data; + int pendingBytes; + while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0) { + // Read encrypted data from the write BIO into a buffer. + data.resize(pendingBytes); + int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes); + + // Write encrypted data from the buffer to the socket. + plainSocket->write(data.constData(), encryptedBytesRead); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: wrote" << encryptedBytesRead << "encrypted bytes to the socket"; +#endif + transmitting = true; + } + + // Check if we've got any data to be read from the socket. + if (!connectionEncrypted || !readBufferMaxSize || readBuffer.size() < readBufferMaxSize) + while ((pendingBytes = plainSocket->bytesAvailable()) > 0) { + // Read encrypted data from the socket into a buffer. + data.resize(pendingBytes); + int decryptedBytesRead = plainSocket->read(data.data(), pendingBytes); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: read" << decryptedBytesRead << "encrypted bytes from the socket"; +#endif + // Write encrypted data from the buffer into the read BIO. + q_BIO_write(readBio, data.constData(), decryptedBytesRead); + transmitting = true; + } + + // If the connection isn't secured yet, this is the time to retry the + // connect / accept. + if (!connectionEncrypted) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: testing encryption"; +#endif + if (testConnection()) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: encryption established"; +#endif + connectionEncrypted = true; + transmitting = true; + } else if (plainSocket->state() != QAbstractSocket::ConnectedState) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: connection lost"; +#endif + break; + } else { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: encryption not done yet"; +#endif + } + } + + // If the request is small and the remote host closes the transmission + // after sending, there's a chance that testConnection() will already + // have triggered a shutdown. + if (!ssl) + continue; + + // We always read everything from the SSL decryption buffers, even if + // we have a readBufferMaxSize. There's no point in leaving data there + // just so that readBuffer.size() == readBufferMaxSize. + int readBytes = 0; + data.resize(4096); + ::memset(data.data(), 0, data.size()); + do { + // Don't use SSL_pending(). It's very unreliable. + if ((readBytes = q_SSL_read(ssl, data.data(), data.size())) > 0) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: decrypted" << readBytes << "bytes"; +#endif + char *ptr = readBuffer.reserve(readBytes); + ::memcpy(ptr, data.data(), readBytes); + + if (readyReadEmittedPointer) + *readyReadEmittedPointer = true; + emit q->readyRead(); + transmitting = true; + continue; + } + + // Error. + switch (q_SSL_get_error(ssl, readBytes)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + // Out of data. + break; + case SSL_ERROR_ZERO_RETURN: + // The remote host closed the connection. +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: remote disconnect"; +#endif + plainSocket->disconnectFromHost(); + break; + default: + // ### Handle errors better. + q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + break; + } + } while (ssl && readBytes > 0); + } while (ssl && ctx && transmitting); +} + +static 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; +} + +bool QSslSocketBackendPrivate::testConnection() +{ + Q_Q(QSslSocket); + + // Check if the connection has been established. Get all errors from the + // verification stage. + _q_sslErrorList()->mutex.lock(); + _q_sslErrorList()->errors.clear(); + int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl); + + const QList<QPair<int, int> > &lastErrors = _q_sslErrorList()->errors; + for (int i = 0; i < lastErrors.size(); ++i) { + const QPair<int, int> ¤tError = lastErrors.at(i); + // Initialize the peer certificate chain in order to find which certificate caused this error + if (configuration.peerCertificateChain.isEmpty()) + configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); + emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.first, + configuration.peerCertificateChain.value(currentError.second))); + if (q->state() != QAbstractSocket::ConnectedState) + break; + } + + errorList << lastErrors; + _q_sslErrorList()->mutex.unlock(); + + // Connection aborted during handshake phase. + if (q->state() != QAbstractSocket::ConnectedState) + return false; + + // Check if we're encrypted or not. + if (result <= 0) { + switch (q_SSL_get_error(ssl, result)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + // The handshake is not yet complete. + break; + default: + // ### Handle errors better + q->setErrorString(QSslSocket::tr("Error during SSL handshake: %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::testConnection: error!" << q->errorString(); +#endif + emit q->error(QAbstractSocket::SslHandshakeFailedError); + q->abort(); + } + return false; + } + + // Store the peer certificate and chain. For clients, the peer certificate + // chain includes the peer certificate; for servers, it doesn't. Both the + // peer certificate and the chain may be empty if the peer didn't present + // any certificate. + if (configuration.peerCertificateChain.isEmpty()) + configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); + X509 *x509 = q_SSL_get_peer_certificate(ssl); + configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); + q_X509_free(x509); + + // Start translating errors. + QList<QSslError> errors; + bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer + || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer + && mode == QSslSocket::SslClientMode); + + // Check the peer certificate itself. First try the subject's common name + // (CN) as a wildcard, then try all alternate subject name DNS entries the + // same way. + if (!configuration.peerCertificate.isNull()) { + // but only if we're a client connecting to a server + // if we're the server, don't check CN + if (mode == QSslSocket::SslClientMode) { + QString peerName = q->peerName(); + QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); + + QRegExp regexp(commonName, Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.exactMatch(peerName)) { + bool matched = false; + foreach (QString altName, configuration.peerCertificate + .alternateSubjectNames().values(QSsl::DnsEntry)) { + regexp.setPattern(altName); + if (regexp.exactMatch(peerName)) { + matched = true; + break; + } + } + if (!matched) { + // No matches in common names or alternate names. + QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + } + } else { + // No peer certificate presented. Report as error if the socket + // expected one. + if (doVerifyPeer) { + QSslError error(QSslError::NoPeerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + + // Translate errors from the error list into QSslErrors. + for (int i = 0; i < errorList.size(); ++i) { + const QPair<int, int> &errorAndDepth = errorList.at(i); + int err = errorAndDepth.first; + int depth = errorAndDepth.second; + errors << _q_OpenSSL_to_QSslError(err, configuration.peerCertificateChain.value(depth)); + } + + if (!errors.isEmpty()) { + sslErrors = errors; + emit q->sslErrors(errors); + if (doVerifyPeer && !ignoreSslErrors) { + q->setErrorString(sslErrors.first().errorString()); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); + emit q->error(QAbstractSocket::SslHandshakeFailedError); + plainSocket->disconnectFromHost(); + return false; + } + } else { + sslErrors.clear(); + } + + // if we have a max read buffer size, reset the plain socket's to 1k + if (readBufferMaxSize) + plainSocket->setReadBufferSize(1024); + + connectionEncrypted = true; + emit q->encrypted(); + if (autoStartHandshake && pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } + return true; +} + +void QSslSocketBackendPrivate::disconnectFromHost() +{ + if (ssl) { + q_SSL_shutdown(ssl); + transmit(); + } + plainSocket->disconnectFromHost(); +} + +void QSslSocketBackendPrivate::disconnected() +{ + if (ssl) { + q_SSL_free(ssl); + ssl = 0; + } + if (ctx) { + q_SSL_CTX_free(ctx); + ctx = 0; + } +} + +QSslCipher QSslSocketBackendPrivate::sessionCipher() const +{ + if (!ssl || !ctx) + return QSslCipher(); + SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); + return sessionCipher ? QSslCipher_from_SSL_CIPHER(sessionCipher) : QSslCipher(); +} + +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; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h new file mode 100644 index 0000000..b3be42a --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_OPENSSL_P_H +#define QSSLSOCKET_OPENSSL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslsocket_p.h" + +#ifdef Q_OS_WIN +#include <windows.h> +#if defined(OCSP_RESPONSE) +#undef OCSP_RESPONSE +#endif +#endif + +#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/pkcs12.h> +#include <openssl/pkcs7.h> +#include <openssl/rand.h> +#include <openssl/ssl.h> +#include <openssl/stack.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/x509_vfy.h> + +QT_BEGIN_NAMESPACE + +class QSslSocketBackendPrivate : public QSslSocketPrivate +{ + Q_DECLARE_PUBLIC(QSslSocket) +public: + QSslSocketBackendPrivate(); + virtual ~QSslSocketBackendPrivate(); + + // SSL context + bool initSslContext(); + SSL *ssl; + SSL_CTX *ctx; + BIO *readBio; + BIO *writeBio; + SSL_SESSION *session; + X509_STORE *certificateStore; + X509_STORE_CTX *certificateStoreCtx; + QList<QPair<int, int> > errorList; + + // Platform specific functions + void startClientEncryption(); + void startServerEncryption(); + void transmit(); + bool testConnection(); + void disconnectFromHost(); + void disconnected(); + QSslCipher sessionCipher() const; + + static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); + static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp new file mode 100644 index 0000000..e09e764 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -0,0 +1,650 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslsocket_openssl_symbols_p.h" + +#include <QtCore/qlibrary.h> +#include <QtCore/qmutex.h> +#include <private/qmutexpool_p.h> +#include <QtCore/qdatetime.h> +#if defined(Q_OS_UNIX) +#include <QtCore/qdir.h> +#endif + +QT_BEGIN_NAMESPACE + +/* + Note to maintainer: + ------------------- + + We load OpenSSL symbols dynamically. Because symbols are known to + disappear, and signatures sometimes change, between releases, we need to + be careful about how this is done. To ensure we don't end up dereferencing + null function pointers, and continue running even if certain functions are + missing, we define helper functions for each of the symbols we load from + OpenSSL, all prefixed with "q_" (declared in + qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect + directly, we call q_SSL_connect, which is a function that checks if the + actual SSL_connect fptr is null, and returns a failure if it is, or calls + SSL_connect if it isn't. + + This requires a somewhat tedious process of declaring each function we + want to call in OpenSSL thrice: once with the q_, in _p.h, once using the + DEFINEFUNC macros below, and once in the function that actually resolves + the symbols, below the DEFINEFUNC declarations below. + + There's one DEFINEFUNC macro declared for every number of arguments + exposed by OpenSSL (feel free to extend when needed). The easiest thing to + do is to find an existing entry that matches the arg count of the function + you want to import, and do the same. + + The first macro arg is the function return type. The second is the + verbatim name of the function/symbol. Then follows a list of N pairs of + argument types with a variable name, and just the variable name (char *a, + a, char *b, b, etc). Finally there's two arguments - a suitable return + statement for the error case (for an int function, return 0 or return -1 + is usually right). Then either just "return" or DUMMYARG, the latter being + for void functions. + + Note: Take into account that these macros and declarations are processed + at compile-time, and the result depends on the OpenSSL headers the + compiling host has installed, but the symbols are resolved at run-time, + possibly with a different version of OpenSSL. +*/ + +#ifdef SSLEAY_MACROS +DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return 0, return) +#endif +DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return 0, return) +DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return) +DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return) +DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return) +DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return) +DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return 0, return) +DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return) +DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return) +DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return) +DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return) +DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG) +DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG) +DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG) +DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG) +#if OPENSSL_VERSION_NUMBER < 0x00908000L +DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, unsigned char **b, b, long c, c, return 0, return) +#else // 0.9.8 broke SC and BC by changing this signature. +DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return 0, return) +#endif +DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, return) +DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return) +DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG) +DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return 0, return) +DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return 0, return) +DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return) +DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return 0, return) +DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return) +#ifdef SSLEAY_MACROS +DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) +DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) +#else +DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) +DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) +#endif +DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return) +DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return) +DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG) +DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return) +DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) +DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG) +DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) +DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) +DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return) +DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return) +DEFINEFUNC3(char *, SSL_CIPHER_description, SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return) +DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return) +#else +DEFINEFUNC(int, SSL_CTX_check_private_key, SSL_CTX *a, a, return -1, return) +#endif +DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return) +DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG) +DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return 0, return) +DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return) +DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return) +DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG) +DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG) +DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return) +DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) +DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return) +DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return) +DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) +DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return 0, return) +#else +DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, SSL *a, a, return 0, return) +#endif +DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return) +DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return) +DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return 0, return) +DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) +#else +DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return) +#endif +DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return) +DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return 0, return) +DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return) +DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG) +DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG) +DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG) +DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return) +DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return) +DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return) +#ifndef SSLEAY_MACROS +DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return 0, return) +#endif +DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return 0, return) +DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG) +DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return 0, return) +DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return) +DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return 0, return) +DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return 0, return) +DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return 0, return) +DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return) +DEFINEFUNC3(char *, X509_NAME_oneline, X509_NAME *a, a, char *b, b, int c, c, return 0, return) +DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return 0, return) +DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG) +DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return) +DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG) +DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return) +DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return) +DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return 0, return) +#ifdef SSLEAY_MACROS +DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return) +DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return) +#endif +DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG) + +#define RESOLVEFUNC(func) \ + if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \ + && !(_q_##func = _q_PTR_##func(libs.second->resolve(#func)))) \ + qWarning("QSslSocket: cannot resolve "#func); + +#if !defined QT_LINKED_OPENSSL + +#ifdef QT_NO_LIBRARY +bool q_resolveOpenSslSymbols() +{ + qWarning("QSslSocket: unable to resolve symbols. " + "QT_NO_LIBRARY is defined which means runtime resolving of " + "libraries won't work."); + qWarning("Either compile Qt statically or with support for runtime resolving " + "of libraries."); + return false; +} +#else + +# ifdef Q_OS_UNIX +static bool libGreaterThan(const QString &lhs, const QString &rhs) +{ + QStringList lhsparts = lhs.split(QLatin1Char('.')); + QStringList rhsparts = rhs.split(QLatin1Char('.')); + Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1); + + for (int i = 1; i < rhsparts.count(); ++i) { + if (lhsparts.count() <= i) + // left hand side is shorter, so it's less than rhs + return false; + + bool ok = false; + int b = 0; + int a = lhsparts.at(i).toInt(&ok); + if (ok) + b = rhsparts.at(i).toInt(&ok); + if (ok) { + // both toInt succeeded + if (a == b) + continue; + return a > b; + } else { + // compare as strings; + if (lhsparts.at(i) == rhsparts.at(i)) + continue; + return lhsparts.at(i) > rhsparts.at(i); + } + } + + // they compared strictly equally so far + // lhs cannot be less than rhs + return true; +} + +static QStringList findAllLibSsl() +{ + QStringList paths; +# ifdef Q_OS_DARWIN + paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH")) + .split(QLatin1Char(':'), QString::SkipEmptyParts); +# else + paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH")) + .split(QLatin1Char(':'), QString::SkipEmptyParts); +# endif + paths << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib"); + + QStringList foundSsls; + foreach (const QString &path, paths) { + QDir dir = QDir(path); + QStringList entryList = dir.entryList(QStringList() << QLatin1String("libssl.*"), QDir::Files); + + qSort(entryList.begin(), entryList.end(), libGreaterThan); + foreach (const QString &entry, entryList) + foundSsls << path + QLatin1Char('/') + entry; + } + + return foundSsls; +} +# endif + +static QPair<QLibrary*, QLibrary*> loadOpenSsl() +{ + QPair<QLibrary*,QLibrary*> pair; + pair.first = 0; + pair.second = 0; + +# ifdef Q_OS_WIN + QLibrary *ssleay32 = new QLibrary(QLatin1String("ssleay32")); + if (!ssleay32->load()) { + // Cannot find ssleay32.dll + delete ssleay32; + return pair; + } + + QLibrary *libeay32 = new QLibrary(QLatin1String("libeay32")); + if (!libeay32->load()) { + delete ssleay32; + delete libeay32; + return pair; + } + + pair.first = ssleay32; + pair.second = libeay32; + return pair; + +# elif defined(Q_OS_UNIX) + QLibrary *&libssl = pair.first; + QLibrary *&libcrypto = pair.second; + libssl = new QLibrary; + libcrypto = new QLibrary; + + // Try to find the libssl library on the system. + // + // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that + // is, libssl.so on most Unix systems. However, the .so file isn't present in + // user installations because it's considered a development file. + // + // The right thing to do is to load the library at the major version we know how + // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h) + // + // However, OpenSSL is a well-known case of binary-compatibility breakage. To + // avoid such problems, many system integrators and Linux distributions change + // the soname of the binary, letting the full version number be the soname. So + // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that + // reason, we will search a few common paths (see findAllLibSsl() above) in hopes + // we find one that works. + // + // It is important, however, to try the canonical name and the unversioned name + // without going through the loop. By not specifying a path, we let the system + // dlopen(3) function determine it for us. This will include any DT_RUNPATH or + // DT_RPATH tags on our library header as well as other system-specific search + // paths. See the man page for dlopen(3) on your system for more information. + +#ifdef SHLIB_VERSION_NUMBER + // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER> + libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER)); + libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER)); + if (libssl->load() && libcrypto->load()) { + // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found + return pair; + } else { + libssl->unload(); + libcrypto->unload(); + } +#endif + + // second attempt: find the development files libssl.so and libcrypto.so + libssl->setFileNameAndVersion(QLatin1String("ssl"), -1); + libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1); + if (libssl->load() && libcrypto->load()) { + // libssl.so.0 and libcrypto.so.0 found + return pair; + } else { + libssl->unload(); + libcrypto->unload(); + } + + // third attempt: loop on the most common library paths and find libssl + QStringList sslList = findAllLibSsl(); + foreach (const QString &ssl, sslList) { + QString crypto = ssl; + crypto.replace(QLatin1String("ssl"), QLatin1String("crypto")); + libssl->setFileNameAndVersion(ssl, -1); + libcrypto->setFileNameAndVersion(crypto, -1); + if (libssl->load() && libcrypto->load()) { + // libssl.so.0 and libcrypto.so.0 found + return pair; + } else { + libssl->unload(); + libcrypto->unload(); + } + } + + // failed to load anything + delete libssl; + delete libcrypto; + libssl = libcrypto = 0; + return pair; + +# else + // not implemented for this platform yet + return pair; +# endif +} + +bool q_resolveOpenSslSymbols() +{ + static volatile bool symbolsResolved = false; + static volatile bool triedToResolveSymbols = false; +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_SSL_library_init)); +#endif + if (symbolsResolved) + return true; + if (triedToResolveSymbols) + return false; + triedToResolveSymbols = true; + + QPair<QLibrary *, QLibrary *> libs = loadOpenSsl(); + if (!libs.first || !libs.second) + // failed to load them + return false; + +#ifdef SSLEAY_MACROS + RESOLVEFUNC(ASN1_dup) +#endif + RESOLVEFUNC(ASN1_STRING_data) + RESOLVEFUNC(ASN1_STRING_length) + RESOLVEFUNC(BIO_ctrl) + RESOLVEFUNC(BIO_free) + RESOLVEFUNC(BIO_new) + RESOLVEFUNC(BIO_new_mem_buf) + RESOLVEFUNC(BIO_read) + RESOLVEFUNC(BIO_s_mem) + RESOLVEFUNC(BIO_write) + RESOLVEFUNC(BN_num_bits) + RESOLVEFUNC(CRYPTO_free) + RESOLVEFUNC(CRYPTO_num_locks) + RESOLVEFUNC(CRYPTO_set_id_callback) + RESOLVEFUNC(CRYPTO_set_locking_callback) + RESOLVEFUNC(DSA_free) + RESOLVEFUNC(ERR_error_string) + RESOLVEFUNC(ERR_get_error) + RESOLVEFUNC(EVP_des_ede3_cbc) + RESOLVEFUNC(EVP_PKEY_assign) + RESOLVEFUNC(EVP_PKEY_free) + RESOLVEFUNC(EVP_PKEY_get1_DSA) + RESOLVEFUNC(EVP_PKEY_get1_RSA) + RESOLVEFUNC(EVP_PKEY_new) + RESOLVEFUNC(EVP_PKEY_type) + RESOLVEFUNC(OBJ_nid2sn) + RESOLVEFUNC(OBJ_obj2nid) +#ifdef SSLEAY_MACROS // ### verify + RESOLVEFUNC(PEM_ASN1_read_bio) +#else + RESOLVEFUNC(PEM_read_bio_DSAPrivateKey) + RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) + RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) + RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) +#endif + RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY) + RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) + RESOLVEFUNC(RAND_seed) + RESOLVEFUNC(RAND_status) + RESOLVEFUNC(RSA_free) + RESOLVEFUNC(sk_free) + RESOLVEFUNC(sk_num) + RESOLVEFUNC(sk_value) + RESOLVEFUNC(SSL_CIPHER_description) + RESOLVEFUNC(SSL_CTX_check_private_key) + RESOLVEFUNC(SSL_CTX_ctrl) + RESOLVEFUNC(SSL_CTX_free) + RESOLVEFUNC(SSL_CTX_new) + RESOLVEFUNC(SSL_CTX_set_cipher_list) + RESOLVEFUNC(SSL_CTX_set_default_verify_paths) + RESOLVEFUNC(SSL_CTX_set_verify) + RESOLVEFUNC(SSL_CTX_set_verify_depth) + RESOLVEFUNC(SSL_CTX_use_certificate) + RESOLVEFUNC(SSL_CTX_use_certificate_file) + RESOLVEFUNC(SSL_CTX_use_PrivateKey) + RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey) + RESOLVEFUNC(SSL_CTX_use_PrivateKey_file) + RESOLVEFUNC(SSL_accept) + RESOLVEFUNC(SSL_clear) + RESOLVEFUNC(SSL_connect) + RESOLVEFUNC(SSL_free) + RESOLVEFUNC(SSL_get_ciphers) + RESOLVEFUNC(SSL_get_current_cipher) + RESOLVEFUNC(SSL_get_error) + RESOLVEFUNC(SSL_get_peer_cert_chain) + RESOLVEFUNC(SSL_get_peer_certificate) + RESOLVEFUNC(SSL_get_verify_result) + RESOLVEFUNC(SSL_library_init) + RESOLVEFUNC(SSL_load_error_strings) + RESOLVEFUNC(SSL_new) + RESOLVEFUNC(SSL_read) + RESOLVEFUNC(SSL_set_accept_state) + RESOLVEFUNC(SSL_set_bio) + RESOLVEFUNC(SSL_set_connect_state) + RESOLVEFUNC(SSL_shutdown) + RESOLVEFUNC(SSL_write) + RESOLVEFUNC(SSLv2_client_method) + RESOLVEFUNC(SSLv3_client_method) + RESOLVEFUNC(SSLv23_client_method) + RESOLVEFUNC(TLSv1_client_method) + RESOLVEFUNC(SSLv2_server_method) + RESOLVEFUNC(SSLv3_server_method) + RESOLVEFUNC(SSLv23_server_method) + RESOLVEFUNC(TLSv1_server_method) + RESOLVEFUNC(X509_NAME_oneline) + RESOLVEFUNC(X509_PUBKEY_get) + RESOLVEFUNC(X509_STORE_free) + RESOLVEFUNC(X509_STORE_new) + RESOLVEFUNC(X509_STORE_add_cert) + RESOLVEFUNC(X509_STORE_CTX_free) + RESOLVEFUNC(X509_STORE_CTX_init) + RESOLVEFUNC(X509_STORE_CTX_new) + RESOLVEFUNC(X509_STORE_CTX_set_purpose) + RESOLVEFUNC(X509_cmp) +#ifndef SSLEAY_MACROS + RESOLVEFUNC(X509_dup) +#endif + RESOLVEFUNC(X509_EXTENSION_get_object) + RESOLVEFUNC(X509_free) + RESOLVEFUNC(X509_get_ext) + RESOLVEFUNC(X509_get_ext_count) + RESOLVEFUNC(X509_get_ext_d2i) + RESOLVEFUNC(X509_get_issuer_name) + RESOLVEFUNC(X509_get_subject_name) + RESOLVEFUNC(X509_verify_cert) + RESOLVEFUNC(d2i_X509) + RESOLVEFUNC(i2d_X509) +#ifdef SSLEAY_MACROS + RESOLVEFUNC(i2d_DSAPrivateKey) + RESOLVEFUNC(i2d_RSAPrivateKey) + RESOLVEFUNC(d2i_DSAPrivateKey) + RESOLVEFUNC(d2i_RSAPrivateKey) +#endif + RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf) + RESOLVEFUNC(OPENSSL_add_all_algorithms_conf) + symbolsResolved = true; + delete libs.first; + delete libs.second; + return true; +} +#endif // QT_NO_LIBRARY + +#else // !defined QT_LINKED_OPENSSL + +bool q_resolveOpenSslSymbols() +{ +#ifdef QT_NO_SSL + return false; +#endif + return true; +} +#endif // !defined QT_LINKED_OPENSSL + +//============================================================================== +// contributed by Jay Case of Sarvega, Inc.; http://sarvega.com/ +// Based on X509_cmp_time() for intitial buffer hacking. +//============================================================================== +QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime) +{ + char lBuffer[24]; + char *pBuffer = lBuffer; + + size_t lTimeLength = aTime->length; + char *pString = (char *) aTime->data; + + if (aTime->type == V_ASN1_UTCTIME) { + if ((lTimeLength < 11) || (lTimeLength > 17)) + return QDateTime(); + + memcpy(pBuffer, pString, 10); + pBuffer += 10; + pString += 10; + } else { + if (lTimeLength < 13) + return QDateTime(); + + memcpy(pBuffer, pString, 12); + pBuffer += 12; + pString += 12; + } + + if ((*pString == 'Z') || (*pString == '-') || (*pString == '+')) { + *pBuffer++ = '0'; + *pBuffer++ = '0'; + } else { + *pBuffer++ = *pString++; + *pBuffer++ = *pString++; + // Skip any fractional seconds... + if (*pString == '.') { + pString++; + while ((*pString >= '0') && (*pString <= '9')) + pString++; + } + } + + *pBuffer++ = 'Z'; + *pBuffer++ = '\0'; + + time_t lSecondsFromUCT; + if (*pString == 'Z') { + lSecondsFromUCT = 0; + } else { + if ((*pString != '+') && (*pString != '-')) + return QDateTime(); + + lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60; + lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0'); + lSecondsFromUCT *= 60; + if (*pString == '-') + lSecondsFromUCT = -lSecondsFromUCT; + } + + tm lTime; + lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0'); + lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0'); + lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0'); + lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0'); + lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1; + lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0'); + if (lTime.tm_year < 50) + lTime.tm_year += 100; // RFC 2459 + + QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday); + QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec); + QDateTime result(resDate, resTime, Qt::UTC); + result = result.addSecs(lSecondsFromUCT); + return result; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h new file mode 100644 index 0000000..c6ae91e --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H +#define QSSLSOCKET_OPENSSL_SYMBOLS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslsocket_openssl_p.h" + +QT_BEGIN_NAMESPACE + +#define DUMMYARG + +#if !defined QT_LINKED_OPENSSL +// **************** Shared declarations ****************** +// ret func(arg) + +# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a); \ + } + +// ret func(arg1, arg2) +# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func);\ + err; \ + } \ + funcret _q_##func(a, b); \ + } + +// ret func(arg1, arg2, arg3) +# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c); \ + } + +// ret func(arg1, arg2, arg3, arg4) +# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg5) +# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg6) +# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e, f); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7) +# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e, f, g); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9) +# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \ + if (_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e, f, g, h, i); \ + } +// **************** Shared declarations ****************** + +#else // !defined QT_LINKED_OPENSSL + +// **************** Static declarations ****************** + +// ret func(arg) +# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + ret q_##func(arg) { funcret func(a); } + +// ret func(arg1, arg2) +# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ + ret q_##func(arg1, arg2) { funcret func(a, b); } + +// ret func(arg1, arg2, arg3) +# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ + ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); } + +// ret func(arg1, arg2, arg3, arg4) +# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); } + +// ret func(arg1, arg2, arg3, arg4, arg5) +# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); } + +// ret func(arg1, arg2, arg3, arg4, arg6) +# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7) +# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9) +# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); } + +// **************** Static declarations ****************** + +#endif // !defined QT_LINKED_OPENSSL + +bool q_resolveOpenSslSymbols(); +unsigned char * q_ASN1_STRING_data(ASN1_STRING *a); +int q_ASN1_STRING_length(ASN1_STRING *a); +long q_BIO_ctrl(BIO *a, int b, long c, void *d); +int q_BIO_free(BIO *a); +BIO *q_BIO_new(BIO_METHOD *a); +BIO *q_BIO_new_mem_buf(void *a, int b); +int q_BIO_read(BIO *a, void *b, int c); +BIO_METHOD *q_BIO_s_mem(); +int q_BIO_write(BIO *a, const void *b, int c); +int q_BN_num_bits(const BIGNUM *a); +int q_CRYPTO_num_locks(); +void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int)); +void q_CRYPTO_set_id_callback(unsigned long (*a)()); +void q_CRYPTO_free(void *a); +void q_DSA_free(DSA *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c); +#else +X509 *q_d2i_X509(X509 **a, unsigned char **b, long c); +#endif +char *q_ERR_error_string(unsigned long a, char *b); +unsigned long q_ERR_get_error(); +const EVP_CIPHER *q_EVP_des_ede3_cbc(); +int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c); +void q_EVP_PKEY_free(EVP_PKEY *a); +RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a); +DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a); +int q_EVP_PKEY_type(int a); +EVP_PKEY *q_EVP_PKEY_new(); +int q_i2d_X509(X509 *a, unsigned char **b); +const char *q_OBJ_nid2sn(int a); +int q_OBJ_obj2nid(const ASN1_OBJECT *a); +#ifdef SSLEAY_MACROS +// ### verify +void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_password_cb *e, + void *f); +// ### ditto for write +#else +DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d); +RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d); +int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d, + int e, pem_password_cb *f, void *g); +int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d, + int e, pem_password_cb *f, void *g); +#endif +DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d); +RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d); +int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b); +int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b); +void q_RAND_seed(const void *a, int b); +int q_RAND_status(); +void q_RSA_free(RSA *a); +void q_sk_free(STACK *a); +int q_sk_num(STACK *a); +char * q_sk_value(STACK *a, int b); +int q_SSL_accept(SSL *a); +int q_SSL_clear(SSL *a); +char *q_SSL_CIPHER_description(SSL_CIPHER *a, char *b, int c); +int q_SSL_connect(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +int q_SSL_CTX_check_private_key(const SSL_CTX *a); +#else +int q_SSL_CTX_check_private_key(SSL_CTX *a); +#endif +long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d); +void q_SSL_CTX_free(SSL_CTX *a); +SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a); +int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b); +int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a); +void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *)); +void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b); +int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b); +int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c); +int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b); +int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b); +int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c); +void q_SSL_free(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a); +#else +STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(SSL *a); +#endif +SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); +int q_SSL_get_error(SSL *a, int b); +STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); +X509 *q_SSL_get_peer_certificate(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +long q_SSL_get_verify_result(const SSL *a); +#else +long q_SSL_get_verify_result(SSL *a); +#endif +int q_SSL_library_init(); +void q_SSL_load_error_strings(); +SSL *q_SSL_new(SSL_CTX *a); +int q_SSL_read(SSL *a, void *b, int c); +void q_SSL_set_bio(SSL *a, BIO *b, BIO *c); +void q_SSL_set_accept_state(SSL *a); +void q_SSL_set_connect_state(SSL *a); +int q_SSL_shutdown(SSL *a); +SSL_METHOD *q_SSLv2_client_method(); +SSL_METHOD *q_SSLv3_client_method(); +SSL_METHOD *q_SSLv23_client_method(); +SSL_METHOD *q_TLSv1_client_method(); +SSL_METHOD *q_SSLv2_server_method(); +SSL_METHOD *q_SSLv3_server_method(); +SSL_METHOD *q_SSLv23_server_method(); +SSL_METHOD *q_TLSv1_server_method(); +int q_SSL_write(SSL *a, const void *b, int c); +int q_X509_cmp(X509 *a, X509 *b); +#ifdef SSLEAY_MACROS +void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x); +#define q_X509_dup(x509) (X509 *)q_ASN1_dup((i2d_of_void *)q_i2d_X509, \ + (d2i_of_void *)q_d2i_X509,(char *)x509) +#else +X509 *q_X509_dup(X509 *a); +#endif +ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a); +void q_X509_free(X509 *a); +X509_EXTENSION *q_X509_get_ext(X509 *a, int b); +int q_X509_get_ext_count(X509 *a); +void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d); +X509_NAME *q_X509_get_issuer_name(X509 *a); +X509_NAME *q_X509_get_subject_name(X509 *a); +int q_X509_verify_cert(X509_STORE_CTX *ctx); +char *q_X509_NAME_oneline(X509_NAME *a, char *b, int c); +EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a); +void q_X509_STORE_free(X509_STORE *store); +X509_STORE *q_X509_STORE_new(); +int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x); +void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx); +int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, + X509 *x509, STACK_OF(X509) *chain); +X509_STORE_CTX *q_X509_STORE_CTX_new(); +int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose); + +#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) +#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) +#ifdef SSLEAY_MACROS +int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); +int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp); +RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length); +DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); +#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \ + (RSA *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u) +#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \ + (DSA *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u) +#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#endif +#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL) +#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st) +#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i) +#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st)) +#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i)) +#define q_sk_X509_num(st) q_SKM_sk_num(X509, (st)) +#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i)) +#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num(SSL_CIPHER, (st)) +#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i)) +#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \ + q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509) +#define q_X509_get_notAfter(x) X509_get_notAfter(x) +#define q_X509_get_notBefore(x) X509_get_notBefore(x) +#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\ + (char *)(rsa)) +#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\ + (char *)(dsa)) +#ifdef OPENSSL_LOAD_CONF +#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf() +#else +#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_noconf() +#endif +void q_OPENSSL_add_all_algorithms_noconf(); +void q_OPENSSL_add_all_algorithms_conf(); + +// Helper function +class QDateTime; +QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime); + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h new file mode 100644 index 0000000..825df46 --- /dev/null +++ b/src/network/ssl/qsslsocket_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_P_H +#define QSSLSOCKET_P_H + +#include "qsslsocket.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtcpsocket_p.h> +#include "qsslkey.h" +#include "qsslconfiguration_p.h" + +#include <QtCore/qstringlist.h> + +#include <private/qringbuffer_p.h> + +QT_BEGIN_NAMESPACE + +class QSslSocketPrivate : public QTcpSocketPrivate +{ + Q_DECLARE_PUBLIC(QSslSocket) +public: + QSslSocketPrivate(); + virtual ~QSslSocketPrivate(); + + void init(); + bool initialized; + + QSslSocket::SslMode mode; + bool autoStartHandshake; + bool connectionEncrypted; + bool ignoreSslErrors; + bool* readyReadEmittedPointer; + + QRingBuffer readBuffer; + QRingBuffer writeBuffer; + + QSslConfigurationPrivate configuration; + QList<QSslError> sslErrors; + + static bool ensureInitialized(); + static void deinitialize(); + static QList<QSslCipher> defaultCiphers(); + static QList<QSslCipher> supportedCiphers(); + static void setDefaultCiphers(const QList<QSslCipher> &ciphers); + static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers); + static void resetDefaultCiphers(); + + static QList<QSslCertificate> defaultCaCertificates(); + static QList<QSslCertificate> systemCaCertificates(); + static void setDefaultCaCertificates(const QList<QSslCertificate> &certs); + static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax); + static void addDefaultCaCertificate(const QSslCertificate &cert); + static void addDefaultCaCertificates(const QList<QSslCertificate> &certs); + + // The socket itself, including private slots. + QTcpSocket *plainSocket; + void createPlainSocket(QIODevice::OpenMode openMode); + void _q_connectedSlot(); + void _q_hostFoundSlot(); + void _q_disconnectedSlot(); + void _q_stateChangedSlot(QAbstractSocket::SocketState); + void _q_errorSlot(QAbstractSocket::SocketError); + void _q_readyReadSlot(); + void _q_bytesWrittenSlot(qint64); + void _q_flushWriteBuffer(); + + // Platform specific functions + virtual void startClientEncryption() = 0; + virtual void startServerEncryption() = 0; + virtual void transmit() = 0; + virtual void disconnectFromHost() = 0; + virtual void disconnected() = 0; + virtual QSslCipher sessionCipher() const = 0; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qt-ca-bundle.crt b/src/network/ssl/qt-ca-bundle.crt new file mode 100644 index 0000000..7755ca0 --- /dev/null +++ b/src/network/ssl/qt-ca-bundle.crt @@ -0,0 +1,1984 @@ +## +## ca-bundle.crt -- Bundle of CA Certificates +## +## This is a bundle of X.509 certificates of public +## Certificate Authorities (CA). +## + +-----BEGIN CERTIFICATE----- +MIICfTCCAeagAwIBAgIEAgAAuDANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSkwJwYD +VQQDEyBCYWx0aW1vcmUgQ3liZXJUcnVzdCBNb2JpbGUgUm9vdDAeFw0wMDA1MTIx +ODIwMDBaFw0yMDA1MTIyMzU5MDBaMGExCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlC +YWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxKTAnBgNVBAMTIEJhbHRpbW9y +ZSBDeWJlclRydXN0IE1vYmlsZSBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCjbbE4Vqz8tVYh3sCQXSZHgsZ9jx+ghY8vu9ThHB3yJB8osC+5pKVvoiIg +ZP6ERzx+K2xparjUwJaOjFINzW9B1L8ErqeBLy2YSNLBlKO1GV1dUWT0jkGwm8At +IqBexthaEmO8EUpeJhId4iYF5g9fIh96X3aUrs9aKA6rRdoiMQIDAQABo0IwQDAd +BgNVHQ4EFgQUyeKPwAImWrbAB+N/lAcY2y6lmnAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEFBQADgYEAUwgLJgl4QnPU7Hp3Rw3j +CzNx764zFE37+v0at1H15JkcBnHXKRnX5hUgUVFGbU/eGEmY0Ph4u3HojQEG1ddk +j5TfR/6ghWk2qS9CemhKEtaLC3BECqQE7yaIwTVxOF0bW0hC8OeUHHCVNKir9avi +eK318FL9m+pCDOjYVL5TZvU= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV +UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL +EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ +BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x +ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg +bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ +j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV +Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG +SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx +JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI +RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw +MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5 +fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i ++DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG +SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN +QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+ +gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGpMQsw +CQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENp +dHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UE +CxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0B +CQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDExODE4NTVaFw0wODExMjgx +ODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMO +U2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0 +IENvLjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx +ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdf +WvnTLnUv2Chi0ZMv/E3Uq4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uK +xBmd9LIO/BZsubEFkoPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBE +zUNKcI5YhZXhTizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F +5X5yP4WdlGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMv +OnNn7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+Legz +ZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvVWlHG4VME +lo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX8ngvYzZAOONG +Dx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn86Oawde3uPclwx12q +gUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsTF7ANUkz+/m9c4pFuHf2k +Ytdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV +UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL +EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ +BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x +ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/ +k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso +LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o +TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG +SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx +JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI +RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3 +MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C +TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5 +WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG +SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR +xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL +B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGpMQsw +CQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENp +dHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UE +CxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0B +CQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAyMjQ2MTZaFw0wODExMjcy +MjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMO +U2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0 +IENvLjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx +ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbV +p9oaBBg5kkp4o4HC9Xd6ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWw +BZoPFflrWXJW8vo5/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl +5WJp3OXuAFK9MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi +3sOP17ihYqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+ +QVCvbK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWogWxyQ +2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6HE3K1GjN +I3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV6YyDfFk/xPEL +553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8PzGn0EdzMzkbzE5q +10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30sPDst2yC7S8xmUJMqbIN +uBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50cnVzdC5u +ZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBsaW1pdHMgbGlh +Yi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBaMIHJMQswCQYDVQQGEwJVUzEU +MBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9D +bGllbnRfQ0FfSW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMq +RW50cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0G +CSqGSIb3DQEBAQUAA4GLADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo +6oT9n3V5z8GKUZSvx1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux +5zDeg7K6PvHViTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zm +AqTmT173iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSC +ARkwggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50 +cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5m +by9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMp +IDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQg +Q2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCyg +KqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9DbGllbnQxLmNybDArBgNV +HRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkxMDEyMTkyNDMwWjALBgNVHQ8E +BAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW/O5bs8qZdIuV6kwwHQYDVR0OBBYE +FMT7nCl7l81MlvzuW7PKmXSLlepMMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7 +pFuPeJoSSJn59DXeDDYHAmsQOokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzz +wy5E97BnRqqS5TvaHBkUODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/a +EkP/TOYGJqibGapEPHayXOw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIzCCAoygAwIBAgIENeHvHjANBgkqhkiG9w0BAQUFADBPMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEuMCwGA1UECxMlRXF1aWZheCBQcmVtaXVtIENl +cnRpZmljYXRlIEF1dGhvcml0eTAeFw05ODA4MjQyMjU0MjNaFw0xODA4MjQyMjU0 +MjNaME8xCzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdFcXVpZmF4MS4wLAYDVQQLEyVF +cXVpZmF4IFByZW1pdW0gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDOoQaOBswIC8GGqN4g1Q0O0Q3En+pq2bPCMkdAb4qI +pAm9OCwd5svmpPM269rrvPxkswf2Lbyqzp8ZSGhK/PWiRX4JEPWPs0lcIwY56hOL +uAvNkR12X9k3oUT7X5DyZ7PNGJlDH3YSawLylYM4Q8L2YjTKyXhdX9LYupr/vhBg +WwIDAQABo4IBCjCCAQYwcQYDVR0fBGowaDBmoGSgYqRgMF4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS4wLAYDVQQLEyVFcXVpZmF4IFByZW1pdW0gQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIw +MTgwODI0MjI1NDIzWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUFe6yKFmrbuX4 +z4uB9CThrj91G5gwHQYDVR0OBBYEFBXusihZq27l+M+LgfQk4a4/dRuYMAwGA1Ud +EwQFMAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEB +BQUAA4GBAL0LnCepA9so3JipS9DRjqeoGlqR4Jzx9xh8LiKeNh/JqLXNRkpu+jUH +G4YI65/iqPmdQS06rlxctl80BOv8KmCw+3TkhellOJbuFcfGd2MSvYpoH6tsfdrK +XBPO6snrCVzFc+cSAdXZUwee4A+W8Iu0u0VIn4bFGVWgy5bFA/xI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT +ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw +MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j +LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo +RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu +WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw +Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK +eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM +zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ +WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN +/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2Vj +dXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0 +NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYD +VQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0G +vxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/ +BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0C +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEX +MBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJl +IGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkw +NjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBq +y/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy +0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1 +E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD +VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv +bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv +b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH +iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS +r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 +04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r +GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 +3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P +lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgICAbYwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD0dURSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RFIEN5YmVyVHJ1 +c3QgU29sdXRpb25zLCBJbmMuMR4wHAYDVQQDExVHVEUgQ3liZXJUcnVzdCBSb290 +IDUwHhcNOTgwODE0MTQ1MDAwWhcNMTMwODE0MjM1OTAwWjBwMQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xHjAcBgNVBAMTFUdURSBDeWJlclRydXN0IFJv +b3QgNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALwSbj+KfHqXAewe +uzlaAvR4RKJIG457SVJ6uHtHs6+Um2+7lvoramVcuByUc76/iQoigO5X/IwFu3Cf +lzkE2qOHXKjlyq/AM5rVN1xLrOSA0KYjYPv9ci6UncfOwgQy73hgXe2thw9FZR48 +mgqavl0dmezn8tHGehfZrZtUln/EfGC/haoVNR1A2hG87FQhKC0joajwzy3N3fx+ +D17hZQdWywe00lboXjHMGGPEhtIthc+Tkqtt/mg5+95zvYb45EZ66p8My/QZ/mO8 +0Sx7iDM29uThnAxTgWAc2i6rlqkWiBNQmbK9Vd8VMH7o5Zj7cH5stQf8/Ea30O03 +ln4y/iECAwEAAaNaMFgwEgYDVR0TAQH/BAgwBgEB/wIBBTAOBgNVHQ8BAf8EBAMC +AQYwFwYDVR0gBBAwDjAMBgoqhkiG+GMBAgEDMBkGA1UdDgQSBBB2CkkhOEyf3vjE +ScdxcZGdMA0GCSqGSIb3DQEBBQUAA4IBAQBBOtQYW9q43iEc4Y4J5fFoNP/elvQH +9ac886xKsZv6kvqb7eYyIapKdsXcTzjl39WG5NXIdn2Y17HNj021kSNsi4rr6nzv +FJTExvAfSi0ycWMrY5EmAgm2gB3t4sy4f9uHY8jh0GwmsTUdQGYQG82VVBgzYewT +T9oT95mvPtDPjqZyorPDBZrJJ32SzH5SjbOrcG2eiZ9N6xp1wpiq1QIW1wyKvyXk +6y28mOlYOBl8uTf+2+KZCHMGx5eDan0QAS8yuRcFSmXmL86+XlOmgumaUwqEdC2D +ysiUFnZflGEo8IWnObvXi9moshMdVAk0JH0ggX1mfqKQdFwQxr3sqxvC +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDnjCCAoagAwIBAgILAgAAAAAA1ni50a8wDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05OTAxMjgxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xv +YmFsU2lnbiBQYXJ0bmVycyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANIs+DKsShJ6N8gpkaWujG4eDsA0M4jlM3EWHHiEaMMYNFAuFj6xlIJPsZqf +APjGETXGaXuYAq0ABohs50wzKACIJ0Yfh7NxdWO8MruI3mYYDlAGk7T2vBQ3MD0i +3z3/dX7ZChrFn7P80KyzCHqJ0wHoAFznSgs9TXsmordiBovaRt2TFz8/WwJLC7aI +IBGSAK27xy7U40Wu9YlafI2krYVkMsAnjMbyioCShiRWWY10aKKDQrOePVBBhm8g +bvb9ztMZ4zLMj+2aXm0fKPVSrG4YXvg90ZLlumwBiEsK8i3eZTMFQqBMqjF2vv2/ +gXj5cRxGXi0VlS0wWY5MQdFiqz0CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgAGMB0G +A1UdDgQWBBRDJI1wFQhiVZxPDEAXXYZeD6JM+zAfBgNVHSMEGDAWgBRge2YaRQ2X +yolQL30EzTSo//z9SzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IB +AQBm7bSIaRGZgiGDrKFti5uErQ8tyB6Mynt+rarUjt4H1p5Fx6W4nAc5YCVVGsBP +GeXPFylJiRg1ZuXrKEBOV8mvs+S4IAWjO5VQkUmUKX0s5YhBpUWIXp2CJ/fS71u1 +T5++/jVlLFVkn+FR2iJhd7pYTo/GeVlZbjCAok+QbiELrdBoOZAQm+0iZW8eETjm +f4zS8zltR9Uh6Op1OkHRrfYWnV0LIb3zH2MGJR3BHzVxLOsgGdXBsOw95W/tAgc/ +E3tmktZEwZj3X1CLelvCb22w0fjldKBAN6MlD+Q9ymQxk5BcMHu5OTGaXkzNuUFP +UOQ9OK7IZtnHO11RR6ybq/Kt +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgILAgAAAAAA1ni4N88wDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MTUxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDEgQ0ExJjAkBgNV +BAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAxIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAvSA1R9Eo1gijEjkjRw29cCFSDlcxlaY0V2vsfkN5 +wwZSSM28taGZvdgfMrzP125ybS53IpCCTkuPmgwBQprZcFm2nR/mY9EMrR1O+IWB ++a7vn6ZSYUR5GnVF4GFWRW1CjD1yy6akErea9dZg0GBQs46mpuy09BLNf6jO77Ph +hTD+csTm53eznlhB1lGDiAfGtmlPNt7RC0g/vdafIXRkbycGPkv9Dqabv6RIV4yQ +7okYCwKBGL5n/lNgiCe6o3M0S1pWtN5zBe2Yll3sSudA/EsJYuvQ4zFPhdF6q1ln +K/uID+uqg701/WEn7GYOQlf3acIM7/xqwm5J2o9BOK5IqQIDAQABo2MwYTAOBgNV +HQ8BAf8EBAMCAAYwHQYDVR0OBBYEFPzgZvZaNZnrQB7SuB5DvJiOH4rDMB8GA1Ud +IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBAJujCETO8pCdcfMyswVqterPKZjeVT6gFn0GekTWr9L6 +E1iM+BzHqx20G+9paJhcCDmP4Pf7SMwh57gz2wWqNCRsSuXpe2Deg7MfCr5BdfzM +MEi3wSYdBDOqtnjtKsu6VpcybvcxlS5G8hTuJ8f3Yom5XFrTOIpk9Te08bM0ctXV +IT1L13iT1zFmNR6j2EdJbxyt4YB/+JgkbHOsDsIadwKjJge3x2tdvILVKkgdY89Q +Mqb7HBhHFQpbDFw4JJoEmKgISF98NIdjqy2NTAB3lBt2uvUWGKMVry+U9ikAdsEV +F9PpN0121MtLKVkkrNpKoOpj3l9Usfrz0UXLxWS0cyE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgILAgAAAAAA1ni4jY0wDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05OTAxMjgxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDIgQ0ExJjAkBgNV +BAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAyIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAkoz+7/RFjhdBbvzYvyFvqwadUsEsAJ0/joW4f0qP +vaBjKspJJ65agvR04lWS/8LRqnmitvrVnYIET8ayxl5jpzq62O7rim+ftrsoQcAi ++05IGgaS17/Xz7nZvThPOw1EblVB/vwJ29i/844h8egStfYTpdPGTJMisAL/7h0M +xKhrT3VoVujcKBJQ96gknS4kOfsJBd7lo2RJIdBofnEwkbFg4Dn0UPh6TZgAa3x5 +uk7OSuK6Nh23xTYVlZxkQupfxLr1QAW+4TpZvYSnGbjeTVNQzgfR0lHT7w2BbObn +bctdfD98zOxPgycl/3BQ9oNZdYQGZlgs3omNAKZJ+aVDdwIDAQABo2MwYTAOBgNV +HQ8BAf8EBAMCAAYwHQYDVR0OBBYEFHznsrEs3rGna+l2DOGj/U5sx7n2MB8GA1Ud +IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBAGPdWc6KeaqYnU7FiWQ3foqTZy8Q6m8nw413bfJcVpQZ +GmlgMEZdj/JtRTyONZd8L7hR4uiJvYjPJxwINFyIwWgk25GF5M/7+0ON6CUBG8QO +9wBCSIYfJAhYWoyN8mtHLGiRsWlC/Q2NySbmkoamZG6Sxc4+PH1x4yOkq8fVqKnf +gqc76IbVw08Y40TQ4NzzxWgu/qUvBYTIfkdCU2uHSv4y/14+cIy3qBXMF8L/RuzQ +7C20bhIoqflA6evUZpdTqWlVwKmqsi7N0Wn0vvi7fGnuVKbbnvtapj7+mu+UUUt1 +7tjU4ZrxAlYTiQ6nQouWi4UMG4W+Jq6rppm8IvFz30I= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgILAgAAAAAA1ni41sMwDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05OTAxMjgxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDMgQ0ExJjAkBgNV +BAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAzIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAkV5WZdbAwAScv0fEXHt6MQH5WJaZ4xyEL9xWj631 +WYHVQ2ZdWpOMdcqp5xHBURAUYMks1HuvxneGq3onrm+VuQvKtkb7fhr0DRRt0slO +sq7wVPZcQEw2SHToVIxlZhCnvSu3II0FSa14fdIkI1Dj8LR5mwE5/6870y3u4UmN +jS88akFFL5vjPeES5JF1ns+gPjySgW+KLhjc4PKMjP2H2Qf0QJTJTk9D32dWb70D +UHyZZ6S5PJFsAm6E1vxG98xvGD4X8O8LZBZX5qyG8UiqQ8HJJ3hzREXihX26/7Ph ++xsFpEs7mRIlAVAUaq9d6sgM7uTa7EuLXGgTldzDtTA61wIDAQABo2MwYTAOBgNV +HQ8BAf8EBAMCAAYwHQYDVR0OBBYEFMw2zBe0RZEv7c87MEh3+7UUmb7jMB8GA1Ud +IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBAFeyVMy9lRdkYIm2U5EMRZLDPahsw8yyGPV4QXTYfaMn +r3cNWT6UHWn6idMMvRoB9D/o4Hcagiha5mLXt+M2yQ6feuPC08xZiQzvFovwNnci +yqS2t8FCZwFAY8znOGSHWxSWZnstFO69SW3/d9DiTlvTgMJND8q4nYGXpzRux+Oc +SOW0qkX19mVMSPISwtKTjMIVJPMrUv/jCK64btYsEs85yxIq56l7X5g9o+HMpmOJ +XH0xdfnV1l3y0NQ9355xqA7c5CCXeOZ/U6QNUU+OOwOuow1aTcN55zVYcELJXqFe +tNkio0RTNaTQz3OAxc+fVph2+RRMd4eCydx+XTTVNnU= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU +YHtmGkUNl8qJUC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B +AQQFAAOCAQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq7 +5bCdPTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q +gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT2iHR +rH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlDNPYPhyk7 +ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBgHcl5JLL2bP2o +Zg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD +VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT +ZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBlcnNvbmFsIEJhc2lj +IENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNpY0B0aGF3dGUuY29tMB4X +DTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgcsxCzAJBgNVBAYTAlpBMRUw +EwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UE +ChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2Vy +dmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBD +QTEoMCYGCSqGSIb3DQEJARYZcGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53 +dXLdjUmbllegeNTKP1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdK +wPQIcOk8RHtQfmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7 +G1sY0b8jkyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQF +AAOBgQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7 +c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95B21P +9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD +VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT +ZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVt +YWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUu +Y29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgdExCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEa +MBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBG +cmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhh +d3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfY +DFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5E +rHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVq +uzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN +BgkqhkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP +MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgCneSa +/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr5PjRznei +gQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD +VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT +ZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBlcnNvbmFsIFByZW1p +dW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXByZW1pdW1AdGhhd3RlLmNv +bTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5NTlaMIHPMQswCQYDVQQGEwJa +QTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xGjAY +BgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5nMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9u +IFNlcnZpY2VzIERpdmlzaW9uMSMwIQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJl +bWl1bSBDQTEqMCgGCSqGSIb3DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUu +Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0Vs +Bd/eJxZRNkERbGw77f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWI +Et12TfIa/G8jHnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYD +ZicRFTuqW/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH +b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVxeTBh +KXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1KzGJ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm +MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx +MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 +dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl +cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 +DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 +yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX +L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG +7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ +qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIRIjCCCQoCAQAwDQYJKoZIhvcNAQEFBQAwVzEPMA0GA1UEChMGVGhhd3RlMSEw +HwYDVQQLExhUaGF3dGUgVW5pdmVyc2FsIENBIFJvb3QxITAfBgNVBAMTGFRoYXd0 +ZSBVbml2ZXJzYWwgQ0EgUm9vdDAeFw05OTEyMDUxMzU2MDVaFw0zNzA0MDMxMzU2 +MDVaMFcxDzANBgNVBAoTBlRoYXd0ZTEhMB8GA1UECxMYVGhhd3RlIFVuaXZlcnNh +bCBDQSBSb290MSEwHwYDVQQDExhUaGF3dGUgVW5pdmVyc2FsIENBIFJvb3Qwgggi +MA0GCSqGSIb3DQEBAQUAA4IIDwAwgggKAoIIAQDiiQVtw3+tpok6/7vHzZ03seHS +IR6bYSoV53tXT1U80Lv52T0+przstK1TmhYC6wty/Yryj0QFxevT5b22RDnm+0e/ +ap4KlRjiaOLWltYhrYj99Rf109pCpZDtKZWWdTrah6HU9dOH3gVipuNmdJLPpby7 +32j/cXVWQVk16zNaZlHy0qMKwYzOc1wRby2MlYyRsf3P5a1WlcyFkoOQVUHJwnft ++aN0QgpoCPPQ0WX9Zyw0/yR/53nIBzslV92kDJg9vuDMGWXb8lSir0LUneKuhCMl +CTMStWoedsSL2UkAbF66H/Ib2mfKJ6qjRCMbg4LO8qsz7VSk3MmrWWXROA7BPhtn +j9Z1AeBVIt12d+yO3fTPeSJtuVcD9ZkIpzw+NPvEF64jWM0k8yPKagIolAGBNLRs +a66LGsOj0gk8FlT1Nl8k459KoeJkxhbDpoF6JDZHjsFeDvv5FXgE1g5Z2Z1YZmLS +lCkyMsh4uWb2tVbhbMYUS5ZSWZECJGpVR9c/tiMaYHeXLuJAr54EV56tEcXJQ3Dv +SLRerBxpLi6C1VuLvoK+GRRe5w0ix1Eb/x6b8TCPcTEGszQnj196ZoJPii0Tq0LP +IVael45mNg+Wm+Ur9AKpKmqMLMTDuHAsLSkeP1B3Hm0qVORVCpE4ocW1ZqJ2Wu4P +v7Rn4ShuD+E2oYLRv9R34cRnMpN4yOdUU/4jeeZozCaQ9hBjXSpvkS2kczJRIfK7 +Fd+qJAhIBt6hnia/uoO/fKTIoIy90v+8hGknEyQYxEUYIyZeGBTKLoiHYqNT5iG3 +uIV7moW7FSZy+Ln3anQPST+SvqkFt5knv78JF0uZTK0REHzfdDH2jyZfqoiuOFfI +VS3T+9gbUZm+JRs6usB9G+3O0km5z/PFfYmQgdhpSCAQo/jvklEYMosRGMA/G4VW +zlfJ8oJkxt8CCS5KES+xJ203UvDwFmHxZ43fh3Kvh9rP+1CUbtSUheuKLOoh9ZZK +RNXgzmp0RE3QBdOHFe020KSLZlVwk+5HBsF+LqUYeWfzKIXxcPcOg6R+VJ5adjLL +ZRu4zfvIKAPSVJHRp8WFQwgXdqXmL2cI2KGigi0M+MGvY9RQd21rRkpBhdWQX3kt +xOzXEYdAiuFo4mT4VTL7b5Ms2nfZIcEX5TYsTn6Qf6yUKzJnvjhQdriuQbnXIcUJ +TGDIo1HENJtXN9/LyTNXi+v7dp8ZTcVqHypFrivtL42npQDLBPolYi50SBvKKoy6 +27Z+9rsCfKnD21h4ob/w/hoQVRHO6GlOlmXGFwPWB2iMVIKuHCJVP/H0CZcowEb3 +TgslHfcH1wkdOhhXODvoMwbnj3hGHlv1BrbsuKYN8boTS9YYIN1pM0ozFa64yJiK +JyyTvC377jO/ZuZNurabBlVgl0u8RM1+9KHYqi/AAighFmJ42whU8vz0NOPGjxxD +V86QGkvcLjsokYk/eto1HY4s7kns9DOtyVOojJ8EUz4kHFLJEvliV6O87izrQHwg +I3ArlflzF4rRwRxpprc4mmf3cB16WgxAz2IPhTzCAk5+tfbFKimEsx83KuGqckLE +7Wsaj5IcXb7R8lvyq6qp0vW4pEErK5FuEkjKmNg3jcjtADC1tgROfpzahOzA+nvl +HYikU0awlORcG6ElLA9IUneXCWzsWxgzgwLlgn7NhSEwEf0nT8/kHuw/pVds6Sow +GSqI5cNpOKtvOXF/hOFBw+HMKokgUi6DD2w5P0stFqwt8CSsAHP0m7MGPwW4FIUf +q55cPJ5inQ5tO4AJ/ALqopd0ysf541bhw8qlpprAkOAkElPSwovavu0CQ15n4YmY +ee7LqsrDG9znpUalfGsWh7ZaKNfbJzxepb22Ud0fQ887Jsg6jSVhwUn0PBvJROqv +HMIrlAEqDjDRW4srR+XD0QQDmw45LNYn1OZwWtl1zyrYyQAF5BOI7MM5+4dhMDZD +A8ienKIGwi/F/PCAY7FUBKBMqS7G9XZ62NDk1JQR5RW1eAbcuICPmakgMz0QhUxl +Cco+WF5gk5qqYl3AUQYcXWCgDZxLQ/anFiGkh6rywS7ukjC4nt/fEAGLhglw2Gyo +t1AeFpa092f9NTohkCoyxwB7TQcQCbkvc9gYfmeZBE8G/FDHhZudQJ2zljf6pdyy +ck7vTgks/ZH9Tfe7pqE+q3uiA0CmqVUn4vr5Gc6HdarxdTbz87iR+JHDi3UTjkxl +mhY5auU06HqWWX81sAD9W2n8Qyb69Shu/ofZfiT7tKCCblSi/66/YrT0cgHCy5hH +mOFMtReAgM6PpijuHkVq+9/xHfxaO9bq9GwdYklXO4qPhurwUwTOnBZo/7q5/IgP +R/cCRHJAuMo7LVOd3DxWjFl7aBosjXG7bADHGs5vQJKxoy8P2UTyo3Aunu4OrjLQ +Oz6LB+rmebNcKeJ9a6he+Vox6AiWoowDmEbxuH2QVCbtdmL+numabl7JScdcNFMp +VNns5EbhgDt12d/7edWH8bqe6xnOTFJz5luHriVPOXnMxrj5EHvs8JtxpAWg0ynT +Tn8f9C0oeMxVlXsekS/MVhhzi7LbvGkH5tDYT+2i/1iFo23gSlO3Z32NDFxbe3co +AjVEegTTKEPIazAXXTK4KTW6dto7FEp2GFik+JI8nk0zb0ZrCNkxSGjd9PskVjSy +z2lmvkjSimYizfJpzcJTE0UpQSLWXZgftqSyo8LuAi9RG9yDpOxwJajUCGEyb+Sh +gS58Y3L6KWW8cETPXQIDAQABMA0GCSqGSIb3DQEBBQUAA4IIAQBVmjRqIgZpCUUz +x66pXMcJTpuGvEGQ1JRS9s0jKZRLIs3ovf6dzVLyve2rh8mrq0YEtL2iPyIwR1DA +S4x2DwP1ktKxLcR6NZzJc4frpp/eD3ON03+Z2LqPb8Tzvhqui6KUNpDi5euNBfT8 +Zd+V8cSUTRdW1588j1A853e/lYYmZPtq/8ba6YyuQrtp5TPG2OkNxlUhScEMtKP5 +m0tc3oNPQQPOKnloOH3wVEkg9bYQ/wjcM2aWm/8G3gCe185WQ5pR/HDN9vBRo7fN +tFyFYs1xt8YrIyvdw25AQvo3/zcc9npXlIeFI9fUycdfwU0vyQ3XXOycJe6eMIKR +lnK4dR34CWhXl7ItS+4l7HokKe5y1JwT26vcAwrYShTJCFdEXaG1U4A08hSXz1Le +og6KEOkU79BgvmGh8SVd1RhzP5MQypbus0DS26NVz1dapQ5PdUff6veQmm31cC4d +FBw3ZARZULDccoZvnDc9XSivc1Xv0u4kdHQT79zbMUn7P2P10wg+M6XnnQreUyxR +jmfbm0FlQVC91KSWbIe8EuCUx9PA5MtzWACD4awnhdadU51cvQo+A0OcDJH1bXv4 +QHJ1qxF2kSvhxqofcGl2cBUJ/pPQ1i23FWqbZ1y0aZ8lpn2K+30iqXHyzk6MuCEt +3v5BcQ3/nexzprsHT4gOWEcufqnCx3jdunqeTuAwTmNvhdQgQen6/kNF5/uverLO +pAUdIppYht/kzkyp/tgWpW/72M5We/XWIO/kR81jJP+5vvFIo8EBcua9wK3tJg3K +NJ/8Ai0gTwUgriE9DMIgPD/wBITcz4n9uSWRjtBD5rMgq1wt1UCeoEvY9LLMffFY +Co6H7YisNpbkVqARivKa0LNXozS7Gas44XRrIsQxzgHVGzbjHjhMM5PfQONZV06s +bnseWj3FHVusyBCCNQIisvx16BCRjcR9eJNHnhydrGtiAliM1hwj1q94woCcpKok +VBS1FJjG+CsaJMtxMgrimw5pa91+jGTRLmPvDn+xPohMnVXlyW4XBLdB/72KQcsl +MW9Edz9HsfyBiAeOBUkgtxHZaQMqA525M4Sa399640Zzo9iijFMZiFVMdLj2RIQr +0RQtTjkukmj/afyFYhvrVU/vJYRiRZnW2E5vP1MIfR0GlYGAf09OdDaYteKHcJjc +1/XcUhXmxtZ5ljl/j5XPq4BTrRsLRUAO1Bi9LN6Kd3b98kRHxiHQ5HTw2BgFyHww +csff8bv8AjCp9EImWQ2TBYKhc+005ThdzVCQ/pT8E7y9/KiiiKdzxLKo0V2IxAKi +evEEyf6MdMnvHWRBn6welmdkrKsoQced98CYG24HwmR9WoNmVig2nOf7HHcOKKDE +92t5OQQghMdXk7wboOq860LlqBH+/KxlzP34KIj0pZrlc1HgqJsNA3dO5eCYs4ja +febGnnwUZsEuU0qSBzegfuk9CeQVfM/9uEGl755mncReBx2H+EGt6ucv0kFjGDf5 +FONN0OX3Q/0V4/k2cwYm3wFPqcNO3iBGd5i0eiQrO3UrTliNm12kxxagvDKIP6GD +8wDI+NhY6WNdTCu18HJB2Kt3N9ZydK62NpzIpoNJS+DJVgspvgAwy93WyEKKANns +FdE0cfJbZIf2J9K364awkL8p2yGeNozjIC+VI1FsG8Kk1ebYAkNnoP6bUANEf7vk +ctXR5NqPkhRk+10UEBJKlQbJZQgpyiGjJjgRySffcGcE/cpIMn9jskV0MVBPh9kg +cNIhcLHWEJ0zXXiDkW1Vguza5GJjx4FG1xllcipDGZC41yNNTBzgRKlmZ6zucXkn +Jnhtcg71XUsjtXx8ZekXxjoLDd1eHlHDhrjsf8cnSqVG6GotGcGHo8uZk4dkolUU +TLdDpZPX59JOeUDKZZlGPT96gHqIaswe5WszRvRQwNUfCbjNii6hJ+tdc6foawrl +V4IqsPziVFJW8KupEsYjlgcknOC8RqW0IATaCZNj5dQuwn7FMe21FXSGF7mz8yaK +HQJq2ho/6LrxBG2UUVTiWrRZgx1g0C1zzAe1Joz518aIke+Az10PoWDLRdRCItGx +cB390LcwkDrGSG1n5TLaj9vjqOMdICWiHOFMuaT2xj9cWA27xrJ3ARaRnxcGDbdA +PsyPjpxL4J1+mx4Fq4gi+tMoG1cUZEo+JCw4TSFpAHMu0FUtdPIV6JRDPkAqxsa5 +alveoswYUFRdTiqFbPaSiykZfufqSuAiKyW892bPd5pBdPI8FA10afVQg83NLyHb +IkaK0PdRGpVX8gWLGhntO0XoNsJufvtXIgAfBlOprpPGj3EqMUWS545t5pkiwIP8 +79xXZndPojYx+6ETjeXKo5V9AQxkcDtTQmiAx7udqAA1aZgMqGfYQ+Wqz5XgUZWk +Fz9CnbgEztN5ecjTihYykuDXou7XN0wvrLh7vkX28RgznHs3piTZvECrAOnDN4ur +2LbzXoFOsBRrBz4f7ML2RCKVu7Pmb9b5cGW6CoNlqg4TL4MTI1OLQBb6zi/8TQT4 +69isxTbCFVdIOOxVs7Qeuq3SQgYXDXPIV6a+lk2p8sD7eiEc9clwqYKQtfEM1HkQ +voGm6VxhnHd5mqTDNyZXN8lSLPoI/9BfxmHA9Ha+/N5Oz6tRmXHH33701s8GVhkT +UwttdFlIGZtTBS2dMlTT5SxTi2Q+1GR744AJFMz+FkZja3Fp+PnLJ/aIVLxFs84C +yJTuQFv5QgLC/7DYLOsof17JJgGZpw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy +NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y +LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ +TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y +TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 +LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW +I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy +NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY +dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 +WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS +v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v +UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu +IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8xCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh +c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05 +NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYD +VQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMSBQdWJsaWMgUHJp +bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3noaACpEO+jglr0aIguVzqKCbJF0N +H8xlbgyw0FaEGIeaBpsQoXPftFg5a27B9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR +4k5FVmkfeAKA2txHkSm7NsljXMXg1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATAN +BgkqhkiG9w0BAQIFAAOBgQBMP7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZo +EWx8QszznC7EBz8UsA9P/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5 +FvjqBUuUfx3CHMjjt/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89Fx +lA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK +VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm +Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J +h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul +uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68 +DzFc6PLZ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 +nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO +8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV +ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb +PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 +6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr +n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a +qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 +wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 +ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs +pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 +E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAyIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZMJaLtVRKXxaeAufqDwSCg+i8VDXyh +YGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvEerf4Zh+AVPy3wo5ZShRXRtGak75BkQO7 +FYCTXOvnzAhsPz6zSvz/S2wj1VCCJkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBAIobK/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxg +J8pFUs4W7z8GZOeUaHxgMxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Nc +r6Pc5iaAIzy4RHT3Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns +YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y +aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe +Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj +IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx +KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM +HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw +DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC +AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji +nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX +rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn +jBJ7xUS0rg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy +aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp +Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV +BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp +Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g +Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU +J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO +JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY +wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o +koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN +qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E +Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe +xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u +7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU +sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI +sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP +cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDM +HO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtK +qsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwj +cSGIL4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y +cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckztImRP +T8qAkbYp +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 +GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ ++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd +U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm +NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ +ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 +CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq +g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c +2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ +bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD +VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0 +MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNV +BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2Vy +dmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJ +ADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII +0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphI +uR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZI +hvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3 +YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc +1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjTCCAXWgAwIBAgIDAQAhMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMxNTNaFw0xMjA3MTIxNjMxNTNaMEMxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xFzAVBgNVBAMTDkNlcnR1bSBM +ZXZlbCBJMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCl73pZ9DFcn7Qy0qBZ +K+So18cav7drUrJ8SiYOlDDVskt81+eIcL/4FelTSGjuAOvYdmm+HGYG998RPB0i +Z+Ak67vXFJ537vRWOcu6aMjNuAwu8BOdc5eSgB0Y8X4+3LOYfugtaZa8mrEQ8Hit +0yLE9UBcU9J+4PmkVGecmZ8jZQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAlDS4aTmgK0YgmUvt/3zN7G2/ZrtBBCtONlUvC69c7 +TmLJWJ842y2AH7ryNXXkcsn6p0ZBTrTJ2tA2y/j2PXJeXrCkK/qAJIpM0l4u0MT7 +enY5akasduHp2NXMP9vDlgMy7elU2s3nkOT79gfh5XttC+5D/x4JDNi1DMAA9hk1 +6DK4zWmDVfjkiP/G3fEndtJgNDQsyqnaQ3E3bljv3f1KJTjZUvtA2Ml6MP2hFRhg +ZPsxuhW8QXidQYNiua1h7XUUiPiERLDLWZmfY6dxGrHXjSTx3shHNaQM0qkDs9gS +6UK8uWJN2bf2YBnvGmzy0IQvx5wDCH7h8AdaBD6DgIG1 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjjCCAXagAwIBAgIDAQAiMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMyMDNaFw0xMjA3MTIxNjMyMDNaMEQxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xGDAWBgNVBAMTD0NlcnR1bSBM +ZXZlbCBJSTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyMQSaN5fA94hNE46 +bMKpGUb5yIPEowReGZzGttYBQnC6oUOy+iM3md8WerzXeBKf7iIZEDW2HAp7BKhS +4rMB6taxT07vDtkNfEKwOk6X7dODw6KY4mxnzjmjh5pf2feKKJ3MoZxi2HAz2a6J +vHKFMq8dAlGL2GBtLvzlFp2jwkMCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN +BgkqhkiG9w0BAQUFAAOCAQEAWo3wgy+/0B7UiTCu4Wn1rvGRXIUtbPNp4Bc4PP/i +1q6pPheIe0ooCopuqnDX9maTHhZeNpnApgCUSbyw71EaOremD7HjWXASRUTylhwL +5FdSx+D6MgF2uW9uwZ+NErkeRJYT2aRXe5FBOVIseC4g93Ay0D8Hg50MkAC5pQqW ++8GSszT94NzT7ppIaMtq53PZpUtLGiL3UBZ5vUJ5pE4lLKD7Ce+pXzZevy/MnkMG +D1L7LgjRWL17OcMlASFETyUTajNjvxMy+oM4C22rwHRh2WQrvgw5MO+Q3UyYA1r5 +VrSaqgQ1g06ZcQt+mhzoc2swlOSwm8iis8H6orR8xmCWrA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjzCCAXegAwIBAgIDAQAjMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMyMTdaFw0xMjA3MTIxNjMyMTdaMEUxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xGTAXBgNVBAMTEENlcnR1bSBM +ZXZlbCBJSUkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALZjBbfGFmlsLjPe +pWaDwG0LqhF11lWKabaHi1sQhK3qomHY7Em7qpL11dUQ1vsMcnnpzz/J0AEH6KDh ++yAyXV1SE/tVToLYYByZK+JGacLYIYF9aCwV8AhqyzOGurO5QX6vLboXB2WNnwmX +hyNVKUgnUVy4ktAR2qZJIw5Bjsn/AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAIsLt3vKCZqd/gui45ovm3FSO6FLjzzq4pagPvbN +nZ39HrhRaCpqkHDAj71L5L27U3eW2D4ILL0iUmZadbC4i3at/PUL9mjhGlajcCN8 +EF6IXGT87Tbcii735jRaaXSbEY4YhNOg9DPBoD4uJMkA8Z0Y/6lYmk4S6KUMCzzt +t5zZBiWjdd08yFi5VGMvpE74KVOMdMa3JNVaR0XvT0Q8yXo1XKCrY9OFIxnhVgDb +hzr9fwjKWDwu8kxhT9khAETm0BU2Buu+CTasaJdT/bBR2YEx9qcN7XyXTeDtkOO5 +QeGSqFgzquwjWEbKhf7l/e+efdRCg+ikH3O5snHB6iS+dgg= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjjCCAXagAwIBAgIDAQAkMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMyMzVaFw0xMjA3MTIxNjMyMzVaMEQxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xGDAWBgNVBAMTD0NlcnR1bSBM +ZXZlbCBJVjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmyb1lKqCAKE4juAy +6lpVNUl6aJ2DuWPSiJ3BBk3/6ty6I4Lr2Dpy1b1vjVelhaFsVKEDgK2JyQlk9XMq +LPZI2Ql166mJiPKFg77aY/W78EcQfGyjnRvVcs0tG40mAs/p84OEpFcVe/RSqDrD +/D7R01u+Wj5xLl0PUsFplIGDbikCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN +BgkqhkiG9w0BAQUFAAOCAQEAPS99JujKGVRfa50TKfieq+uK1SxjidErYaZTb3cJ +NNfQDYn6nk4lnrnab5EUVhO/NegP2yIu3YOnZGfxFDhvVozMTKKAB5r5XKOvzsP9 +9C9578PVMLozucfUMCSwau7Z4l5uuQOHuzjzlVLCibbbf4RwfvZ7hh5sB5c0pNbw +RQq64RXQUUEvul/W9gUeT9ISHOsASGTq+HJ5i7vNARjukEAXW/maqs9vyTWWbGVI +1FSOnVyteymq4Xk+9YlIyNPNyacgnsMnU72XKBLDS0KJdhIWALFAZI4dSh5WZNuW +ZguUnEmeH81lLbR+p/N3iuN8+oSo8UXik92jxeUY2tQJUA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF +MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU +QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI +MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENBMSkwJwYJKoZIhvcN +AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla +Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy +ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y +IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1 +c3RDZW50ZXIgQ2xhc3MgMiBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA +dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0y +AClxgwENv4wB3NrGrTmkqYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDw +TFXlay3HhQswHJJOgtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8 +/vhYnvgpjbB7zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3 +LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G +CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+WLDO/ +jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xRT3h2oNms +Gb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/AcASZ4smZHcFFk +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF +MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU +QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI +MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENBMSkwJwYJKoZIhvcN +AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla +Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy +ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y +IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1 +c3RDZW50ZXIgQ2xhc3MgMyBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA +dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUF +Lg2N7KBAahwOJ6ZQkmtQGwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGw +Dtf7pBc9r6tpepYnv68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDW +w1Krj10nnGvAo+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3 +LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G +CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4iJIE +Tb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yCGdHHsbHD +2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQSCdS7kjXvD9s0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIG2jCCBcKgAwIBAgIDFc/9MA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJE +RTEhMB8GA1UEChMYRGV1dHNjaGVzIEZvcnNjaHVuZ3NuZXR6MRYwFAYDVQQLEw1E +Rk4tQ0VSVCBHbWJIMRAwDgYDVQQLEwdERk4tUENBMS0wKwYDVQQDEyRERk4gVG9w +bGV2ZWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEmNl +cnRpZnlAcGNhLmRmbi5kZTAeFw0wMTEyMDExMjExMTZaFw0xMDAxMzExMjExMTZa +MIGsMQswCQYDVQQGEwJERTEhMB8GA1UEChMYRGV1dHNjaGVzIEZvcnNjaHVuZ3Nu +ZXR6MRYwFAYDVQQLEw1ERk4tQ0VSVCBHbWJIMRAwDgYDVQQLEwdERk4tUENBMS0w +KwYDVQQDEyRERk4gVG9wbGV2ZWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxITAf +BgkqhkiG9w0BCQEWEmNlcnRpZnlAcGNhLmRmbi5kZTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMF5rhMt6zmhxK5oWPwT2FG7Up7T5DovHSD/YKPIRxsv +DWmC4dTzByIBLnOmEflk+5KAqAYao6eY1qF0hR4WiS4DjCsn7l3zNo/4i2eF4EmG +EksBygb4tRlTThcO7heFX+Du5qFoks+ONqa70RlwOr2l53KVwjMXBCtCLFSKRLVu +xeh5+Smkm+FuOmwEugndM2n74Djjyf9DCOaHGZrHwVDh+Vpy5Ny4bKCSboujRxd5 +NxsStUshDVbTeS3B8TuzAJbywYWEE7erox+7WTfQr8ivSCBhrNJ36VRjAb8hiV9I +uy2TmJYo2oPyC8a3eM3xj9Ku2IW3tS2zpfiIzt9xvFMCAwEAAaOCAwEwggL9MA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAYL+rX4SHijILELPs+g0MTRf33QMIHb +BgNVHSMEgdMwgdCAFAYL+rX4SHijILELPs+g0MTRf33QoYGypIGvMIGsMQswCQYD +VQQGEwJERTEhMB8GA1UEChMYRGV1dHNjaGVzIEZvcnNjaHVuZ3NuZXR6MRYwFAYD +VQQLEw1ERk4tQ0VSVCBHbWJIMRAwDgYDVQQLEwdERk4tUENBMS0wKwYDVQQDEyRE +Rk4gVG9wbGV2ZWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxITAfBgkqhkiG9w0B +CQEWEmNlcnRpZnlAcGNhLmRmbi5kZYIDFc/9MAsGA1UdDwQEAwIBBjARBglghkgB +hvhCAQEEBAMCAAcwgaUGA1UdHwSBnTCBmjBLoEmgR4ZFaHR0cDovL3d3dy5kZm4t +cGNhLmRlL2NlcnRpZmljYXRpb24veDUwOS9nMS9kYXRhL2NybHMvcm9vdC1jYS1j +cmwuY3J4MEugSaBHhkVodHRwOi8vd3d3LmRmbi1wY2EuZGUvY2VydGlmaWNhdGlv +bi94NTA5L2cxL2RhdGEvY3Jscy9yb290LWNhLWNybC5jcmwwOAYJYIZIAYb4QgED +BCsWKWh0dHBzOi8vd3d3LmRmbi1wY2EuZGUvY2dpL2NoZWNrLXJldi5jZ2k/MEsG +CWCGSAGG+EIBCAQ+FjxodHRwOi8vd3d3LmRmbi1wY2EuZGUvY2VydGlmaWNhdGlv +bi9wb2xpY2llcy94NTA5cG9saWN5Lmh0bWwwOAYJYIZIAYb4QgENBCsWKVRoZSBE +Rk4gVG9wLUxldmVsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MGQGA1UdIARdMFsw +WQYLKwYBBAHZGoIsAQEwSjBIBggrBgEFBQcCARY8aHR0cDovL3d3dy5kZm4tcGNh +LmRlL2NlcnRpZmljYXRpb24vcG9saWNpZXMveDUwOXBvbGljeS5odG1sMA0GCSqG +SIb3DQEBBQUAA4IBAQAmbai6JMt7nkuavyvxKzLGn04Gyt0zKrp8zmERp4inktvY +7p+vkaomYu2QYC7cHq0tlrPXQQhhetjiXGb+36aJtHDkEA0NwrJzYnHgPsvx7z0w +ysENP4wxf97KsSWm07RY+f6/gIQF7Je7CW30Rzq7N6R0NMBs32mJgdn3ntqlFNw3 +Nbs050FEjPNq54RdawlJo85x+w+QJd7uQM4yZjHpRhvwgte9Ge1UqCUdpMsLHzeM +KJ0B9GhwIIqOJCMiPgKjcUBrn6ehSX70POvXvjjE2+FzhPGTyTkS474d2UCAnL9q +hPrdWXzBjOumOjhJutT1aecm9eljlshmh1cNen00 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCB +ozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3Qt +TmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5WhcNMTkwNzA5MTg1 +NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYD +VQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VS +Rmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCz+5Gh5DZVhawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2 +N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCH +iZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hARe +YFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1 +axwiP8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6g +yN7igEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD +AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPh +ahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9V +VE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0GCSqGSIb3DQEB +BQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y +IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6Lzs +QCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4 +ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qM +YEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUb +QErNaLly7HF27FSOH4UMAWr6pjisH8SE +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx +MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG +29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk +oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk +3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL +qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN +nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX +ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H +DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO +TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv +kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w +zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB +rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt +Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa +Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV +BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l +dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE +AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B +YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 +hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l +L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm +SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM +1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws +6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw +Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 +aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u +7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 +xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ +rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim +eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk +USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB +lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt +T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc +BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3 +dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP +HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO +KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo +5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+ +pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb +kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC +AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov +L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV +HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN +AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw +NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB +mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU +4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5 +81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR +Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD +EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X +DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw +DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u +c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr +TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA +OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC +2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW +RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P +AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW +ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 +YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz +b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO +ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB +IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs +b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s +YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg +a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g +SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 +aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg +YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg +Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY +ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g +pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 +Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV +MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe +TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 +dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 +N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC +dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu +MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL +b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD +zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi +3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 +WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY +Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi +NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC +ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 +QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 +YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz +aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm +ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg +ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs +amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv +IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 +Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 +ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 +YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg +dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs +b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G +CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO +xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP +0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ +QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk +f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK +8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD +EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 +OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l +dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK +gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX +iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc +Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E +BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G +SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu +b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh +bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv +Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln +aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 +IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph +biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo +ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP +UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj +YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA +bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 +sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa +n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS +NitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiTCCAfKgAwIBAgIEN4dnrDANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJi +ZTERMA8GA1UEChMIQmVsZ2Fjb20xDDAKBgNVBAsTA01UTTEkMCIGA1UEAxMbQmVs +Z2Fjb20gRS1UcnVzdCBQcmltYXJ5IENBMR8wHQYKCZImiZPyLGQBAxQPaW5mb0Bl +LXRydXN0LmJlMB4XDTk4MTEwNDEzMDQzOVoXDTEwMDEyMTEzMDQzOVowdTELMAkG +A1UEBhMCYmUxETAPBgNVBAoTCEJlbGdhY29tMQwwCgYDVQQLEwNNVE0xJDAiBgNV +BAMTG0JlbGdhY29tIEUtVHJ1c3QgUHJpbWFyeSBDQTEfMB0GCgmSJomT8ixkAQMU +D2luZm9AZS10cnVzdC5iZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqtm5 +s9VPak3FQdB7BGFqi3GBB9pk41huJ1XCrc4XsPz6ko0I8Bxy/7LDMf7gaoeXTMxD +V6coeTq1g12kHWrxasU+FCIdWQZv8KYxd9ywSTjmywwP/qpyNIjaKDohWu50Kxuk +21sTFrVzX8OujNLAPj2wy/Dsi4YLwsFEGFpjqNUCAwEAAaMmMCQwDwYDVR0TBAgw +BgEB/wIBATARBglghkgBhvhCAQEEBAMCAAcwDQYJKoZIhvcNAQEFBQADgYEAerKx +pbF9M+nC4RvO05OMfwH9Gx1amq6rB1Ev7Ymr3VBCux//SrWknLFhKQpM6oNZSY2v +hmnXgaxHqqRxblnvynxqblSK2qiSyfVms3lf1IsBniFjRjWTpcJfImIDcB1jI+hr +SB0jECfY9t9HorrsgFBKbMRwpnrkdCJ/9oRiMn8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJE +SzEMMAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEw +ODM5MzBaFw0zNzAyMTEwOTA5MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNU +REMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuHnEz9pPPEXyG9VhDr +2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0zY0s +2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItU +GBxIYXvViGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKj +dGqPqcNiKXEx5TukYBdedObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+r +TpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB5DCB4TCB3gYIKoFQgSkB +AQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5kay9yZXBv +c2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRl +ciBmcmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEu +MS4xLiBDZXJ0aWZpY2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIg +T0lEIDEuMi4yMDguMTY5LjEuMS4xLjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1Ud +HwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEMMAoGA1UEChMDVERDMRQwEgYD +VQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYmaHR0cDovL2Ny +bC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy +MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZ +J2cdUBVLc647+RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqG +SIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACrom +JkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4A9G28kNBKWKnctj7fAXmMXAnVBhO +inxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYscA+UYyAFMP8uXBV2Y +caaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9AOoB +mbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQ +YqbsFbS1AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9 +BKNDLdr8C2LqL19iUw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE +SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg +Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV +BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl +cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA +vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu +Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a +0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1 +4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN +eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD +R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG +A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu +dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME +Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3 +WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw +HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ +KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO +Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX +wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89 +9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0 +jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38 +aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD +EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz +aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w +MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l +dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh +bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq +eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe +r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5 +3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd +vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l +mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC +wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg +hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0 +TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh +biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg +ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg +dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6 +b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl +c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0 +ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3 +dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu +ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo +ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3 +Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u +ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA +A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ +MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+ +NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR +VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY +83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3 +macqaJVmlaut74nLYKkGEsaUR+ko +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri new file mode 100644 index 0000000..196e19d --- /dev/null +++ b/src/network/ssl/ssl.pri @@ -0,0 +1,33 @@ +# OpenSSL support; compile in QSslSocket. +contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { + include($$QT_SOURCE_TREE/config.tests/unix/openssl/openssl.pri) + + HEADERS += ssl/qssl.h \ + ssl/qsslcertificate.h \ + ssl/qsslcertificate_p.h \ + ssl/qsslconfiguration.h \ + ssl/qsslconfiguration_p.h \ + ssl/qsslcipher.h \ + ssl/qsslcipher_p.h \ + ssl/qsslerror.h \ + ssl/qsslkey.h \ + ssl/qsslsocket.h \ + ssl/qsslsocket_openssl_p.h \ + ssl/qsslsocket_openssl_symbols_p.h \ + ssl/qsslsocket_p.h + SOURCES += ssl/qssl.cpp \ + ssl/qsslcertificate.cpp \ + ssl/qsslconfiguration.cpp \ + ssl/qsslcipher.cpp \ + ssl/qsslerror.cpp \ + ssl/qsslkey.cpp \ + ssl/qsslsocket.cpp \ + ssl/qsslsocket_openssl.cpp \ + ssl/qsslsocket_openssl_symbols.cpp + + # Include Qt's default CA bundle + RESOURCES += network.qrc + + # Add optional SSL libs + LIBS += $$OPENSSL_LIBS +} |