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

source code of qtbase/src/network/ssl/qsslcertificate.cpp