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