1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtNetwork module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 \class QSslCertificate
45 \brief The QSslCertificate class provides a convenient API for an X509 certificate.
53 QSslCertificate stores an X509 certificate, and is commonly used
54 to verify the identity and store information about the local host,
55 a remotely connected peer, or a trusted third party Certificate
58 There are many ways to construct a QSslCertificate. The most
59 common way is to call QSslSocket::peerCertificate(), which returns
60 a QSslCertificate object, or QSslSocket::peerCertificateChain(),
61 which returns a list of them. You can also load certificates from
62 a DER (binary) or PEM (Base64) encoded bundle, typically stored as
63 one or more local files, or in a Qt Resource.
65 You can call isNull() to check if your certificate is null. By
66 default, QSslCertificate constructs a null certificate. To check
67 if the certificate is valid, call isValid(). A null certificate is
68 invalid, but an invalid certificate is not necessarily null. If
69 you want to reset all contents in a certificate, call clear().
71 After loading a certificate, you can find information about the
72 certificate, its subject, and its issuer, by calling one of the
73 many accessor functions, including version(), serialNumber(),
74 issuerInfo() and subjectInfo(). You can call effectiveDate() and
75 expiryDate() to check when the certificate starts being
76 effective and when it expires.
77 The publicKey() function returns the certificate
78 subject's public key as a QSslKey. You can call issuerInfo() or
79 subjectInfo() to get detailed information about the certificate
80 issuer and its subject.
82 Internally, QSslCertificate is stored as an X509 structure. You
83 can access this handle by calling handle(), but the results are
84 likely to not be portable.
86 \sa QSslSocket, QSslKey, QSslCipher, QSslError
90 \enum QSslCertificate::SubjectInfo
92 Describes keys that you can pass to QSslCertificate::issuerInfo() or
93 QSslCertificate::subjectInfo() to get information about the certificate
96 \value Organization "O" The name of the organization.
98 \value CommonName "CN" The common name; most often this is used to store
101 \value LocalityName "L" The locality.
103 \value OrganizationalUnitName "OU" The organizational unit name.
105 \value CountryName "C" The country.
107 \value StateOrProvinceName "ST" The state or province.
110 #include "qsslsocket_openssl_symbols_p.h"
111 #include "qsslcertificate.h"
112 #include "qsslcertificate_p.h"
114 #include "qsslkey_p.h"
116 #include <QtCore/qatomic.h>
117 #include <QtCore/qdatetime.h>
118 #include <QtCore/qdebug.h>
119 #include <QtCore/qdir.h>
120 #include <QtCore/qdiriterator.h>
121 #include <QtCore/qfile.h>
122 #include <QtCore/qfileinfo.h>
123 #include <QtCore/qmap.h>
124 #include <QtCore/qstring.h>
125 #include <QtCore/qstringlist.h>
129 // forward declaration
130 static QMap<QString, QString> _q_mapFromOnelineName(char *name);
133 Constructs a QSslCertificate by reading \a format encoded data
134 from \a device and using the first certificate found. You can
135 later call isNull() to see if \a device contained a certificate,
136 and if this certificate was loaded successfully.
138 QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
139 : d(new QSslCertificatePrivate)
141 QSslSocketPrivate::ensureInitialized();
143 d->init(device->readAll(), format);
147 Constructs a QSslCertificate by parsing the \a format encoded
148 \a data and using the first available certificate found. You can
149 later call isNull() to see if \a data contained a certificate,
150 and if this certificate was loaded successfully.
152 QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
153 : d(new QSslCertificatePrivate)
155 QSslSocketPrivate::ensureInitialized();
156 d->init(data, format);
160 Constructs an identical copy of \a other.
162 QSslCertificate::QSslCertificate(const QSslCertificate &other) : d(other.d)
167 Destroys the QSslCertificate.
169 QSslCertificate::~QSslCertificate()
174 Copies the contents of \a other into this certificate, making the two
175 certificates identical.
177 QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
184 Returns true if this certificate is the same as \a other; otherwise
187 bool QSslCertificate::operator==(const QSslCertificate &other) const
191 if (d->null && other.d->null)
193 if (d->x509 && other.d->x509)
194 return q_X509_cmp(d->x509, other.d->x509) == 0;
199 \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
201 Returns true if this certificate is not the same as \a other; otherwise
206 Returns true if this is a null certificate (i.e., a certificate
207 with no contents); otherwise returns false.
209 By default, QSslCertificate constructs a null certificate.
211 \sa isValid(), clear()
213 bool QSslCertificate::isNull() const
219 Returns true if this certificate is valid; otherwise returns
222 Note: Currently, this function checks that the current
223 data-time is within the date-time range during which the
224 certificate is considered valid, and checks that the
225 certificate is not in a blacklist of fraudulent certificates.
229 bool QSslCertificate::isValid() const
231 const QDateTime currentTime = QDateTime::currentDateTime();
232 return currentTime >= d->notValidBefore &&
233 currentTime <= d->notValidAfter &&
234 ! QSslCertificatePrivate::isBlacklisted(*this);
238 Clears the contents of this certificate, making it a null
243 void QSslCertificate::clear()
247 d = new QSslCertificatePrivate;
251 Returns the certificate's version string.
253 QByteArray QSslCertificate::version() const
255 if (d->versionString.isEmpty() && d->x509)
257 QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1);
259 return d->versionString;
263 Returns the certificate's serial number string in decimal format.
264 In case the serial number cannot be converted to decimal format
265 (i.e. if it is bigger than 4294967295, which means it does not fit into 4 bytes),
266 its hexadecimal version is returned.
268 QByteArray QSslCertificate::serialNumber() const
270 if (d->serialNumberString.isEmpty() && d->x509) {
271 ASN1_INTEGER *serialNumber = d->x509->cert_info->serialNumber;
272 // if we cannot convert to a long, just output the hexadecimal number
273 if (serialNumber->length > 4) {
274 QByteArray hexString;
275 hexString.reserve(serialNumber->length * 3);
276 for (int a = 0; a < serialNumber->length; ++a) {
277 hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0');
281 d->serialNumberString = hexString;
283 d->serialNumberString = QByteArray::number(qlonglong(q_ASN1_INTEGER_get(serialNumber)));
286 return d->serialNumberString;
290 Returns a cryptographic digest of this certificate. By default,
291 an MD5 digest will be generated, but you can also specify a
294 QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const
296 return QCryptographicHash::hash(toDer(), algorithm);
299 static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info)
303 case QSslCertificate::Organization: str = QLatin1String("O"); break;
304 case QSslCertificate::CommonName: str = QLatin1String("CN"); break;
305 case QSslCertificate::LocalityName: str = QLatin1String("L"); break;
306 case QSslCertificate::OrganizationalUnitName: str = QLatin1String("OU"); break;
307 case QSslCertificate::CountryName: str = QLatin1String("C"); break;
308 case QSslCertificate::StateOrProvinceName: str = QLatin1String("ST"); break;
314 \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const
316 Returns the issuer information for the \a subject from the
317 certificate, or an empty string if there is no information for
318 \a subject in the certificate.
322 QString QSslCertificate::issuerInfo(SubjectInfo info) const
325 if (d->issuerInfo.isEmpty() && d->x509)
327 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
329 return d->issuerInfo.value(_q_SubjectInfoToString(info));
333 Returns the issuer information for \a tag from the certificate,
334 or an empty string if there is no information for \a tag in the
339 QString QSslCertificate::issuerInfo(const QByteArray &tag) const
342 if (d->issuerInfo.isEmpty() && d->x509)
344 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
346 return d->issuerInfo.value(QString::fromLatin1(tag));
351 \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
353 Returns the information for the \a subject, or an empty string if
354 there is no information for \a subject in the certificate.
358 QString QSslCertificate::subjectInfo(SubjectInfo info) const
361 if (d->subjectInfo.isEmpty() && d->x509)
363 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
365 return d->subjectInfo.value(_q_SubjectInfoToString(info));
369 Returns the subject information for \a tag, or an empty string if
370 there is no information for \a tag in the certificate.
374 QString QSslCertificate::subjectInfo(const QByteArray &tag) const
377 if (d->subjectInfo.isEmpty() && d->x509)
379 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
381 return d->subjectInfo.value(QString::fromLatin1(tag));
385 Returns the list of alternative subject names for this
386 certificate. The alternate subject names typically contain host
387 names, optionally with wildcards, that are valid for this
390 These names are tested against the connected peer's host name, if
391 either the subject information for \l CommonName doesn't define a
392 valid host name, or the subject info name doesn't match the peer's
397 QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
399 QMultiMap<QSsl::AlternateNameEntryType, QString> result;
404 STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0);
407 for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
408 const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
409 if (genName->type != GEN_DNS && genName->type != GEN_EMAIL)
412 int len = q_ASN1_STRING_length(genName->d.ia5);
413 if (len < 0 || len >= 8192) {
418 const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_data(genName->d.ia5));
419 const QString altName = QString::fromLatin1(altNameStr, len);
420 if (genName->type == GEN_DNS)
421 result.insert(QSsl::DnsEntry, altName);
422 else if (genName->type == GEN_EMAIL)
423 result.insert(QSsl::EmailEntry, altName);
425 q_sk_pop_free((STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_sk_free));
432 Returns the date-time that the certificate becomes valid, or an
433 empty QDateTime if this is a null certificate.
437 QDateTime QSslCertificate::effectiveDate() const
439 return d->notValidBefore;
443 Returns the date-time that the certificate expires, or an empty
444 QDateTime if this is a null certificate.
448 QDateTime QSslCertificate::expiryDate() const
450 return d->notValidAfter;
454 Returns a pointer to the native certificate handle, if there is
455 one, or a null pointer otherwise.
457 You can use this handle, together with the native API, to access
458 extended information about the certificate.
460 \warning Use of this function has a high probability of being
461 non-portable, and its return value may vary from platform to
462 platform or change from minor release to minor release.
464 Qt::HANDLE QSslCertificate::handle() const
466 return Qt::HANDLE(d->x509);
470 Returns the certificate subject's public key.
472 QSslKey QSslCertificate::publicKey() const
479 key.d->type = QSsl::PublicKey;
480 X509_PUBKEY *xkey = d->x509->cert_info->key;
481 EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey);
484 if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) {
485 key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
486 key.d->algorithm = QSsl::Rsa;
487 key.d->isNull = false;
488 } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
489 key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
490 key.d->algorithm = QSsl::Dsa;
491 key.d->isNull = false;
492 } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) {
498 q_EVP_PKEY_free(pkey);
503 Returns this certificate converted to a PEM (Base64) encoded
506 QByteArray QSslCertificate::toPem() const
510 return d->QByteArray_from_X509(d->x509, QSsl::Pem);
514 Returns this certificate converted to a DER (binary) encoded
517 QByteArray QSslCertificate::toDer() const
521 return d->QByteArray_from_X509(d->x509, QSsl::Der);
525 Searches all files in the \a path for certificates encoded in the
526 specified \a format and returns them in a list. \e must be a file or a
527 pattern matching one or more files, as specified by \a syntax.
531 \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0
535 QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
536 QSsl::EncodingFormat format,
537 QRegExp::PatternSyntax syntax)
539 // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
541 if (syntax == QRegExp::Wildcard)
542 pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\*\\?\\[\\]]")));
543 else if (syntax != QRegExp::FixedString)
544 pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
545 QString pathPrefix = path.left(pos); // == path if pos < 0
547 pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/')));
549 // Special case - if the prefix ends up being nothing, use "." instead and
550 // chop off the first two characters from the glob'ed paths.
552 if (pathPrefix.trimmed().isEmpty()) {
553 if(path.startsWith(QLatin1Char('/'))) {
554 pathPrefix = path.left(path.indexOf(QRegExp(QLatin1String("[\\*\\?\\[]"))));
555 pathPrefix = path.left(path.lastIndexOf(QLatin1Char('/')));
558 pathPrefix = QLatin1String(".");
562 // The path is a file.
563 if (pos == -1 && QFileInfo(pathPrefix).isFile()) {
564 QFile file(pathPrefix);
565 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
566 return QSslCertificate::fromData(file.readAll(),format);
567 return QList<QSslCertificate>();
570 // The path can be a file or directory.
571 QList<QSslCertificate> certs;
572 QRegExp pattern(path, Qt::CaseSensitive, syntax);
573 QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
574 while (it.hasNext()) {
575 QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
576 if (!pattern.exactMatch(filePath))
579 QFile file(filePath);
580 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
581 certs += QSslCertificate::fromData(file.readAll(),format);
587 Searches for and parses all certificates in \a device that are
588 encoded in the specified \a format and returns them in a list of
593 QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format)
596 qWarning("QSslCertificate::fromDevice: cannot read from a null device");
597 return QList<QSslCertificate>();
599 return fromData(device->readAll(), format);
603 Searches for and parses all certificates in \a data that are
604 encoded in the specified \a format and returns them in a list of
609 QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
611 return (format == QSsl::Pem)
612 ? QSslCertificatePrivate::certificatesFromPem(data)
613 : QSslCertificatePrivate::certificatesFromDer(data);
616 void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
618 if (!data.isEmpty()) {
619 QList<QSslCertificate> certs = (format == QSsl::Pem)
620 ? certificatesFromPem(data, 1)
621 : certificatesFromDer(data, 1);
622 if (!certs.isEmpty()) {
623 *this = *certs.first().d;
625 x509 = q_X509_dup(x509);
630 #define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
631 #define ENDCERTSTRING "-----END CERTIFICATE-----"
633 // ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
634 QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
637 qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
641 // Use i2d_X509 to convert the X509 to an array.
642 int length = q_i2d_X509(x509, 0);
644 array.resize(length);
645 char *data = array.data();
646 char **dataP = &data;
647 unsigned char **dataPu = (unsigned char **)dataP;
648 if (q_i2d_X509(x509, dataPu) < 0)
651 if (format == QSsl::Der)
654 // Convert to Base64 - wrap at 64 characters.
655 array = array.toBase64();
657 for (int i = 0; i <= array.size() - 64; i += 64) {
658 tmp += QByteArray::fromRawData(array.data() + i, 64);
661 if (int remainder = array.size() % 64) {
662 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
666 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
669 static QMap<QString, QString> _q_mapFromOnelineName(char *name)
671 QMap<QString, QString> info;
672 QString infoStr = QString::fromLocal8Bit(name);
675 // ### The right-hand encoding seems to allow hex (Regulierungsbeh\xC8orde)
676 //entry.replace(QLatin1String("\\x"), QLatin1String("%"));
677 //entry = QUrl::fromPercentEncoding(entry.toLatin1());
678 // ### See RFC-4630 for more details!
680 QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)"));
683 while ((pos = rx.indexIn(infoStr, pos)) != -1) {
684 const QString name = rx.cap(1);
686 QString value = rx.cap(2);
687 const int valuePos = rx.pos(2);
689 const int next = rx.indexIn(value);
691 info.insert(name, value);
695 value = value.left(next);
696 info.insert(name, value);
697 pos = valuePos + value.length();
703 QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
705 QSslCertificate certificate;
706 if (!x509 || !QSslSocket::supportsSsl())
709 ASN1_TIME *nbef = q_X509_get_notBefore(x509);
710 ASN1_TIME *naft = q_X509_get_notAfter(x509);
711 certificate.d->notValidBefore = q_getTimeFromASN1(nbef);
712 certificate.d->notValidAfter = q_getTimeFromASN1(naft);
713 certificate.d->null = false;
714 certificate.d->x509 = q_X509_dup(x509);
719 static bool matchLineFeed(const QByteArray &pem, int *offset)
723 // ignore extra whitespace at the end of the line
724 while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
731 if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
738 QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
740 QList<QSslCertificate> certificates;
741 QSslSocketPrivate::ensureInitialized();
744 while (count == -1 || certificates.size() < count) {
745 int startPos = pem.indexOf(BEGINCERTSTRING, offset);
748 startPos += sizeof(BEGINCERTSTRING) - 1;
749 if (!matchLineFeed(pem, &startPos))
752 int endPos = pem.indexOf(ENDCERTSTRING, startPos);
756 offset = endPos + sizeof(ENDCERTSTRING) - 1;
757 if (offset < pem.size() && !matchLineFeed(pem, &offset))
760 QByteArray decoded = QByteArray::fromBase64(
761 QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
762 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
763 const unsigned char *data = (const unsigned char *)decoded.data();
765 unsigned char *data = (unsigned char *)decoded.data();
768 if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
769 certificates << QSslCertificate_from_X509(x509);
777 QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
779 QList<QSslCertificate> certificates;
780 QSslSocketPrivate::ensureInitialized();
783 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
784 const unsigned char *data = (const unsigned char *)der.data();
786 unsigned char *data = (unsigned char *)der.data();
788 int size = der.size();
790 while (count == -1 || certificates.size() < count) {
791 if (X509 *x509 = q_d2i_X509(0, &data, size)) {
792 certificates << QSslCertificate_from_X509(x509);
797 size -= ((char *)data - der.data());
803 // These certificates are known to be fraudulent and were created during the comodo
804 // compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
805 static const char *certificate_blacklist[] = {
806 "04:7e:cb:e9:fc:a5:5f:7b:d0:9e:ae:36:e1:0c:ae:1e",
807 "f5:c8:6a:f3:61:62:f1:3a:64:f5:4f:6d:c9:58:7c:06",
808 "d7:55:8f:da:f5:f1:10:5b:b2:13:28:2b:70:77:29:a3",
809 "39:2a:43:4f:0e:07:df:1f:8a:a3:05:de:34:e0:c2:29",
810 "3e:75:ce:d4:6b:69:30:21:21:88:30:ae:86:a8:2a:71",
811 "e9:02:8b:95:78:e4:15:dc:1a:71:0a:2b:88:15:44:47",
812 "92:39:d5:34:8f:40:d1:69:5a:74:54:70:e1:f2:3f:43",
813 "b0:b7:13:3e:d0:96:f9:b5:6f:ae:91:c8:74:bd:3a:c0",
814 "d8:f3:5f:4e:b7:87:2b:2d:ab:06:92:e3:15:38:2f:b0",
818 bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
820 for (int a = 0; certificate_blacklist[a] != 0; a++) {
821 if (certificate.serialNumber() == certificate_blacklist[a])
827 #ifndef QT_NO_DEBUG_STREAM
828 QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
830 debug << "QSslCertificate("
831 << certificate.version()
832 << ',' << certificate.serialNumber()
833 << ',' << certificate.digest().toBase64()
834 << ',' << certificate.issuerInfo(QSslCertificate::Organization)
835 << ',' << certificate.subjectInfo(QSslCertificate::Organization)
836 << ',' << certificate.alternateSubjectNames()
837 #ifndef QT_NO_TEXTSTREAM
838 << ',' << certificate.effectiveDate()
839 << ',' << certificate.expiryDate()
844 QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info)
847 case QSslCertificate::Organization: debug << "Organization"; break;
848 case QSslCertificate::CommonName: debug << "CommonName"; break;
849 case QSslCertificate::CountryName: debug << "CountryName"; break;
850 case QSslCertificate::LocalityName: debug << "LocalityName"; break;
851 case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break;
852 case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break;