diff options
Diffstat (limited to 'src/network')
33 files changed, 1004 insertions, 411 deletions
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp index 072a7f249d..af5b126953 100644 --- a/src/network/access/qnetworkcookiejar.cpp +++ b/src/network/access/qnetworkcookiejar.cpp @@ -45,6 +45,14 @@ #include "QtCore/qdatetime.h" #if QT_CONFIG(topleveldomain) #include "private/qtldurl_p.h" +#else +QT_BEGIN_NAMESPACE +static bool qIsEffectiveTLD(QString domain) +{ + // provide minimal checking by not accepting cookies on real TLDs + return !domain.contains(QLatin1Char('.')); +} +QT_END_NAMESPACE #endif QT_BEGIN_NAMESPACE @@ -356,19 +364,12 @@ bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl // https://tools.ietf.org/html/rfc6265#section-5.3 step 5 if (host == domain) return true; -#if QT_CONFIG(topleveldomain) // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2 // redundant; the "leading dot" rule has been relaxed anyway, see QNetworkCookie::normalize() // we remove the leading dot for this check if it's present - if (qIsEffectiveTLD(domain)) - return false; // not accepted -#else - // provide minimal checking by not accepting cookies on real TLDs - if (!domain.contains(QLatin1Char('.'))) - return false; -#endif - - return true; + // Normally defined in qtldurl_p.h, but uses fall-back in this file when topleveldomain isn't + // configured: + return !qIsEffectiveTLD(domain); } QT_END_NAMESPACE diff --git a/src/network/configure.json b/src/network/configure.json index 019f8378c7..31f8bf9533 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -15,6 +15,7 @@ "openssl-linked": { "type": "void", "name": "openssl", "value": "linked" }, "openssl-runtime": { "type": "void", "name": "openssl", "value": "runtime" }, "dtls": "boolean", + "ocsp": "boolean", "sctp": "boolean", "securetransport": "boolean", "ssl": "boolean", @@ -162,6 +163,23 @@ ] }, "use": "openssl" + }, + "ocsp": { + "label": "OCSP stapling support in OpenSSL", + "type": "compile", + "test": { + "include": ["openssl/ssl.h", "openssl/ocsp.h"], + "tail": [ + "#if defined(OPENSSL_NO_OCSP) || defined(OPENSSL_NO_TLSEXT)", + "# error OpenSSL without OCSP stapling", + "#endif" + ], + "main": [ + "(void)SSL_get_tlsext_status_ocsp_resp(nullptr, nullptr);", + "(void)d2i_OCSP_RESPONSE(nullptr, nullptr, 0);" + ] + }, + "use": "openssl" } }, @@ -236,6 +254,13 @@ "condition": "features.openssl && tests.dtls", "output": [ "publicFeature" ] }, + "ocsp": { + "label": "OCSP-stapling", + "purpose": "Provides OCSP stapling support", + "section": "Networking", + "condition": "features.opensslv11 && tests.ocsp", + "output": [ "publicFeature" ] + }, "opensslv11": { "label": "OpenSSL 1.1", "condition": "features.openssl && tests.openssl11", @@ -369,6 +394,7 @@ For example: "openssl-linked", "opensslv11", "dtls", + "ocsp", "sctp", "system-proxies" ] diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 34db5b4b31..47ce9ab0c6 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -465,27 +465,12 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet methodString = ""; phase = Done; break; - case QAuthenticatorPrivate::Plain: - response = '\0' + user.toUtf8() + '\0' + password.toUtf8(); - phase = Done; - break; case QAuthenticatorPrivate::Basic: methodString = "Basic "; response = user.toLatin1() + ':' + password.toLatin1(); response = response.toBase64(); phase = Done; break; - case QAuthenticatorPrivate::Login: - if (challenge.contains("VXNlciBOYW1lAA==")) { - response = user.toUtf8().toBase64(); - phase = Phase2; - } else if (challenge.contains("UGFzc3dvcmQA")) { - response = password.toUtf8().toBase64(); - phase = Done; - } - break; - case QAuthenticatorPrivate::CramMd5: - break; case QAuthenticatorPrivate::DigestMd5: methodString = "Digest "; response = digestMd5Response(challenge, requestMethod, path); diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h index 8a1ee0ebe6..265cb7afe2 100644 --- a/src/network/kernel/qauthenticator_p.h +++ b/src/network/kernel/qauthenticator_p.h @@ -68,7 +68,7 @@ class QNtlmWindowsHandles; class Q_AUTOTEST_EXPORT QAuthenticatorPrivate { public: - enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 }; + enum Method { None, Basic, Ntlm, DigestMd5 }; QAuthenticatorPrivate(); ~QAuthenticatorPrivate(); diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 0973d0dd52..1c7a8da06d 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -300,25 +300,6 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, */ /*! - \fn template<typename PointerToMemberFunction> int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function) - - \since 5.9 - - \overload - - Looks up the IP address(es) associated with host name \a name, and - returns an ID for the lookup. When the result of the lookup is - ready, the slot or signal \a function in \a receiver is called with - a QHostInfo argument. The QHostInfo object can then be inspected - to get the results of the lookup. - - \note There is no guarantee on the order the signals will be emitted - if you start multiple requests with lookupHost(). - - \sa abortHostLookup(), addresses(), error(), fromName() -*/ - -/*! \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor functor) \since 5.9 @@ -354,6 +335,16 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, thread of \a context. The context's thread must have a running Qt event loop. + Here is an alternative signature for the function: + \code + lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function) + \endcode + + In this case, when the result of the lookup is ready, the slot or + signal \c{function} in \c{receiver} is called with a QHostInfo + argument. The QHostInfo object can then be inspected to get the + results of the lookup. + \note There is no guarantee on the order the signals will be emitted if you start multiple requests with lookupHost(). diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h index 75917a02a3..49871ad470 100644 --- a/src/network/kernel/qhostinfo.h +++ b/src/network/kernel/qhostinfo.h @@ -91,13 +91,10 @@ public: static QString localDomainName(); #ifdef Q_CLANG_QDOC - template<typename PointerToMemberFunction> - static int QHostInfo::lookupHost(const QString &name, const QObject *receiver, - PointerToMemberFunction function); template<typename Functor> - static int QHostInfo::lookupHost(const QString &name, Functor functor); + static int lookupHost(const QString &name, Functor functor); template<typename Functor> - static int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor); + static int lookupHost(const QString &name, const QObject *context, Functor functor); #else // lookupHost to a QObject slot template <typename Func> diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index c999bd2088..24e8eabb6e 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -1146,22 +1146,17 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const qint64 ret = -1; int recvResult = 0; DWORD flags; - DWORD bufferCount = 5; - WSABUF * buf = 0; + // We start at 1500 bytes (the MTU for Ethernet V2), which should catch + // almost all uses (effective MTU for UDP under IPv4 is 1468), except + // for localhost datagrams and those reassembled by the IP layer. + char udpMessagePeekBuffer[1500]; + std::vector<WSABUF> buf; for (;;) { - // We start at 1500 bytes (the MTU for Ethernet V2), which should catch - // almost all uses (effective MTU for UDP under IPv4 is 1468), except - // for localhost datagrams and those reassembled by the IP layer. - char udpMessagePeekBuffer[1500]; - - buf = new WSABUF[bufferCount]; - for (DWORD i=0; i<bufferCount; i++) { - buf[i].buf = udpMessagePeekBuffer; - buf[i].len = sizeof(udpMessagePeekBuffer); - } + buf.resize(buf.size() + 5, {sizeof(udpMessagePeekBuffer), udpMessagePeekBuffer}); + flags = MSG_PEEK; DWORD bytesRead = 0; - recvResult = ::WSARecv(socketDescriptor, buf, bufferCount, &bytesRead, &flags, 0,0); + recvResult = ::WSARecv(socketDescriptor, buf.data(), DWORD(buf.size()), &bytesRead, &flags, nullptr, nullptr); int err = WSAGetLastError(); if (recvResult != SOCKET_ERROR) { ret = qint64(bytesRead); @@ -1169,8 +1164,6 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const } else { switch (err) { case WSAEMSGSIZE: - bufferCount += 5; - delete[] buf; continue; case WSAECONNRESET: case WSAENETRESET: @@ -1185,9 +1178,6 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const } } - if (buf) - delete[] buf; - #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %lli", ret); #endif diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h index 2068254a95..59d1f58482 100644 --- a/src/network/ssl/qasn1element_p.h +++ b/src/network/ssl/qasn1element_p.h @@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE #define RSA_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.1.1") #define DSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10040.4.1") #define EC_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10045.2.1") +#define DH_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.3.1") // These are mostly from the RFC for PKCS#5 // PKCS#5: https://tools.ietf.org/html/rfc8018#appendix-B diff --git a/src/network/ssl/qdtls.h b/src/network/ssl/qdtls.h index 8505b00d5e..d057eadf19 100644 --- a/src/network/ssl/qdtls.h +++ b/src/network/ssl/qdtls.h @@ -48,7 +48,9 @@ #include <QtCore/qcryptographichash.h> #include <QtCore/qobject.h> +#ifndef Q_CLANG_QDOC QT_REQUIRE_CONFIG(dtls); +#endif QT_BEGIN_NAMESPACE diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp index 19d99bc489..ea2b73bad5 100644 --- a/src/network/ssl/qssl.cpp +++ b/src/network/ssl/qssl.cpp @@ -71,7 +71,8 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl"); \value Rsa The RSA algorithm. \value Dsa The DSA algorithm. - \value Ec The Elliptic Curve algorithm + \value Ec The Elliptic Curve algorithm. + \value Dh The Diffie-Hellman algorithm. \value Opaque A key that should be treated as a 'black box' by QSslKey. The opaque key facility allows applications to add support for facilities diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h index 60362cb410..5c25e4e105 100644 --- a/src/network/ssl/qssl.h +++ b/src/network/ssl/qssl.h @@ -62,7 +62,8 @@ namespace QSsl { Opaque, Rsa, Dsa, - Ec + Ec, + Dh, }; enum AlternativeNameEntryType { diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 3f732b4646..4697a5f90f 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -228,7 +228,8 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->nextAllowedProtocols == other.d->nextAllowedProtocols && d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol && d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus && - d->dtlsCookieEnabled == other.d->dtlsCookieEnabled; + d->dtlsCookieEnabled == other.d->dtlsCookieEnabled && + d->ocspStaplingEnabled == other.d->ocspStaplingEnabled; } /*! @@ -272,7 +273,8 @@ bool QSslConfiguration::isNull() const d->preSharedKeyIdentityHint.isNull() && d->nextAllowedProtocols.isEmpty() && d->nextNegotiatedProtocol.isNull() && - d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone); + d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone && + d->ocspStaplingEnabled == false); } /*! @@ -1094,6 +1096,37 @@ void QSslConfiguration::setDefaultDtlsConfiguration(const QSslConfiguration &con #endif // dtls +/*! + \since 5.13 + If \a enabled is true, client QSslSocket will send a certificate status request + to its peer when initiating a handshake. During the handshake QSslSocket will + verify the server's response. This value must be set before the handshake + starts. + + \sa ocspStaplingEnabled() +*/ +void QSslConfiguration::setOcspStaplingEnabled(bool enabled) +{ +#if QT_CONFIG(ocsp) + d->ocspStaplingEnabled = enabled; +#else + if (enabled) + qCWarning(lcSsl, "Enabling OCSP-stapling requires the feature 'ocsp'"); +#endif // ocsp +} + +/*! + \since 5.13 + Returns true if OCSP stapling was enabled by setOCSPStaplingEnabled(), + otherwise false (which is the default value). + + \sa setOcspStaplingEnabled() +*/ +bool QSslConfiguration::ocspStaplingEnabled() const +{ + return d->ocspStaplingEnabled; +} + /*! \internal */ bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) { diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 454ac0cee3..8f53e25a53 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -170,6 +170,9 @@ public: static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration); #endif // dtls + void setOcspStaplingEnabled(bool enable); + bool ocspStaplingEnabled() const; + enum NextProtocolNegotiationStatus { NextProtocolNegotiationNone, NextProtocolNegotiationNegotiated, diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 6c23165c6a..83126bb9a0 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -143,6 +143,12 @@ public: const bool dtlsCookieEnabled = false; #endif // dtls +#if QT_CONFIG(ocsp) + bool ocspStaplingEnabled = false; +#else + const bool ocspStaplingEnabled = false; +#endif + // in qsslsocket.cpp: static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp index 3f79d1a037..74ff6987d2 100644 --- a/src/network/ssl/qsslerror.cpp +++ b/src/network/ssl/qsslerror.cpp @@ -86,6 +86,18 @@ \value UnspecifiedError \value NoSslSupport \value CertificateBlacklisted + \value OcspNoResponseFound + \value OcspMalformedRequest + \value OcspMalformedResponse + \value OcspInternalError + \value OcspTryLater + \value OcspSigRequred + \value OcspUnauthorized + \value OcspResponseCannotBeTrusted + \value OcspResponseCertIdUnknown + \value OcspResponseExpired + \value OcspStatusUnknown + \sa QSslError::errorString() */ @@ -292,6 +304,39 @@ QString QSslError::errorString() const case CertificateBlacklisted: errStr = QSslSocket::tr("The peer certificate is blacklisted"); break; + case OcspNoResponseFound: + errStr = QSslSocket::tr("No OCSP status response found"); + break; + case OcspMalformedRequest: + errStr = QSslSocket::tr("The OCSP status request had invalid syntax"); + break; + case OcspMalformedResponse: + errStr = QSslSocket::tr("OCSP response contains an unexpected number of SingleResponse structures"); + break; + case OcspInternalError: + errStr = QSslSocket::tr("OCSP responder reached an inconsistent internal state"); + break; + case OcspTryLater: + errStr = QSslSocket::tr("OCSP responder was unable to return a status for the requested certificate"); + break; + case OcspSigRequred: + errStr = QSslSocket::tr("The server requires the client to sign the OCSP request in order to construct a response"); + break; + case OcspUnauthorized: + errStr = QSslSocket::tr("The client is not authorized to request OCSP status from this server"); + break; + case OcspResponseCannotBeTrusted: + errStr = QSslSocket::tr("OCSP reponder's identity cannot be verified"); + break; + case OcspResponseCertIdUnknown: + errStr = QSslSocket::tr("The identity of a certificate in an OCSP response cannot be established"); + break; + case OcspResponseExpired: + errStr = QSslSocket::tr("The certificate status response has expired"); + break; + case OcspStatusUnknown: + errStr = QSslSocket::tr("The certificate's status is unknown"); + break; default: errStr = QSslSocket::tr("Unknown error"); break; diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h index d7c959423d..513b8afd7f 100644 --- a/src/network/ssl/qsslerror.h +++ b/src/network/ssl/qsslerror.h @@ -80,6 +80,18 @@ public: HostNameMismatch, NoSslSupport, CertificateBlacklisted, + CertificateStatusUnknown, + OcspNoResponseFound, + OcspMalformedRequest, + OcspMalformedResponse, + OcspInternalError, + OcspTryLater, + OcspSigRequred, + OcspUnauthorized, + OcspResponseCannotBeTrusted, + OcspResponseCertIdUnknown, + OcspResponseExpired, + OcspStatusUnknown, UnspecifiedError = -1 }; diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp index 9a43e67772..99c1a39c73 100644 --- a/src/network/ssl/qsslkey_openssl.cpp +++ b/src/network/ssl/qsslkey_openssl.cpp @@ -69,6 +69,11 @@ void QSslKeyPrivate::clear(bool deep) q_DSA_free(dsa); dsa = nullptr; } + if (algorithm == QSsl::Dh && dh) { + if (deep) + q_DH_free(dh); + dh = nullptr; + } #ifndef OPENSSL_NO_EC if (algorithm == QSsl::Ec && ec) { if (deep) @@ -105,6 +110,12 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey) type = QSsl::PrivateKey; dsa = q_EVP_PKEY_get1_DSA(pkey); return true; + } else if (keyType == EVP_PKEY_DH) { + isNull = false; + algorithm = QSsl::Dh; + type = QSsl::PrivateKey; + dh = q_EVP_PKEY_get1_DH(pkey); + return true; } #ifndef OPENSSL_NO_EC else if (keyType == EVP_PKEY_EC) { @@ -160,6 +171,15 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase); if (dsa && dsa == result) isNull = false; + } else if (algorithm == QSsl::Dh) { + EVP_PKEY *result = (type == QSsl::PublicKey) + ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase) + : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase); + if (result) + dh = q_EVP_PKEY_get1_DH(result); + if (dh) + isNull = false; + q_EVP_PKEY_free(result); #ifndef OPENSSL_NO_EC } else if (algorithm == QSsl::Ec) { EC_KEY *result = (type == QSsl::PublicKey) @@ -181,6 +201,7 @@ int QSslKeyPrivate::length() const switch (algorithm) { case QSsl::Rsa: return q_RSA_bits(rsa); case QSsl::Dsa: return q_DSA_bits(dsa); + case QSsl::Dh: return q_DH_bits(dh); #ifndef OPENSSL_NO_EC case QSsl::Ec: return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec)); #endif @@ -215,7 +236,7 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const fail = true; } else { if (!q_PEM_write_bio_RSAPrivateKey( - bio, rsa, cipher, const_cast<uchar *>((const uchar *)passPhrase.data()), + bio, rsa, cipher, (uchar *)passPhrase.data(), passPhrase.size(), nullptr, nullptr)) { fail = true; } @@ -226,20 +247,33 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const fail = true; } else { if (!q_PEM_write_bio_DSAPrivateKey( - bio, dsa, cipher, const_cast<uchar *>((const uchar *)passPhrase.data()), + bio, dsa, cipher, (uchar *)passPhrase.data(), passPhrase.size(), nullptr, nullptr)) { fail = true; } } + } else if (algorithm == QSsl::Dh) { + EVP_PKEY *result = q_EVP_PKEY_new(); + if (!result || !q_EVP_PKEY_set1_DH(result, dh)) { + fail = true; + } else if (type == QSsl::PublicKey) { + if (!q_PEM_write_bio_PUBKEY(bio, result)) + fail = true; + } else if (!q_PEM_write_bio_PrivateKey( + bio, result, cipher, (uchar *)passPhrase.data(), + passPhrase.size(), nullptr, nullptr)) { + fail = true; + } + q_EVP_PKEY_free(result); #ifndef OPENSSL_NO_EC } else if (algorithm == QSsl::Ec) { if (type == QSsl::PublicKey) { if (!q_PEM_write_bio_EC_PUBKEY(bio, ec)) fail = true; } else { - if (!q_PEM_write_bio_ECPrivateKey(bio, ec, cipher, - const_cast<uchar *>((const uchar *)passPhrase.data()), - passPhrase.size(), nullptr, nullptr)) { + if (!q_PEM_write_bio_ECPrivateKey( + bio, ec, cipher, (uchar *)passPhrase.data(), + passPhrase.size(), nullptr, nullptr)) { fail = true; } } @@ -267,6 +301,8 @@ Qt::HANDLE QSslKeyPrivate::handle() const return Qt::HANDLE(rsa); case QSsl::Dsa: return Qt::HANDLE(dsa); + case QSsl::Dh: + return Qt::HANDLE(dh); #ifndef OPENSSL_NO_EC case QSsl::Ec: return Qt::HANDLE(ec); diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index 28e3e2efd8..b29b38beab 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -116,6 +116,8 @@ QByteArray QSslKeyPrivate::pemHeader() const return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----"); else if (algorithm == QSsl::Ec) return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----"); + else if (algorithm == QSsl::Dh) + return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); Q_UNREACHABLE(); return QByteArray(); @@ -141,6 +143,8 @@ QByteArray QSslKeyPrivate::pemFooter() const return QByteArrayLiteral("-----END DSA PRIVATE KEY-----"); else if (algorithm == QSsl::Ec) return QByteArrayLiteral("-----END EC PRIVATE KEY-----"); + else if (algorithm == QSsl::Dh) + return QByteArrayLiteral("-----END PRIVATE KEY-----"); Q_UNREACHABLE(); return QByteArray(); @@ -535,7 +539,9 @@ QDebug operator<<(QDebug debug, const QSslKey &key) debug << "QSslKey(" << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey") << ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" : - (key.algorithm() == QSsl::Rsa ? "RSA" : ((key.algorithm() == QSsl::Dsa) ? "DSA" : "EC"))) + (key.algorithm() == QSsl::Rsa ? "RSA" : + (key.algorithm() == QSsl::Dsa ? "DSA" : + (key.algorithm() == QSsl::Dh ? "DH" : "EC")))) << ", " << key.length() << ')'; return debug; diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index 7ae2cc740b..310553cab2 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -116,6 +116,7 @@ public: EVP_PKEY *opaque; RSA *rsa; DSA *dsa; + DH *dh; #ifndef OPENSSL_NO_EC EC_KEY *ec; #endif diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp index a13275f3bb..5ebd8ac3bd 100644 --- a/src/network/ssl/qsslkey_qt.cpp +++ b/src/network/ssl/qsslkey_qt.cpp @@ -165,6 +165,7 @@ static int extractPkcs8KeyLength(const QVector<QAsn1Element> &items, QSslKeyPriv switch (algorithm){ case QSsl::Rsa: return "RSA"; case QSsl::Dsa: return "DSA"; + case QSsl::Dh: return "DH"; case QSsl::Ec: return "EC"; case QSsl::Opaque: return "Opaque"; } @@ -217,6 +218,21 @@ static int extractPkcs8KeyLength(const QVector<QAsn1Element> &items, QSslKeyPriv if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType) return -1; keyLength = numberOfBits(dsaInfo[0].value()); + } else if (value == DH_ENCRYPTION_OID) { + if (Q_UNLIKELY(that->algorithm != QSsl::Dh)) { + // As above for RSA. + qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm) + << "\nLoading will fail."; + return -1; + } + // DH's structure is documented here: + // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9. + if (pkcs8Info[1].type() != QAsn1Element::SequenceType) + return -1; + const QVector<QAsn1Element> dhInfo = pkcs8Info[1].toVector(); + if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType) + return -1; + keyLength = numberOfBits(dhInfo[0].value()); } else { // in case of unexpected formats: qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value @@ -268,6 +284,16 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhra if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) return; keyLength = numberOfBits(params[0].value()); + } else if (algorithm == QSsl::Dh) { + if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID) + return; + if (infoItems[1].type() != QAsn1Element::SequenceType) + return; + // key params + const QVector<QAsn1Element> params = infoItems[1].toVector(); + if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) + return; + keyLength = numberOfBits(params[0].value()); } else if (algorithm == QSsl::Ec) { if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID) return; @@ -307,6 +333,12 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhra if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType) return; keyLength = numberOfBits(items[1].value()); + } else if (algorithm == QSsl::Dh) { + if (versionHex != "00") + return; + if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType) + return; + keyLength = numberOfBits(items[1].value()); } else if (algorithm == QSsl::Ec) { if (versionHex != "01") return; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 4a9d054c0d..270e81774d 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -952,6 +952,9 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.nextAllowedProtocols = configuration.allowedNextProtocols(); d->configuration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol(); d->configuration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus(); +#if QT_CONFIG(ocsp) + d->configuration.ocspStaplingEnabled = configuration.ocspStaplingEnabled(); +#endif // if the CA certificates were set explicitly (either via // QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(), @@ -2324,6 +2327,9 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri #if QT_CONFIG(dtls) ptr->dtlsCookieEnabled = global->dtlsCookieEnabled; #endif +#if QT_CONFIG(ocsp) + ptr->ocspStaplingEnabled = global->ocspStaplingEnabled; +#endif } /*! diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index 65d98758ac..66623cebef 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -993,9 +993,6 @@ void QSslSocketBackendPrivate::destroySslContext() context.reset(nullptr); } -static QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase); - - bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode) { Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); @@ -1511,264 +1508,4 @@ bool QSslSocketBackendPrivate::startHandshake() } } -/* - PKCS12 helpers. -*/ - -static QAsn1Element wrap(quint8 type, const QAsn1Element &child) -{ - QByteArray value; - QDataStream stream(&value, QIODevice::WriteOnly); - child.write(stream); - return QAsn1Element(type, value); -} - -static QAsn1Element _q_PKCS7_data(const QByteArray &data) -{ - QVector<QAsn1Element> items; - items << QAsn1Element::fromObjectId("1.2.840.113549.1.7.1"); - items << wrap(QAsn1Element::Context0Type, - QAsn1Element(QAsn1Element::OctetStringType, data)); - return QAsn1Element::fromVector(items); -} - -/*! - PKCS #12 key derivation. - - Some test vectors: - http://www.drh-consultancy.demon.co.uk/test.txt -*/ -static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r) -{ - const int u = 20; - const int v = 64; - - // password formatting - QByteArray passUnicode(passPhrase.size() * 2 + 2, '\0'); - char *p = passUnicode.data(); - for (int i = 0; i < passPhrase.size(); ++i) { - quint16 ch = passPhrase[i].unicode(); - *(p++) = (ch & 0xff00) >> 8; - *(p++) = (ch & 0xff); - } - - // prepare I - QByteArray D(64, id); - QByteArray S, P; - const int sSize = v * ((salt.size() + v - 1) / v); - S.resize(sSize); - for (int i = 0; i < sSize; ++i) { - S[i] = salt[i % salt.size()]; - } - const int pSize = v * ((passUnicode.size() + v - 1) / v); - P.resize(pSize); - for (int i = 0; i < pSize; ++i) { - P[i] = passUnicode[i % passUnicode.size()]; - } - QByteArray I = S + P; - - // apply hashing - const int c = (n + u - 1) / u; - QByteArray A; - QByteArray B; - B.resize(v); - QCryptographicHash hash(QCryptographicHash::Sha1); - for (int i = 0; i < c; ++i) { - // hash r iterations - QByteArray Ai = D + I; - for (int j = 0; j < r; ++j) { - hash.reset(); - hash.addData(Ai); - Ai = hash.result(); - } - - for (int j = 0; j < v; ++j) { - B[j] = Ai[j % u]; - } - - // modify I as Ij = (Ij + B + 1) modulo 2^v - for (int p = 0; p < I.size(); p += v) { - quint8 carry = 1; - for (int j = v - 1; j >= 0; --j) { - quint16 v = quint8(I[p+j]) + quint8(B[j]) + carry; - I[p+j] = v & 0xff; - carry = (v & 0xff00) >> 8; - } - } - A += Ai; - } - return A.left(n); -} - -static QByteArray _q_PKCS12_salt() -{ - QByteArray salt; - salt.resize(8); - for (int i = 0; i < salt.size(); ++i) { - salt[i] = (qrand() & 0xff); - } - return salt; -} - -static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert) -{ - QVector<QAsn1Element> items; - items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.3"); - - // certificate - QVector<QAsn1Element> certItems; - certItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.22.1"); - certItems << wrap(QAsn1Element::Context0Type, - QAsn1Element(QAsn1Element::OctetStringType, cert.toDer())); - items << wrap(QAsn1Element::Context0Type, - QAsn1Element::fromVector(certItems)); - - // local key id - const QByteArray localKeyId = cert.digest(QCryptographicHash::Sha1); - QVector<QAsn1Element> idItems; - idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21"); - idItems << wrap(QAsn1Element::SetType, - QAsn1Element(QAsn1Element::OctetStringType, localKeyId)); - items << wrap(QAsn1Element::SetType, QAsn1Element::fromVector(idItems)); - - // dump - QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items)); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -static QAsn1Element _q_PKCS12_key(const QSslKey &key) -{ - Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa); - - QVector<QAsn1Element> keyItems; - keyItems << QAsn1Element::fromInteger(0); - QVector<QAsn1Element> algoItems; - if (key.algorithm() == QSsl::Rsa) - algoItems << QAsn1Element::fromObjectId(RSA_ENCRYPTION_OID); - else if (key.algorithm() == QSsl::Dsa) - algoItems << QAsn1Element::fromObjectId(DSA_ENCRYPTION_OID); - algoItems << QAsn1Element(QAsn1Element::NullType); - keyItems << QAsn1Element::fromVector(algoItems); - keyItems << QAsn1Element(QAsn1Element::OctetStringType, key.toDer()); - return QAsn1Element::fromVector(keyItems); -} - -static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &passPhrase, const QByteArray &localKeyId) -{ - const int iterations = 2048; - QByteArray salt = _q_PKCS12_salt(); - QByteArray cKey = _q_PKCS12_keygen(1, salt, passPhrase, 24, iterations); - QByteArray cIv = _q_PKCS12_keygen(2, salt, passPhrase, 8, iterations); - - // prepare and encrypt data - QByteArray plain; - QDataStream plainStream(&plain, QIODevice::WriteOnly); - _q_PKCS12_key(key).write(plainStream); - QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc, - plain, cKey, cIv); - - QVector<QAsn1Element> items; - items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2"); - - // key - QVector<QAsn1Element> keyItems; - QVector<QAsn1Element> algoItems; - algoItems << QAsn1Element::fromObjectId("1.2.840.113549.1.12.1.3"); - QVector<QAsn1Element> paramItems; - paramItems << QAsn1Element(QAsn1Element::OctetStringType, salt); - paramItems << QAsn1Element::fromInteger(iterations); - algoItems << QAsn1Element::fromVector(paramItems); - keyItems << QAsn1Element::fromVector(algoItems); - keyItems << QAsn1Element(QAsn1Element::OctetStringType, crypted); - items << wrap(QAsn1Element::Context0Type, - QAsn1Element::fromVector(keyItems)); - - // local key id - QVector<QAsn1Element> idItems; - idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21"); - idItems << wrap(QAsn1Element::SetType, - QAsn1Element(QAsn1Element::OctetStringType, localKeyId)); - items << wrap(QAsn1Element::SetType, - QAsn1Element::fromVector(idItems)); - - // dump - QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items)); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase) -{ - QVector<QAsn1Element> items; - - // certs - for (int i = 0; i < certs.size(); ++i) - items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i])); - - // key - const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1); - items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId)); - - // dump - QAsn1Element root = QAsn1Element::fromVector(items); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - -static QAsn1Element _q_PKCS12_mac(const QByteArray &data, const QString &passPhrase) -{ - const int iterations = 2048; - - // salt generation - QByteArray macSalt = _q_PKCS12_salt(); - QByteArray key = _q_PKCS12_keygen(3, macSalt, passPhrase, 20, iterations); - - // HMAC calculation - QMessageAuthenticationCode hmac(QCryptographicHash::Sha1, key); - hmac.addData(data); - - QVector<QAsn1Element> algoItems; - algoItems << QAsn1Element::fromObjectId("1.3.14.3.2.26"); - algoItems << QAsn1Element(QAsn1Element::NullType); - - QVector<QAsn1Element> digestItems; - digestItems << QAsn1Element::fromVector(algoItems); - digestItems << QAsn1Element(QAsn1Element::OctetStringType, hmac.result()); - - QVector<QAsn1Element> macItems; - macItems << QAsn1Element::fromVector(digestItems); - macItems << QAsn1Element(QAsn1Element::OctetStringType, macSalt); - macItems << QAsn1Element::fromInteger(iterations); - return QAsn1Element::fromVector(macItems); -} - -QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase) -{ - QVector<QAsn1Element> items; - - // version - items << QAsn1Element::fromInteger(3); - - // auth safe - const QByteArray data = _q_PKCS12_bag(certs, key, passPhrase); - items << _q_PKCS7_data(data); - - // HMAC - items << _q_PKCS12_mac(data, passPhrase); - - // dump - QAsn1Element root = QAsn1Element::fromVector(items); - QByteArray ba; - QDataStream stream(&ba, QIODevice::WriteOnly); - root.write(stream); - return ba; -} - QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 2c6c35ef24..56764ebc7f 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -83,16 +83,14 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qscopedvaluerollback.h> +#if QT_CONFIG(ocsp) +#include <openssl/ocsp.h> +#endif + #include <string.h> QT_BEGIN_NAMESPACE -#if defined(Q_OS_WIN) - PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = nullptr; - PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = nullptr; - PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = nullptr; -#endif - bool QSslSocketPrivate::s_libraryLoaded = false; bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; @@ -204,8 +202,92 @@ QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) }; } +#if QT_CONFIG(ocsp) + +QSslError qt_OCSP_response_status_to_QSslError(long code) +{ + switch (code) { + case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: + return QSslError::OcspMalformedRequest; + case OCSP_RESPONSE_STATUS_INTERNALERROR: + return QSslError::OcspInternalError; + case OCSP_RESPONSE_STATUS_TRYLATER: + return QSslError::OcspTryLater; + case OCSP_RESPONSE_STATUS_SIGREQUIRED: + return QSslError::OcspSigRequred; + case OCSP_RESPONSE_STATUS_UNAUTHORIZED: + return QSslError::OcspUnauthorized; + case OCSP_RESPONSE_STATUS_SUCCESSFUL: + default: + return {}; + } + Q_UNREACHABLE(); +} + +bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert, X509 *issuer) +{ + // OCSP_basic_verify does verify that the responder is legit, the response is + // correctly signed, CertID is correct. But it does not know which certificate + // we were presented with by our peer, so it does not check if it's a response + // for our peer's certificate. + Q_ASSERT(singleResponse && peerCert && issuer); + + const OCSP_CERTID *certId = q_OCSP_SINGLERESP_get0_id(singleResponse); // Does not increment refcount. + if (!certId) { + qCWarning(lcSsl, "A SingleResponse without CertID"); + return false; + } + + ASN1_OBJECT *md = nullptr; + ASN1_INTEGER *reportedSerialNumber = nullptr; + const int result = q_OCSP_id_get0_info(nullptr, &md, nullptr, &reportedSerialNumber, const_cast<OCSP_CERTID *>(certId)); + if (result != 1 || !md || !reportedSerialNumber) { + qCWarning(lcSsl, "Failed to extract a hash and serial number from CertID structure"); + return false; + } + + if (!q_X509_get_serialNumber(peerCert)) { + // Is this possible at all? But we have to check this, + // ASN1_INTEGER_cmp (called from OCSP_id_cmp) dereferences + // without any checks at all. + qCWarning(lcSsl, "No serial number in peer's ceritificate"); + return false; + } + + const int nid = q_OBJ_obj2nid(md); + if (nid == NID_undef) { + qCWarning(lcSsl, "Unknown hash algorithm in CertID"); + return false; + } + + const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount. + if (!digest) { + qCWarning(lcSsl) << "No digest for nid" << nid; + return false; + } + + OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer); + if (!recreatedId) { + qCWarning(lcSsl, "Failed to re-create CertID"); + return false; + } + const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free); + + if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) { + qDebug(lcSsl, "Certificate ID mismatch"); + return false; + } + // Bingo! + return true; +} + +#endif // ocsp + // ### This list is shared between all threads, and protected by a -// mutex. Investigate using thread local storage instead. +// mutex. Investigate using thread local storage instead. Or better properly +// use OpenSSL's ability to attach application data to an SSL/SSL_CTX +// and extract it in a callback. See how it's done, for example, in PSK +// callback or in DTLS verification callback. struct QSslErrorList { QMutex mutex; @@ -412,6 +494,21 @@ bool QSslSocketBackendPrivate::initSslContext() } #endif +#if QT_CONFIG(ocsp) + if (configuration.ocspStaplingEnabled) { + if (mode == QSslSocket::SslServerMode) { + setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, + QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling")); + return false; + } + if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) { + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Failed to enable OCSP stapling")); + return false; + } + } +#endif // ocsp + return true; } @@ -548,22 +645,20 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() #endif QList<QSslCertificate> systemCerts; #if defined(Q_OS_WIN) - if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) { - HCERTSTORE hSystemStore; - hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT"); - if (hSystemStore) { - PCCERT_CONTEXT pc = nullptr; - while (1) { - pc = ptrCertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc); - if (!pc) - break; - QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded), - static_cast<int>(pc->cbCertEncoded)); - QSslCertificate cert(der, QSsl::Der); - systemCerts.append(cert); - } - ptrCertCloseStore(hSystemStore, 0); + HCERTSTORE hSystemStore; + hSystemStore = CertOpenSystemStoreW(0, L"ROOT"); + if (hSystemStore) { + PCCERT_CONTEXT pc = nullptr; + while (1) { + pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc); + if (!pc) + break; + QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded), + static_cast<int>(pc->cbCertEncoded)); + QSslCertificate cert(der, QSsl::Der); + systemCerts.append(cert); } + CertCloseStore(hSystemStore, 0); } #elif defined(Q_OS_UNIX) QSet<QString> certFiles; @@ -1001,9 +1096,33 @@ bool QSslSocketBackendPrivate::startHandshake() } } - bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); + const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer + || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer + && mode == QSslSocket::SslClientMode); + +#if QT_CONFIG(ocsp) + // For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early, + // if it's enabled in QSslSocket::SslServerMode. This can change. + if (!configuration.peerCertificate.isNull() && configuration.ocspStaplingEnabled && doVerifyPeer) { + if (!checkOcspStatus()) { + if (ocspErrors.isEmpty()) { + { + const ScopedBool bg(inSetAndEmitError, true); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription); + } + q->abort(); + return false; + } + + for (const QSslError &error : ocspErrors) { + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + } +#endif // ocsp // 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 @@ -1250,6 +1369,180 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi #endif +#if QT_CONFIG(ocsp) + +bool QSslSocketBackendPrivate::checkOcspStatus() +{ + Q_ASSERT(ssl); + Q_ASSERT(mode == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode + Q_ASSERT(configuration.peerVerifyMode != QSslSocket::VerifyNone); + + ocspErrorDescription.clear(); + ocspErrors.clear(); + + const unsigned char *responseData = nullptr; + const long responseLength = q_SSL_get_tlsext_status_ocsp_resp(ssl, &responseData); + if (responseLength <= 0 || !responseData) { + ocspErrors.push_back(QSslError::OcspNoResponseFound); + return false; + } + + OCSP_RESPONSE *response = q_d2i_OCSP_RESPONSE(nullptr, &responseData, responseLength); + if (!response) { + // Treat this as a fatal SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("Failed to decode OCSP response"); + return false; + } + const QSharedPointer<OCSP_RESPONSE> responseGuard(response, q_OCSP_RESPONSE_free); + + const int ocspStatus = q_OCSP_response_status(response); + if (ocspStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + // It's not a definitive response, it's an error message (not signed by the responder). + ocspErrors.push_back(qt_OCSP_response_status_to_QSslError(ocspStatus)); + return false; + } + + OCSP_BASICRESP *basicResponse = q_OCSP_response_get1_basic(response); + if (!basicResponse) { + // SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("Failed to extract basic OCSP response"); + return false; + } + const QSharedPointer<OCSP_BASICRESP> basicResponseGuard(basicResponse, q_OCSP_BASICRESP_free); + + SSL_CTX *ctx = q_SSL_get_SSL_CTX(ssl); // Does not increment refcount. + Q_ASSERT(ctx); + X509_STORE *store = q_SSL_CTX_get_cert_store(ctx); // Does not increment refcount. + if (!store) { + // SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("No certificate verification store, cannot verify OCSP response"); + return false; + } + + STACK_OF(X509) *peerChain = q_SSL_get_peer_cert_chain(ssl); // Does not increment refcount. + X509 *peerX509 = q_SSL_get_peer_certificate(ssl); + Q_ASSERT(peerChain || peerX509); + const QSharedPointer<X509> peerX509Guard(peerX509, q_X509_free); + // OCSP_basic_verify with 0 as verificationFlags: + // + // 0) Tries to find the OCSP responder's certificate in either peerChain + // or basicResponse->certs. If not found, verification fails. + // 1) It checks the signature using the responder's public key. + // 2) Then it tries to validate the responder's cert (building a chain + // etc.) + // 3) It checks CertID in response. + // 4) Ensures the responder is authorized to sign the status respond. + // + // Here it's important to notice that it calls X509_cert_verify and + // as a result, possibly, our verification callback. Given this callback + // at the moment uses a global variable, we have to lock. This will change + // as soon as we fix our verification procedure. + // Also note, OpenSSL prior to 1.0.2b would only use bs->certs to + // verify the responder's chain (see their commit 4ba9a4265bd). + // Working this around - is too much fuss for ancient versions we + // are dropping quite soon anyway. + { + const unsigned long verificationFlags = 0; + const QMutexLocker locker(&_q_sslErrorList()->mutex); + // Before unlocking the mutex, startHandshake() stores errors (found in SSL_connect() + // or SSL_accept()) into the local variable, so it's safe to clear it here - as soon + // as we managed to lock, whoever had the lock before, already stored their own copy + // of errors. + _q_sslErrorList()->errors.clear(); + const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags); + if (success <= 0 || _q_sslErrorList()->errors.size()) { + _q_sslErrorList()->errors.clear(); + ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted); + } + } + + if (q_OCSP_resp_count(basicResponse) != 1) { + ocspErrors.push_back(QSslError::OcspMalformedResponse); + return false; + } + + OCSP_SINGLERESP *singleResponse = q_OCSP_resp_get0(basicResponse, 0); + if (!singleResponse) { + ocspErrors.clear(); + // A fatal problem -> SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("Failed to decode a SingleResponse from OCSP status response"); + return false; + } + + // Let's make sure the response is for the correct certificate - we + // can re-create this CertID using our peer's certificate and its + // issuer's public key. + + bool matchFound = false; + if (configuration.peerCertificate.isSelfSigned()) { + matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, peerX509); + } else { + const STACK_OF(X509) *certs = q_SSL_get_peer_cert_chain(ssl); + if (!certs) // Oh, what a cataclysm! Last try: + certs = q_OCSP_resp_get0_certs(basicResponse); + if (certs) { + // It could be the first certificate in 'certs' is our peer's + // certificate. Since it was not captured by the 'self-signed' branch + // above, the CertID will not match and we'll just iterate on to the + // next certificate. So we start from 0, not 1. + for (int i = 0, e = q_sk_X509_num(certs); i < e; ++i) { + X509 *issuer = q_sk_X509_value(certs, i); + matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer); + if (matchFound) { + if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) + break; + matchFound = false; + } + } + } + } + + if (!matchFound) + ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate}); + + // Check if the response is valid time-wise: + ASN1_GENERALIZEDTIME *revTime = nullptr; + ASN1_GENERALIZEDTIME *thisUpdate = nullptr; + ASN1_GENERALIZEDTIME *nextUpdate = nullptr; + int reason; + const int certStatus = q_OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate); + if (!thisUpdate) { + // This is unexpected, treat as SslHandshakeError, OCSP_check_validity assumes this pointer + // to be != nullptr. + ocspErrors.clear(); + ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse"); + return false; + } + + // OCSP_check_validity(this, next, nsec, maxsec) does this check: + // this <= now <= next. They allow some freedom to account + // for delays/time inaccuracy. + // this > now + nsec ? -> NOT_YET_VALID + // if maxsec >= 0: + // now - maxsec > this ? -> TOO_OLD + // now - nsec > next ? -> EXPIRED + // next < this ? -> NEXT_BEFORE_THIS + // OK. + if (!q_OCSP_check_validity(thisUpdate, nextUpdate, 60, -1)) + ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate}); + + // And finally, the status: + switch (certStatus) { + case V_OCSP_CERTSTATUS_GOOD: + // This certificate was not found among the revoked ones. + break; + case V_OCSP_CERTSTATUS_REVOKED: + ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate}); + break; + case V_OCSP_CERTSTATUS_UNKNOWN: + ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate}); + } + + return !ocspErrors.size(); +} + +#endif // ocsp + void QSslSocketBackendPrivate::disconnectFromHost() { if (ssl) { diff --git a/src/network/ssl/qsslsocket_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp index 2a2667bd48..b60b8be41f 100644 --- a/src/network/ssl/qsslsocket_openssl11.cpp +++ b/src/network/ssl/qsslsocket_openssl11.cpp @@ -122,21 +122,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() #if QT_CONFIG(library) //load symbols needed to receive certificates from system store -#if defined(Q_OS_WIN) - HINSTANCE hLib = LoadLibraryW(L"Crypt32"); - if (hLib) { - ptrCertOpenSystemStoreW = reinterpret_cast<PtrCertOpenSystemStoreW>( - reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertOpenSystemStoreW"))); - ptrCertFindCertificateInStore = reinterpret_cast<PtrCertFindCertificateInStore>( - reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertFindCertificateInStore"))); - ptrCertCloseStore = reinterpret_cast<PtrCertCloseStore>( - reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertCloseStore"))); - if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) - qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen - } else { - qCWarning(lcSsl, "could not load crypt32 library"); // should never happen - } -#elif defined(Q_OS_QNX) +#if defined(Q_OS_QNX) s_loadRootCertsOnDemand = true; #elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h index fae007e12d..b94b05b765 100644 --- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -172,6 +172,10 @@ void q_BIO_set_init(BIO *a, int init); int q_BIO_get_shutdown(BIO *a); void q_BIO_set_shutdown(BIO *a, int shut); +#if QT_CONFIG(ocsp) +const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x); +#endif // ocsp + #define q_SSL_CTX_set_min_proto_version(ctx, version) \ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr) diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index c16b9d5f76..6396b44808 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -69,6 +69,9 @@ #include <QtNetwork/private/qtnetworkglobal_p.h> #include "qsslsocket_p.h" +#include <QtCore/qvector.h> +#include <QtCore/qstring.h> + #ifdef Q_OS_WIN #include <qt_windows.h> #if defined(OCSP_RESPONSE) @@ -152,6 +155,15 @@ public: void _q_caRootLoaded(QSslCertificate,QSslCertificate) override; #endif +#if QT_CONFIG(ocsp) + bool checkOcspStatus(); + + // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription) + QString ocspErrorDescription; + // These will go to sslErrors() + QVector<QSslError> ocspErrors; +#endif + Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index fd58e9548e..3688e8ffd3 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -199,6 +199,28 @@ DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallbac DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return) #endif // dtls +#if QT_CONFIG(ocsp) +DEFINEFUNC(const OCSP_CERTID *, OCSP_SINGLERESP_get0_id, const OCSP_SINGLERESP *x, x, return nullptr, return) +DEFINEFUNC3(OCSP_RESPONSE *, d2i_OCSP_RESPONSE, OCSP_RESPONSE **a, a, const unsigned char **in, in, long len, len, return nullptr, return) +DEFINEFUNC(void, OCSP_RESPONSE_free, OCSP_RESPONSE *rs, rs, return, DUMMYARG) +DEFINEFUNC(OCSP_BASICRESP *, OCSP_response_get1_basic, OCSP_RESPONSE *resp, resp, return nullptr, return) +DEFINEFUNC(void, OCSP_BASICRESP_free, OCSP_BASICRESP *bs, bs, return, DUMMYARG) +DEFINEFUNC(int, OCSP_response_status, OCSP_RESPONSE *resp, resp, return OCSP_RESPONSE_STATUS_INTERNALERROR, return) +DEFINEFUNC4(int, OCSP_basic_verify, OCSP_BASICRESP *bs, bs, STACK_OF(X509) *certs, certs, X509_STORE *st, st, unsigned long flags, flags, return -1, return) +DEFINEFUNC(int, OCSP_resp_count, OCSP_BASICRESP *bs, bs, return 0, return) +DEFINEFUNC2(OCSP_SINGLERESP *, OCSP_resp_get0, OCSP_BASICRESP *bs, bs, int idx, idx, return nullptr, return) +DEFINEFUNC5(int, OCSP_single_get0_status, OCSP_SINGLERESP *single, single, int *reason, reason, ASN1_GENERALIZEDTIME **revtime, revtime, + ASN1_GENERALIZEDTIME **thisupd, thisupd, ASN1_GENERALIZEDTIME **nextupd, nextupd, return -1, return) +DEFINEFUNC4(int, OCSP_check_validity, ASN1_GENERALIZEDTIME *thisupd, thisupd, ASN1_GENERALIZEDTIME *nextupd, nextupd, long nsec, nsec, long maxsec, maxsec, return 0, return) +DEFINEFUNC3(OCSP_CERTID *, OCSP_cert_to_id, const EVP_MD *dgst, dgst, X509 *subject, subject, X509 *issuer, issuer, return nullptr, return) +DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG) +DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd, + ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid, + return 0, return) +DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return) +DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return) +#endif // ocsp + DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG) DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return) DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG) @@ -329,6 +351,7 @@ DEFINEFUNC(const char *, SSLeay_version, int a, a, return nullptr, return) #endif // QT_CONFIG(opensslv11) DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return) +DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return) DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return) DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return) DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return) @@ -357,6 +380,7 @@ DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *typ DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return) DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return) DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return) +DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return) #ifndef OPENSSL_NO_DES DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return) @@ -368,12 +392,14 @@ DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return) DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return) DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return) +DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return) #ifndef OPENSSL_NO_EC DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return) #endif DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG) DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return) DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return) +DEFINEFUNC(DH *, EVP_PKEY_get1_DH, EVP_PKEY *a, a, return nullptr, return) #ifndef OPENSSL_NO_EC DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return) #endif @@ -399,6 +425,7 @@ DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_p DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, 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) +DEFINEFUNC7(int, PEM_write_bio_PrivateKey, BIO *a, a, EVP_PKEY *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) #ifndef OPENSSL_NO_EC DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *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 @@ -411,6 +438,7 @@ DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_pass #endif 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(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return) #ifndef OPENSSL_NO_EC DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return) #endif @@ -469,6 +497,7 @@ DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return) #endif DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return) +DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return) DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, 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) @@ -1016,7 +1045,25 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(BIO_meth_set_create) RESOLVEFUNC(BIO_meth_set_destroy) #endif // dtls - +#if QT_CONFIG(ocsp) + RESOLVEFUNC(OCSP_SINGLERESP_get0_id) + RESOLVEFUNC(d2i_OCSP_RESPONSE) + RESOLVEFUNC(OCSP_RESPONSE_free) + RESOLVEFUNC(OCSP_response_status) + RESOLVEFUNC(OCSP_response_get1_basic) + RESOLVEFUNC(OCSP_BASICRESP_free) + RESOLVEFUNC(OCSP_basic_verify) + RESOLVEFUNC(OCSP_resp_count) + RESOLVEFUNC(OCSP_resp_get0) + RESOLVEFUNC(OCSP_single_get0_status) + RESOLVEFUNC(OCSP_check_validity) + RESOLVEFUNC(OCSP_cert_to_id) + RESOLVEFUNC(OCSP_id_get0_info) + RESOLVEFUNC(OCSP_resp_get0_certs); + RESOLVEFUNC(OCSP_CERTID_free) + RESOLVEFUNC(OCSP_cert_to_id) + RESOLVEFUNC(OCSP_id_cmp) +#endif // ocsp RESOLVEFUNC(BIO_set_data) RESOLVEFUNC(BIO_get_data) RESOLVEFUNC(BIO_set_init) @@ -1123,6 +1170,7 @@ bool q_resolveOpenSslSymbols() #endif // !opensslv11 RESOLVEFUNC(ASN1_INTEGER_get) + RESOLVEFUNC(ASN1_INTEGER_cmp) RESOLVEFUNC(ASN1_STRING_length) RESOLVEFUNC(ASN1_STRING_to_UTF8) RESOLVEFUNC(BIO_ctrl) @@ -1159,6 +1207,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_CipherInit_ex) RESOLVEFUNC(EVP_CipherUpdate) RESOLVEFUNC(EVP_CipherFinal) + RESOLVEFUNC(EVP_get_digestbyname) #ifndef OPENSSL_NO_DES RESOLVEFUNC(EVP_des_cbc) RESOLVEFUNC(EVP_des_ede3_cbc) @@ -1170,12 +1219,14 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_PKEY_assign) RESOLVEFUNC(EVP_PKEY_set1_RSA) RESOLVEFUNC(EVP_PKEY_set1_DSA) + RESOLVEFUNC(EVP_PKEY_set1_DH) #ifndef OPENSSL_NO_EC RESOLVEFUNC(EVP_PKEY_set1_EC_KEY) #endif RESOLVEFUNC(EVP_PKEY_free) RESOLVEFUNC(EVP_PKEY_get1_DSA) RESOLVEFUNC(EVP_PKEY_get1_RSA) + RESOLVEFUNC(EVP_PKEY_get1_DH) #ifndef OPENSSL_NO_EC RESOLVEFUNC(EVP_PKEY_get1_EC_KEY) #endif @@ -1199,6 +1250,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(PEM_read_bio_DHparams) RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) + RESOLVEFUNC(PEM_write_bio_PrivateKey) #ifndef OPENSSL_NO_EC RESOLVEFUNC(PEM_write_bio_ECPrivateKey) #endif @@ -1212,6 +1264,7 @@ bool q_resolveOpenSslSymbols() #endif RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY) RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_PUBKEY) #ifndef OPENSSL_NO_EC RESOLVEFUNC(PEM_write_bio_EC_PUBKEY) #endif @@ -1257,6 +1310,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_get_peer_certificate) RESOLVEFUNC(SSL_get_verify_result) RESOLVEFUNC(SSL_new) + RESOLVEFUNC(SSL_get_SSL_CTX) RESOLVEFUNC(SSL_ctrl) RESOLVEFUNC(SSL_read) RESOLVEFUNC(SSL_set_accept_state) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index bfdfbf0efc..b5aac1c2ae 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -72,6 +72,10 @@ #include "qsslsocket_openssl_p.h" #include <QtCore/qglobal.h> +#if QT_CONFIG(ocsp) +#include <openssl/ocsp.h> +#endif + QT_BEGIN_NAMESPACE #define DUMMYARG @@ -224,6 +228,7 @@ QT_BEGIN_NAMESPACE bool q_resolveOpenSslSymbols(); long q_ASN1_INTEGER_get(ASN1_INTEGER *a); +int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y); int q_ASN1_STRING_length(ASN1_STRING *a); int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b); long q_BIO_ctrl(BIO *a, int b, long c, void *d); @@ -267,6 +272,8 @@ int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc); int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); +const EVP_MD *q_EVP_get_digestbyname(const char *name); + #ifndef OPENSSL_NO_DES const EVP_CIPHER *q_EVP_des_cbc(); const EVP_CIPHER *q_EVP_des_ede3_cbc(); @@ -278,12 +285,14 @@ const EVP_MD *q_EVP_sha1(); int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c); Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b); int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); +int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b); #ifndef OPENSSL_NO_EC int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b); #endif 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); +DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a); #ifndef OPENSSL_NO_EC EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a); #endif @@ -297,6 +306,7 @@ int q_OBJ_ln2nid(const char *s); int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj); int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name); int q_OBJ_obj2nid(const ASN1_OBJECT *a); +#define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(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, @@ -314,6 +324,8 @@ int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned 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); +int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d, + int e, pem_password_cb *f, void *g); #ifndef OPENSSL_NO_EC int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d, int e, pem_password_cb *f, void *g); @@ -327,6 +339,7 @@ EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d #endif int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b); int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b); +int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b); #ifndef OPENSSL_NO_EC int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b); #endif @@ -380,6 +393,7 @@ STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); X509 *q_SSL_get_peer_certificate(SSL *a); long q_SSL_get_verify_result(const SSL *a); SSL *q_SSL_new(SSL_CTX *a); +SSL_CTX *q_SSL_get_SSL_CTX(SSL *a); long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg); int q_SSL_read(SSL *a, void *b, int c); void q_SSL_set_bio(SSL *a, BIO *b, BIO *c); @@ -571,6 +585,36 @@ int q_BIO_set_ex_data(BIO *b, int idx, void *data); class QDateTime; QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime); +#ifndef OPENSSL_NO_TLSEXT + +#define q_SSL_set_tlsext_status_type(ssl, type) q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr) + +#endif // OPENSSL_NO_TLSEXT + +#if QT_CONFIG(ocsp) + +OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len); +void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs); +int q_OCSP_response_status(OCSP_RESPONSE *resp); +OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp); +void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs); +int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags); +int q_OCSP_resp_count(OCSP_BASICRESP *bs); +OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx); +int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, + ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd); +int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec); +int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash, + ASN1_INTEGER **pserial, OCSP_CERTID *cid); +const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs); +OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer); +void q_OCSP_CERTID_free(OCSP_CERTID *cid); +int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b); + +#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg) + +#endif // ocsp + QT_END_NAMESPACE #endif diff --git a/src/network/ssl/qsslsocket_opensslpre11.cpp b/src/network/ssl/qsslsocket_opensslpre11.cpp index bc4fd9dc85..f5aab821ea 100644 --- a/src/network/ssl/qsslsocket_opensslpre11.cpp +++ b/src/network/ssl/qsslsocket_opensslpre11.cpp @@ -251,21 +251,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() #if QT_CONFIG(library) //load symbols needed to receive certificates from system store -#if defined(Q_OS_WIN) - HINSTANCE hLib = LoadLibraryW(L"Crypt32"); - if (hLib) { - ptrCertOpenSystemStoreW = reinterpret_cast<PtrCertOpenSystemStoreW>( - reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertOpenSystemStoreW"))); - ptrCertFindCertificateInStore = reinterpret_cast<PtrCertFindCertificateInStore>( - reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertFindCertificateInStore"))); - ptrCertCloseStore = reinterpret_cast<PtrCertCloseStore>( - reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertCloseStore"))); - if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) - qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen - } else { - qCWarning(lcSsl, "could not load crypt32 library"); // should never happen - } -#elif defined(Q_OS_QNX) +#if defined(Q_OS_QNX) s_loadRootCertsOnDemand = true; #elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h index b7bac5d2a2..daf46f485c 100644 --- a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h +++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h @@ -218,6 +218,7 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); #define q_SSL_SESSION_get_ticket_lifetime_hint(s) ((s)->tlsext_tick_lifetime_hint) #define q_RSA_bits(rsa) q_BN_num_bits((rsa)->n) #define q_DSA_bits(dsa) q_BN_num_bits((dsa)->p) +#define q_DH_bits(dsa) q_BN_num_bits((dh)->p) #define q_X509_STORE_set_verify_cb(s,c) X509_STORE_set_verify_cb_func((s),(c)) char *q_CONF_get1_default_config_file(); diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 6f34c6c888..2f394f013b 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -89,14 +89,6 @@ QT_BEGIN_NAMESPACE typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*); #endif -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(HCRYPTPROV_LEGACY, LPCWSTR); - typedef PCCERT_CONTEXT (WINAPI *PtrCertFindCertificateInStore)(HCERTSTORE, DWORD, DWORD, DWORD, const void*, PCCERT_CONTEXT); - typedef BOOL (WINAPI *PtrCertCloseStore)(HCERTSTORE, DWORD); -#endif // Q_OS_WIN && !Q_OS_WINRT - - - class QSslSocketPrivate : public QTcpSocketPrivate { Q_DECLARE_PUBLIC(QSslSocket) @@ -155,12 +147,6 @@ public: const QString &peerName); Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW; - static PtrCertFindCertificateInStore ptrCertFindCertificateInStore; - static PtrCertCloseStore ptrCertCloseStore; -#endif // Q_OS_WIN && !Q_OS_WINRT - // The socket itself, including private slots. QTcpSocket *plainSocket; void createPlainSocket(QIODevice::OpenMode openMode); @@ -222,6 +208,11 @@ protected: bool flushTriggered; }; +#if QT_CONFIG(securetransport) +// Implemented in qsslsocket_qt.cpp +QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase); +#endif + QT_END_NAMESPACE #endif diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp new file mode 100644 index 0000000000..2c78f0f4e7 --- /dev/null +++ b/src/network/ssl/qsslsocket_qt.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qmessageauthenticationcode.h> + +#include "qsslsocket_p.h" +#include "qasn1element_p.h" +#include "qsslkey_p.h" + +QT_BEGIN_NAMESPACE + +/* + PKCS12 helpers. +*/ + +static QAsn1Element wrap(quint8 type, const QAsn1Element &child) +{ + QByteArray value; + QDataStream stream(&value, QIODevice::WriteOnly); + child.write(stream); + return QAsn1Element(type, value); +} + +static QAsn1Element _q_PKCS7_data(const QByteArray &data) +{ + QVector<QAsn1Element> items; + items << QAsn1Element::fromObjectId("1.2.840.113549.1.7.1"); + items << wrap(QAsn1Element::Context0Type, + QAsn1Element(QAsn1Element::OctetStringType, data)); + return QAsn1Element::fromVector(items); +} + +/*! + PKCS #12 key derivation. + + Some test vectors: + http://www.drh-consultancy.demon.co.uk/test.txt +*/ +static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r) +{ + const int u = 20; + const int v = 64; + + // password formatting + QByteArray passUnicode(passPhrase.size() * 2 + 2, '\0'); + char *p = passUnicode.data(); + for (int i = 0; i < passPhrase.size(); ++i) { + quint16 ch = passPhrase[i].unicode(); + *(p++) = (ch & 0xff00) >> 8; + *(p++) = (ch & 0xff); + } + + // prepare I + QByteArray D(64, id); + QByteArray S, P; + const int sSize = v * ((salt.size() + v - 1) / v); + S.resize(sSize); + for (int i = 0; i < sSize; ++i) { + S[i] = salt[i % salt.size()]; + } + const int pSize = v * ((passUnicode.size() + v - 1) / v); + P.resize(pSize); + for (int i = 0; i < pSize; ++i) { + P[i] = passUnicode[i % passUnicode.size()]; + } + QByteArray I = S + P; + + // apply hashing + const int c = (n + u - 1) / u; + QByteArray A; + QByteArray B; + B.resize(v); + QCryptographicHash hash(QCryptographicHash::Sha1); + for (int i = 0; i < c; ++i) { + // hash r iterations + QByteArray Ai = D + I; + for (int j = 0; j < r; ++j) { + hash.reset(); + hash.addData(Ai); + Ai = hash.result(); + } + + for (int j = 0; j < v; ++j) { + B[j] = Ai[j % u]; + } + + // modify I as Ij = (Ij + B + 1) modulo 2^v + for (int p = 0; p < I.size(); p += v) { + quint8 carry = 1; + for (int j = v - 1; j >= 0; --j) { + quint16 v = quint8(I[p+j]) + quint8(B[j]) + carry; + I[p+j] = v & 0xff; + carry = (v & 0xff00) >> 8; + } + } + A += Ai; + } + return A.left(n); +} + +static QByteArray _q_PKCS12_salt() +{ + QByteArray salt; + salt.resize(8); + for (int i = 0; i < salt.size(); ++i) { + salt[i] = (qrand() & 0xff); + } + return salt; +} + +static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert) +{ + QVector<QAsn1Element> items; + items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.3"); + + // certificate + QVector<QAsn1Element> certItems; + certItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.22.1"); + certItems << wrap(QAsn1Element::Context0Type, + QAsn1Element(QAsn1Element::OctetStringType, cert.toDer())); + items << wrap(QAsn1Element::Context0Type, + QAsn1Element::fromVector(certItems)); + + // local key id + const QByteArray localKeyId = cert.digest(QCryptographicHash::Sha1); + QVector<QAsn1Element> idItems; + idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21"); + idItems << wrap(QAsn1Element::SetType, + QAsn1Element(QAsn1Element::OctetStringType, localKeyId)); + items << wrap(QAsn1Element::SetType, QAsn1Element::fromVector(idItems)); + + // dump + QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items)); + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + root.write(stream); + return ba; +} + +static QAsn1Element _q_PKCS12_key(const QSslKey &key) +{ + Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa); + + QVector<QAsn1Element> keyItems; + keyItems << QAsn1Element::fromInteger(0); + QVector<QAsn1Element> algoItems; + if (key.algorithm() == QSsl::Rsa) + algoItems << QAsn1Element::fromObjectId(RSA_ENCRYPTION_OID); + else if (key.algorithm() == QSsl::Dsa) + algoItems << QAsn1Element::fromObjectId(DSA_ENCRYPTION_OID); + algoItems << QAsn1Element(QAsn1Element::NullType); + keyItems << QAsn1Element::fromVector(algoItems); + keyItems << QAsn1Element(QAsn1Element::OctetStringType, key.toDer()); + return QAsn1Element::fromVector(keyItems); +} + +static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &passPhrase, const QByteArray &localKeyId) +{ + const int iterations = 2048; + QByteArray salt = _q_PKCS12_salt(); + QByteArray cKey = _q_PKCS12_keygen(1, salt, passPhrase, 24, iterations); + QByteArray cIv = _q_PKCS12_keygen(2, salt, passPhrase, 8, iterations); + + // prepare and encrypt data + QByteArray plain; + QDataStream plainStream(&plain, QIODevice::WriteOnly); + _q_PKCS12_key(key).write(plainStream); + QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc, + plain, cKey, cIv); + + QVector<QAsn1Element> items; + items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2"); + + // key + QVector<QAsn1Element> keyItems; + QVector<QAsn1Element> algoItems; + algoItems << QAsn1Element::fromObjectId("1.2.840.113549.1.12.1.3"); + QVector<QAsn1Element> paramItems; + paramItems << QAsn1Element(QAsn1Element::OctetStringType, salt); + paramItems << QAsn1Element::fromInteger(iterations); + algoItems << QAsn1Element::fromVector(paramItems); + keyItems << QAsn1Element::fromVector(algoItems); + keyItems << QAsn1Element(QAsn1Element::OctetStringType, crypted); + items << wrap(QAsn1Element::Context0Type, + QAsn1Element::fromVector(keyItems)); + + // local key id + QVector<QAsn1Element> idItems; + idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21"); + idItems << wrap(QAsn1Element::SetType, + QAsn1Element(QAsn1Element::OctetStringType, localKeyId)); + items << wrap(QAsn1Element::SetType, + QAsn1Element::fromVector(idItems)); + + // dump + QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items)); + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + root.write(stream); + return ba; +} + +static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase) +{ + QVector<QAsn1Element> items; + + // certs + for (int i = 0; i < certs.size(); ++i) + items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i])); + + // key + const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1); + items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId)); + + // dump + QAsn1Element root = QAsn1Element::fromVector(items); + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + root.write(stream); + return ba; +} + +static QAsn1Element _q_PKCS12_mac(const QByteArray &data, const QString &passPhrase) +{ + const int iterations = 2048; + + // salt generation + QByteArray macSalt = _q_PKCS12_salt(); + QByteArray key = _q_PKCS12_keygen(3, macSalt, passPhrase, 20, iterations); + + // HMAC calculation + QMessageAuthenticationCode hmac(QCryptographicHash::Sha1, key); + hmac.addData(data); + + QVector<QAsn1Element> algoItems; + algoItems << QAsn1Element::fromObjectId("1.3.14.3.2.26"); + algoItems << QAsn1Element(QAsn1Element::NullType); + + QVector<QAsn1Element> digestItems; + digestItems << QAsn1Element::fromVector(algoItems); + digestItems << QAsn1Element(QAsn1Element::OctetStringType, hmac.result()); + + QVector<QAsn1Element> macItems; + macItems << QAsn1Element::fromVector(digestItems); + macItems << QAsn1Element(QAsn1Element::OctetStringType, macSalt); + macItems << QAsn1Element::fromInteger(iterations); + return QAsn1Element::fromVector(macItems); +} + +QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase) +{ + QVector<QAsn1Element> items; + + // version + items << QAsn1Element::fromInteger(3); + + // auth safe + const QByteArray data = _q_PKCS12_bag(certs, key, passPhrase); + items << _q_PKCS7_data(data); + + // HMAC + items << _q_PKCS12_mac(data, passPhrase); + + // dump + QAsn1Element root = QAsn1Element::fromVector(items); + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + root.write(stream); + return ba; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri index 6975264038..b5603c1258 100644 --- a/src/network/ssl/ssl.pri +++ b/src/network/ssl/ssl.pri @@ -56,6 +56,7 @@ qtConfig(ssl) { ssl/qsslkey_mac.cpp \ ssl/qsslsocket_mac_shared.cpp \ ssl/qsslsocket_mac.cpp \ + ssl/qsslsocket_qt.cpp \ ssl/qsslellipticcurve_dummy.cpp } |