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