1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 
41 /*!
42     \class QSslCertificate
43     \brief The QSslCertificate class provides a convenient API for an X509 certificate.
44     \since 4.3
45 
46     \reentrant
47     \ingroup network
48     \ingroup ssl
49     \ingroup shared
50     \inmodule QtNetwork
51 
52     QSslCertificate stores an X509 certificate, and is commonly used
53     to verify the identity and store information about the local host,
54     a remotely connected peer, or a trusted third party Certificate
55     Authority.
56 
57     There are many ways to construct a QSslCertificate. The most
58     common way is to call QSslSocket::peerCertificate(), which returns
59     a QSslCertificate object, or QSslSocket::peerCertificateChain(),
60     which returns a list of them. You can also load certificates from
61     a DER (binary) or PEM (Base64) encoded bundle, typically stored as
62     one or more local files, or in a Qt Resource.
63 
64     You can call isNull() to check if your certificate is null. By default,
65     QSslCertificate constructs a null certificate. A null certificate is
66     invalid, but an invalid certificate is not necessarily null. If you want
67     to reset all contents in a certificate, call clear().
68 
69     After loading a certificate, you can find information about the
70     certificate, its subject, and its issuer, by calling one of the
71     many accessor functions, including version(), serialNumber(),
72     issuerInfo() and subjectInfo(). You can call effectiveDate() and
73     expiryDate() to check when the certificate starts being
74     effective and when it expires.
75     The publicKey() function returns the certificate
76     subject's public key as a QSslKey. You can call issuerInfo() or
77     subjectInfo() to get detailed information about the certificate
78     issuer and its subject.
79 
80     Internally, QSslCertificate is stored as an X509 structure. You
81     can access this handle by calling handle(), but the results are
82     likely to not be portable.
83 
84     \sa QSslSocket, QSslKey, QSslCipher, QSslError
85 */
86 
87 /*!
88     \enum QSslCertificate::SubjectInfo
89 
90     Describes keys that you can pass to QSslCertificate::issuerInfo() or
91     QSslCertificate::subjectInfo() to get information about the certificate
92     issuer or subject.
93 
94     \value Organization "O" The name of the organization.
95 
96     \value CommonName "CN" The common name; most often this is used to store
97     the host name.
98 
99     \value LocalityName "L" The locality.
100 
101     \value OrganizationalUnitName "OU" The organizational unit name.
102 
103     \value CountryName "C" The country.
104 
105     \value StateOrProvinceName "ST" The state or province.
106 
107     \value DistinguishedNameQualifier The distinguished name qualifier
108 
109     \value SerialNumber The certificate's serial number
110 
111     \value EmailAddress The email address associated with the certificate
112 */
113 
114 /*!
115     \enum QSslCertificate::PatternSyntax
116     \since 5.15
117 
118     The syntax used to interpret the meaning of the pattern.
119 
120     \value RegularExpression A rich Perl-like pattern matching syntax.
121 
122     \value Wildcard This provides a simple pattern matching syntax
123     similar to that used by shells (command interpreters) for "file
124     globbing". See \l {QRegularExpression#Wildcard matching}
125     {QRegularExpression Wildcard Matching}.
126 
127     \value FixedString The pattern is a fixed string. This is
128     equivalent to using the RegularExpression pattern on a string in
129     which all metacharacters are escaped using escape(). This is the
130     default.
131 */
132 
133 #include <QtNetwork/qtnetworkglobal.h>
134 #ifndef QT_NO_OPENSSL
135 #include "qsslsocket_openssl_symbols_p.h"
136 #endif
137 #if defined(Q_OS_WINRT) && QT_CONFIG(ssl)
138 #include "qsslsocket_winrt_p.h"
139 #endif
140 #ifdef QT_SECURETRANSPORT
141 #include "qsslsocket_mac_p.h"
142 #endif
143 #if QT_CONFIG(schannel)
144 #include "qsslsocket_schannel_p.h"
145 #endif
146 #if QT_CONFIG(regularexpression)
147 #include "qregularexpression.h"
148 #endif
149 #include "qssl_p.h"
150 #include "qsslcertificate.h"
151 #include "qsslcertificate_p.h"
152 #ifndef QT_NO_SSL
153 #include "qsslkey_p.h"
154 #endif
155 
156 #include <QtCore/qdir.h>
157 #include <QtCore/qdiriterator.h>
158 #include <QtCore/qfile.h>
159 
160 QT_BEGIN_NAMESPACE
161 
162 /*!
163     Constructs a QSslCertificate by reading \a format encoded data
164     from \a device and using the first certificate found. You can
165     later call isNull() to see if \a device contained a certificate,
166     and if this certificate was loaded successfully.
167 */
QSslCertificate(QIODevice * device,QSsl::EncodingFormat format)168 QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
169     : d(new QSslCertificatePrivate)
170 {
171 #ifndef QT_NO_OPENSSL
172     QSslSocketPrivate::ensureInitialized();
173     if (device && QSslSocket::supportsSsl())
174 #else
175     if (device)
176 #endif
177         d->init(device->readAll(), format);
178 }
179 
180 /*!
181     Constructs a QSslCertificate by parsing the \a format encoded
182     \a data and using the first available certificate found. You can
183     later call isNull() to see if \a data contained a certificate,
184     and if this certificate was loaded successfully.
185 */
QSslCertificate(const QByteArray & data,QSsl::EncodingFormat format)186 QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
187     : d(new QSslCertificatePrivate)
188 {
189 #ifndef QT_NO_OPENSSL
190     QSslSocketPrivate::ensureInitialized();
191     if (QSslSocket::supportsSsl())
192 #endif
193         d->init(data, format);
194 }
195 
196 /*!
197     Constructs an identical copy of \a other.
198 */
QSslCertificate(const QSslCertificate & other)199 QSslCertificate::QSslCertificate(const QSslCertificate &other) : d(other.d)
200 {
201 }
202 
203 /*!
204     Destroys the QSslCertificate.
205 */
~QSslCertificate()206 QSslCertificate::~QSslCertificate()
207 {
208 }
209 
210 /*!
211     Copies the contents of \a other into this certificate, making the two
212     certificates identical.
213 */
operator =(const QSslCertificate & other)214 QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
215 {
216     d = other.d;
217     return *this;
218 }
219 
220 /*!
221     \fn void QSslCertificate::swap(QSslCertificate &other)
222     \since 5.0
223 
224     Swaps this certificate instance with \a other. This function is
225     very fast and never fails.
226 */
227 
228 /*!
229     \fn bool QSslCertificate::operator==(const QSslCertificate &other) const
230 
231     Returns \c true if this certificate is the same as \a other; otherwise
232     returns \c false.
233 */
234 
235 /*!
236     \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
237 
238     Returns \c true if this certificate is not the same as \a other; otherwise
239     returns \c false.
240 */
241 
242 /*!
243     \fn bool QSslCertificate::isNull() const
244 
245     Returns \c true if this is a null certificate (i.e., a certificate
246     with no contents); otherwise returns \c false.
247 
248     By default, QSslCertificate constructs a null certificate.
249 
250     \sa clear()
251 */
252 
253 #if QT_DEPRECATED_SINCE(5,0)
254 /*!
255     \fn bool QSslCertificate::isValid() const
256     \obsolete
257 
258     To verify a certificate, use verify().
259     To check if a certificate is blacklisted, use isBlacklisted().
260     To check if a certificate has expired or is not yet valid, compare
261     expiryDate() and effectiveDate() with QDateTime::currentDateTime()
262 
263     This function checks that the current
264     date-time is within the date-time range during which the
265     certificate is considered valid, and checks that the
266     certificate is not in a blacklist of fraudulent certificates.
267 
268     \sa isNull(), verify(), isBlacklisted(), expiryDate(), effectiveDate()
269 */
270 #endif
271 
272 /*!
273     Returns \c true if this certificate is blacklisted; otherwise
274     returns \c false.
275 
276     \sa isNull()
277 */
isBlacklisted() const278 bool QSslCertificate::isBlacklisted() const
279 {
280     return QSslCertificatePrivate::isBlacklisted(*this);
281 }
282 
283 /*!
284     \fn bool QSslCertificate::isSelfSigned() const
285     \since 5.4
286 
287     Returns \c true if this certificate is self signed; otherwise
288     returns \c false.
289 
290     A certificate is considered self-signed its issuer and subject
291     are identical.
292 */
293 
294 /*!
295     Clears the contents of this certificate, making it a null
296     certificate.
297 
298     \sa isNull()
299 */
clear()300 void QSslCertificate::clear()
301 {
302     if (isNull())
303         return;
304     d = new QSslCertificatePrivate;
305 }
306 
307 /*!
308     \fn QByteArray QSslCertificate::version() const
309     Returns the certificate's version string.
310 */
311 
312 /*!
313     \fn QByteArray QSslCertificate::serialNumber() const
314 
315     Returns the certificate's serial number string in hexadecimal format.
316 */
317 
318 /*!
319     Returns a cryptographic digest of this certificate. By default,
320     an MD5 digest will be generated, but you can also specify a
321     custom \a algorithm.
322 */
digest(QCryptographicHash::Algorithm algorithm) const323 QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const
324 {
325     return QCryptographicHash::hash(toDer(), algorithm);
326 }
327 
328 /*!
329   \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const
330 
331   Returns the issuer information for the \a subject from the
332   certificate, or an empty list if there is no information for
333   \a subject in the certificate. There can be more than one entry
334   of each type.
335 
336   \sa subjectInfo()
337 */
338 
339 /*!
340   \fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
341 
342   Returns the issuer information for \a attribute from the certificate,
343   or an empty list if there is no information for \a attribute in the
344   certificate. There can be more than one entry for an attribute.
345 
346   \sa subjectInfo()
347 */
348 
349 /*!
350   \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
351 
352   Returns the information for the \a subject, or an empty list if
353   there is no information for \a subject in the certificate. There
354   can be more than one entry of each type.
355 
356     \sa issuerInfo()
357 */
358 
359 /*!
360     \fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
361 
362     Returns the subject information for \a attribute, or an empty list if
363     there is no information for \a attribute in the certificate. There
364     can be more than one entry for an attribute.
365 
366     \sa issuerInfo()
367 */
368 
369 /*!
370     \fn QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
371 
372     \since 5.0
373     Returns a list of the attributes that have values in the subject
374     information of this certificate. The information associated
375     with a given attribute can be accessed using the subjectInfo()
376     method. Note that this list may include the OIDs for any
377     elements that are not known by the SSL backend.
378 
379     \sa subjectInfo()
380 */
381 
382 /*!
383     \fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
384 
385     \since 5.0
386     Returns a list of the attributes that have values in the issuer
387     information of this certificate. The information associated
388     with a given attribute can be accessed using the issuerInfo()
389     method. Note that this list may include the OIDs for any
390     elements that are not known by the SSL backend.
391 
392     \sa subjectInfo()
393 */
394 
395 #if QT_DEPRECATED_SINCE(5,0)
396 /*!
397   \fn QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
398   \obsolete
399 
400   Use QSslCertificate::subjectAlternativeNames();
401 */
402 #endif
403 
404 /*!
405   \fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
406 
407   Returns the list of alternative subject names for this
408   certificate. The alternative names typically contain host
409   names, optionally with wildcards, that are valid for this
410   certificate.
411 
412   These names are tested against the connected peer's host name, if
413   either the subject information for \l CommonName doesn't define a
414   valid host name, or the subject info name doesn't match the peer's
415   host name.
416 
417   \sa subjectInfo()
418 */
419 
420 /*!
421   \fn QDateTime QSslCertificate::effectiveDate() const
422 
423   Returns the date-time that the certificate becomes valid, or an
424   empty QDateTime if this is a null certificate.
425 
426   \sa expiryDate()
427 */
428 
429 /*!
430   \fn QDateTime QSslCertificate::expiryDate() const
431 
432   Returns the date-time that the certificate expires, or an empty
433   QDateTime if this is a null certificate.
434 
435     \sa effectiveDate()
436 */
437 
438 /*!
439     \fn Qt::HANDLE QSslCertificate::handle() const
440     Returns a pointer to the native certificate handle, if there is
441     one, else \nullptr.
442 
443     You can use this handle, together with the native API, to access
444     extended information about the certificate.
445 
446     \warning Use of this function has a high probability of being
447     non-portable, and its return value may vary from platform to
448     platform or change from minor release to minor release.
449 */
450 
451 /*!
452     \fn QSslKey QSslCertificate::publicKey() const
453     Returns the certificate subject's public key.
454 */
455 
456 /*!
457     \fn QList<QSslCertificateExtension> QSslCertificate::extensions() const
458 
459     Returns a list containing the X509 extensions of this certificate.
460     \since 5.0
461  */
462 
463 /*!
464     \fn QByteArray QSslCertificate::toPem() const
465 
466     Returns this certificate converted to a PEM (Base64) encoded
467     representation.
468 */
469 
470 /*!
471     \fn QByteArray QSslCertificate::toDer() const
472 
473     Returns this certificate converted to a DER (binary) encoded
474     representation.
475 */
476 
477 /*!
478     \fn QString QSslCertificate::toText() const
479 
480     Returns this certificate converted to a human-readable text
481     representation.
482 
483     \since 5.0
484 */
485 
486 #if QT_DEPRECATED_SINCE(5,15)
487 /*!
488     \obsolete
489 
490     Searches all files in the \a path for certificates encoded in the
491     specified \a format and returns them in a list. \a path must be a file
492     or a pattern matching one or more files, as specified by \a syntax.
493 
494     Example:
495 
496     \snippet code/src_network_ssl_qsslcertificate.cpp 0
497 
498     \sa fromData()
499 */
fromPath(const QString & path,QSsl::EncodingFormat format,QRegExp::PatternSyntax syntax)500 QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
501                                                  QSsl::EncodingFormat format,
502                                                  QRegExp::PatternSyntax syntax)
503 {
504     // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
505 
506     // make sure to use the same path separators on Windows and Unix like systems.
507     QString sourcePath = QDir::fromNativeSeparators(path);
508 
509     // Find the path without the filename
510     QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(QLatin1Char('/')));
511 
512     // Check if the path contains any special chars
513     int pos = -1;
514     if (syntax == QRegExp::Wildcard)
515         pos = pathPrefix.indexOf(QRegExp(QLatin1String("[*?[]")));
516     else if (syntax != QRegExp::FixedString)
517         pos = sourcePath.indexOf(QRegExp(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
518     if (pos != -1) {
519         // there was a special char in the path so cut of the part containing that char.
520         pathPrefix = pathPrefix.left(pos);
521         const int lastIndexOfSlash = pathPrefix.lastIndexOf(QLatin1Char('/'));
522         if (lastIndexOfSlash != -1)
523             pathPrefix = pathPrefix.left(lastIndexOfSlash);
524         else
525             pathPrefix.clear();
526     } else {
527         // Check if the path is a file.
528         if (QFileInfo(sourcePath).isFile()) {
529             QFile file(sourcePath);
530             QIODevice::OpenMode openMode = QIODevice::ReadOnly;
531             if (format == QSsl::Pem)
532                 openMode |= QIODevice::Text;
533             if (file.open(openMode))
534                 return QSslCertificate::fromData(file.readAll(), format);
535             return QList<QSslCertificate>();
536         }
537     }
538 
539     // Special case - if the prefix ends up being nothing, use "." instead.
540     int startIndex = 0;
541     if (pathPrefix.isEmpty()) {
542         pathPrefix = QLatin1String(".");
543         startIndex = 2;
544     }
545 
546     // The path can be a file or directory.
547     QList<QSslCertificate> certs;
548     QRegExp pattern(sourcePath, Qt::CaseSensitive, syntax);
549     QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
550     while (it.hasNext()) {
551         QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
552         if (!pattern.exactMatch(filePath))
553             continue;
554 
555         QFile file(filePath);
556         QIODevice::OpenMode openMode = QIODevice::ReadOnly;
557         if (format == QSsl::Pem)
558             openMode |= QIODevice::Text;
559         if (file.open(openMode))
560             certs += QSslCertificate::fromData(file.readAll(), format);
561     }
562     return certs;
563 }
564 #endif // QT_DEPRECATED_SINCE(5,15)
565 
566 /*!
567     \since 5.15
568 
569     Searches all files in the \a path for certificates encoded in the
570     specified \a format and returns them in a list. \a path must be a file
571     or a pattern matching one or more files, as specified by \a syntax.
572 
573     Example:
574 
575     \snippet code/src_network_ssl_qsslcertificate.cpp 1
576 
577     \sa fromData()
578 */
fromPath(const QString & path,QSsl::EncodingFormat format,PatternSyntax syntax)579 QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
580                                                  QSsl::EncodingFormat format,
581                                                  PatternSyntax syntax)
582 {
583     // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
584 
585     // make sure to use the same path separators on Windows and Unix like systems.
586     QString sourcePath = QDir::fromNativeSeparators(path);
587 
588     // Find the path without the filename
589     QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(QLatin1Char('/')));
590 
591     // Check if the path contains any special chars
592     int pos = -1;
593 
594 #if QT_CONFIG(regularexpression)
595     if (syntax == PatternSyntax::Wildcard)
596         pos = pathPrefix.indexOf(QRegularExpression(QLatin1String("[*?[]")));
597     else if (syntax == PatternSyntax::RegularExpression)
598         pos = sourcePath.indexOf(QRegularExpression(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
599 #else
600     if (syntax == PatternSyntax::Wildcard || syntax == PatternSyntax::RegExp)
601         qWarning("Regular expression support is disabled in this build. Only fixed string can be searched");
602         return QList<QSslCertificate>();
603 #endif
604 
605     if (pos != -1) {
606         // there was a special char in the path so cut of the part containing that char.
607         pathPrefix = pathPrefix.left(pos);
608         const int lastIndexOfSlash = pathPrefix.lastIndexOf(QLatin1Char('/'));
609         if (lastIndexOfSlash != -1)
610             pathPrefix = pathPrefix.left(lastIndexOfSlash);
611         else
612             pathPrefix.clear();
613     } else {
614         // Check if the path is a file.
615         if (QFileInfo(sourcePath).isFile()) {
616             QFile file(sourcePath);
617             QIODevice::OpenMode openMode = QIODevice::ReadOnly;
618             if (format == QSsl::Pem)
619                 openMode |= QIODevice::Text;
620             if (file.open(openMode))
621                 return QSslCertificate::fromData(file.readAll(), format);
622             return QList<QSslCertificate>();
623         }
624     }
625 
626     // Special case - if the prefix ends up being nothing, use "." instead.
627     int startIndex = 0;
628     if (pathPrefix.isEmpty()) {
629         pathPrefix = QLatin1String(".");
630         startIndex = 2;
631     }
632 
633     // The path can be a file or directory.
634     QList<QSslCertificate> certs;
635 
636 #if QT_CONFIG(regularexpression)
637     if (syntax == PatternSyntax::Wildcard)
638         sourcePath = QRegularExpression::wildcardToRegularExpression(sourcePath);
639 
640     QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath));
641 #endif
642 
643     QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
644     while (it.hasNext()) {
645         QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
646 
647 #if QT_CONFIG(regularexpression)
648         if (!pattern.match(filePath).hasMatch())
649             continue;
650 #else
651         if (sourcePath != filePath)
652             continue;
653 #endif
654 
655         QFile file(filePath);
656         QIODevice::OpenMode openMode = QIODevice::ReadOnly;
657         if (format == QSsl::Pem)
658             openMode |= QIODevice::Text;
659         if (file.open(openMode))
660             certs += QSslCertificate::fromData(file.readAll(), format);
661     }
662     return certs;
663 }
664 
665 /*!
666     Searches for and parses all certificates in \a device that are
667     encoded in the specified \a format and returns them in a list of
668     certificates.
669 
670     \sa fromData()
671 */
fromDevice(QIODevice * device,QSsl::EncodingFormat format)672 QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format)
673 {
674     if (!device) {
675         qCWarning(lcSsl, "QSslCertificate::fromDevice: cannot read from a null device");
676         return QList<QSslCertificate>();
677     }
678     return fromData(device->readAll(), format);
679 }
680 
681 /*!
682     Searches for and parses all certificates in \a data that are
683     encoded in the specified \a format and returns them in a list of
684     certificates.
685 
686     \sa fromDevice()
687 */
fromData(const QByteArray & data,QSsl::EncodingFormat format)688 QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
689 {
690     return (format == QSsl::Pem)
691         ? QSslCertificatePrivate::certificatesFromPem(data)
692         : QSslCertificatePrivate::certificatesFromDer(data);
693 }
694 
695 #ifndef QT_NO_SSL
696 
697 /*!
698     Verifies a certificate chain. The chain to be verified is passed in the
699     \a certificateChain parameter. The first certificate in the list should
700     be the leaf certificate of the chain to be verified. If \a hostName is
701     specified then the certificate is also checked to see if it is valid for
702     the specified host name.
703 
704     Note that the root (CA) certificate should not be included in the list to be verified,
705     this will be looked up automatically using the CA list specified in the
706     default QSslConfiguration, and, in addition, if possible, CA certificates loaded on
707     demand on Unix and Windows.
708 
709     \since 5.0
710  */
711 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
verify(const QList<QSslCertificate> & certificateChain,const QString & hostName)712 QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
713 #else
714 QList<QSslError> QSslCertificate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
715 #endif
716 {
717     return QSslSocketBackendPrivate::verify(certificateChain, hostName);
718 }
719 
720 /*!
721   \since 5.4
722 
723   Imports a PKCS#12 (pfx) file from the specified \a device. A PKCS#12
724   file is a bundle that can contain a number of certificates and keys.
725   This method reads a single \a key, its \a certificate and any
726   associated \a caCertificates from the bundle. If a \a passPhrase is
727   specified then this will be used to decrypt the bundle. Returns
728   \c true if the PKCS#12 file was successfully loaded.
729 
730   \note The \a device must be open and ready to be read from.
731  */
importPkcs12(QIODevice * device,QSslKey * key,QSslCertificate * certificate,QList<QSslCertificate> * caCertificates,const QByteArray & passPhrase)732 bool QSslCertificate::importPkcs12(QIODevice *device,
733                                    QSslKey *key, QSslCertificate *certificate,
734                                    QList<QSslCertificate> *caCertificates,
735                                    const QByteArray &passPhrase)
736 {
737     return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase);
738 }
739 
740 #endif
741 
742 // These certificates are known to be fraudulent and were created during the comodo
743 // compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
744 static const char *const certificate_blacklist[] = {
745     "04:7e:cb:e9:fc:a5:5f:7b:d0:9e:ae:36:e1:0c:ae:1e", "mail.google.com", // Comodo
746     "f5:c8:6a:f3:61:62:f1:3a:64:f5:4f:6d:c9:58:7c:06", "www.google.com", // Comodo
747     "d7:55:8f:da:f5:f1:10:5b:b2:13:28:2b:70:77:29:a3", "login.yahoo.com", // Comodo
748     "39:2a:43:4f:0e:07:df:1f:8a:a3:05:de:34:e0:c2:29", "login.yahoo.com", // Comodo
749     "3e:75:ce:d4:6b:69:30:21:21:88:30:ae:86:a8:2a:71", "login.yahoo.com", // Comodo
750     "e9:02:8b:95:78:e4:15:dc:1a:71:0a:2b:88:15:44:47", "login.skype.com", // Comodo
751     "92:39:d5:34:8f:40:d1:69:5a:74:54:70:e1:f2:3f:43", "addons.mozilla.org", // Comodo
752     "b0:b7:13:3e:d0:96:f9:b5:6f:ae:91:c8:74:bd:3a:c0", "login.live.com", // Comodo
753     "d8:f3:5f:4e:b7:87:2b:2d:ab:06:92:e3:15:38:2f:b0", "global trustee", // Comodo
754 
755     "05:e2:e6:a4:cd:09:ea:54:d6:65:b0:75:fe:22:a2:56", "*.google.com", // leaf certificate issued by DigiNotar
756     "0c:76:da:9c:91:0c:4e:2c:9e:fe:15:d0:58:93:3c:4c", "DigiNotar Root CA", // DigiNotar root
757     "f1:4a:13:f4:87:2b:56:dc:39:df:84:ca:7a:a1:06:49", "DigiNotar Services CA", // DigiNotar intermediate signed by DigiNotar Root
758     "36:16:71:55:43:42:1b:9d:e6:cb:a3:64:41:df:24:38", "DigiNotar Services 1024 CA", // DigiNotar intermediate signed by DigiNotar Root
759     "0a:82:bd:1e:14:4e:88:14:d7:5b:1a:55:27:be:bf:3e", "DigiNotar Root CA G2", // other DigiNotar Root CA
760     "a4:b6:ce:e3:2e:d3:35:46:26:3c:b3:55:3a:a8:92:21", "CertiID Enterprise Certificate Authority", // DigiNotar intermediate signed by "DigiNotar Root CA G2"
761     "5b:d5:60:9c:64:17:68:cf:21:0e:35:fd:fb:05:ad:41", "DigiNotar Qualified CA", // DigiNotar intermediate signed by DigiNotar Root
762 
763     "46:9c:2c:b0",                                     "DigiNotar Services 1024 CA", // DigiNotar intermediate cross-signed by Entrust
764     "07:27:10:0d",                                     "DigiNotar Cyber CA", // DigiNotar intermediate cross-signed by CyberTrust
765     "07:27:0f:f9",                                     "DigiNotar Cyber CA", // DigiNotar intermediate cross-signed by CyberTrust
766     "07:27:10:03",                                     "DigiNotar Cyber CA", // DigiNotar intermediate cross-signed by CyberTrust
767     "01:31:69:b0",                                     "DigiNotar PKIoverheid CA Overheid en Bedrijven", // DigiNotar intermediate cross-signed by the Dutch government
768     "01:31:34:bf",                                     "DigiNotar PKIoverheid CA Organisatie - G2", // DigiNotar intermediate cross-signed by the Dutch government
769     "d6:d0:29:77:f1:49:fd:1a:83:f2:b9:ea:94:8c:5c:b4", "DigiNotar Extended Validation CA", // DigiNotar intermediate signed by DigiNotar EV Root
770     "1e:7d:7a:53:3d:45:30:41:96:40:0f:71:48:1f:45:04", "DigiNotar Public CA 2025", // DigiNotar intermediate
771 //    "(has not been seen in the wild so far)", "DigiNotar Public CA - G2", // DigiNotar intermediate
772 //    "(has not been seen in the wild so far)", "Koninklijke Notariele Beroepsorganisatie CA", // compromised during DigiNotar breach
773 //    "(has not been seen in the wild so far)", "Stichting TTP Infos CA," // compromised during DigiNotar breach
774     "46:9c:2c:af",                                     "DigiNotar Root CA", // DigiNotar intermediate cross-signed by Entrust
775     "46:9c:3c:c9",                                     "DigiNotar Root CA", // DigiNotar intermediate cross-signed by Entrust
776 
777     "07:27:14:a9",                                     "Digisign Server ID (Enrich)", // (Malaysian) Digicert Sdn. Bhd. cross-signed by Verizon CyberTrust
778     "4c:0e:63:6a",                                     "Digisign Server ID - (Enrich)", // (Malaysian) Digicert Sdn. Bhd. cross-signed by Entrust
779     "72:03:21:05:c5:0c:08:57:3d:8e:a5:30:4e:fe:e8:b0", "UTN-USERFirst-Hardware", // comodogate test certificate
780     "41",                                              "MD5 Collisions Inc. (http://www.phreedom.org/md5)", // http://www.phreedom.org/research/rogue-ca/
781 
782     "08:27",                                           "*.EGO.GOV.TR", // Turktrust mis-issued intermediate certificate
783     "08:64",                                           "e-islem.kktcmerkezbankasi.org", // Turktrust mis-issued intermediate certificate
784 
785     "03:1d:a7",                                        "AC DG Tr\xC3\xA9sor SSL", // intermediate certificate linking back to ANSSI French National Security Agency
786     "27:83",                                           "NIC Certifying Authority", // intermediate certificate from NIC India (2007)
787     "27:92",                                           "NIC CA 2011", // intermediate certificate from NIC India (2011)
788     "27:b1",                                           "NIC CA 2014", // intermediate certificate from NIC India (2014)
789     nullptr
790 };
791 
isBlacklisted(const QSslCertificate & certificate)792 bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
793 {
794     for (int a = 0; certificate_blacklist[a] != nullptr; a++) {
795         QString blacklistedCommonName = QString::fromUtf8(certificate_blacklist[(a+1)]);
796         if (certificate.serialNumber() == certificate_blacklist[a++] &&
797             (certificate.subjectInfo(QSslCertificate::CommonName).contains(blacklistedCommonName) ||
798              certificate.issuerInfo(QSslCertificate::CommonName).contains(blacklistedCommonName)))
799             return true;
800     }
801     return false;
802 }
803 
subjectInfoToString(QSslCertificate::SubjectInfo info)804 QByteArray QSslCertificatePrivate::subjectInfoToString(QSslCertificate::SubjectInfo info)
805 {
806     QByteArray str;
807     switch (info) {
808     case QSslCertificate::Organization: str = QByteArray("O"); break;
809     case QSslCertificate::CommonName: str = QByteArray("CN"); break;
810     case QSslCertificate::LocalityName: str = QByteArray("L"); break;
811     case QSslCertificate::OrganizationalUnitName: str = QByteArray("OU"); break;
812     case QSslCertificate::CountryName: str = QByteArray("C"); break;
813     case QSslCertificate::StateOrProvinceName: str = QByteArray("ST"); break;
814     case QSslCertificate::DistinguishedNameQualifier: str = QByteArray("dnQualifier"); break;
815     case QSslCertificate::SerialNumber: str = QByteArray("serialNumber"); break;
816     case QSslCertificate::EmailAddress: str = QByteArray("emailAddress"); break;
817     }
818     return str;
819 }
820 
821 /*!
822     \since 5.12
823 
824     Returns a name that describes the issuer. It returns the QSslCertificate::CommonName
825     if available, otherwise falls back to the first QSslCertificate::Organization or the
826     first QSslCertificate::OrganizationalUnitName.
827 
828     \sa issuerInfo()
829 */
issuerDisplayName() const830 QString QSslCertificate::issuerDisplayName() const
831 {
832     QStringList names;
833     names = issuerInfo(QSslCertificate::CommonName);
834     if (!names.isEmpty())
835         return names.first();
836     names = issuerInfo(QSslCertificate::Organization);
837     if (!names.isEmpty())
838         return names.first();
839     names = issuerInfo(QSslCertificate::OrganizationalUnitName);
840     if (!names.isEmpty())
841         return names.first();
842 
843     return QString();
844 }
845 
846 /*!
847     \since 5.12
848 
849     Returns a name that describes the subject. It returns the QSslCertificate::CommonName
850     if available, otherwise falls back to the first QSslCertificate::Organization or the
851     first QSslCertificate::OrganizationalUnitName.
852 
853     \sa subjectInfo()
854 */
subjectDisplayName() const855 QString QSslCertificate::subjectDisplayName() const
856 {
857     QStringList names;
858     names = subjectInfo(QSslCertificate::CommonName);
859     if (!names.isEmpty())
860         return names.first();
861     names = subjectInfo(QSslCertificate::Organization);
862     if (!names.isEmpty())
863         return names.first();
864     names = subjectInfo(QSslCertificate::OrganizationalUnitName);
865     if (!names.isEmpty())
866         return names.first();
867 
868     return QString();
869 }
870 
871 /*!
872     \fn uint qHash(const QSslCertificate &key, uint seed)
873 
874     Returns the hash value for the \a key, using \a seed to seed the calculation.
875     \since 5.4
876     \relates QHash
877 */
878 
879 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug debug,const QSslCertificate & certificate)880 QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
881 {
882     QDebugStateSaver saver(debug);
883     debug.resetFormat().nospace();
884     debug << "QSslCertificate("
885           << certificate.version()
886           << ", " << certificate.serialNumber()
887           << ", " << certificate.digest().toBase64()
888           << ", " << certificate.issuerDisplayName()
889           << ", " << certificate.subjectDisplayName()
890           << ", " << certificate.subjectAlternativeNames()
891 #if QT_CONFIG(datestring)
892           << ", " << certificate.effectiveDate()
893           << ", " << certificate.expiryDate()
894 #endif
895           << ')';
896     return debug;
897 }
operator <<(QDebug debug,QSslCertificate::SubjectInfo info)898 QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info)
899 {
900     switch (info) {
901     case QSslCertificate::Organization: debug << "Organization"; break;
902     case QSslCertificate::CommonName: debug << "CommonName"; break;
903     case QSslCertificate::CountryName: debug << "CountryName"; break;
904     case QSslCertificate::LocalityName: debug << "LocalityName"; break;
905     case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break;
906     case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break;
907     case QSslCertificate::DistinguishedNameQualifier: debug << "DistinguishedNameQualifier"; break;
908     case QSslCertificate::SerialNumber: debug << "SerialNumber"; break;
909     case QSslCertificate::EmailAddress: debug << "EmailAddress"; break;
910     }
911     return debug;
912 }
913 #endif
914 
915 QT_END_NAMESPACE
916