source: trunk/src/network/ssl/qsslcertificate.cpp@ 646

Last change on this file since 646 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 24.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42
43/*!
44 \class QSslCertificate
45 \brief The QSslCertificate class provides a convenient API for an X509 certificate.
46 \since 4.3
47
48 \reentrant
49 \ingroup network
50 \ingroup ssl
51 \inmodule QtNetwork
52
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
56 Authority.
57
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.
64
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().
70
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.
81
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.
85
86 \sa QSslSocket, QSslKey, QSslCipher, QSslError
87*/
88
89/*!
90 \enum QSslCertificate::SubjectInfo
91
92 Describes keys that you can pass to QSslCertificate::issuerInfo() or
93 QSslCertificate::subjectInfo() to get information about the certificate
94 issuer or subject.
95
96 \value Organization "O" The name of the organization.
97
98 \value CommonName "CN" The common name; most often this is used to store
99 the host name.
100
101 \value LocalityName "L" The locality.
102
103 \value OrganizationalUnitName "OU" The organizational unit name.
104
105 \value CountryName "C" The country.
106
107 \value StateOrProvinceName "ST" The state or province.
108*/
109
110#include "qsslsocket_openssl_symbols_p.h"
111#include "qsslcertificate.h"
112#include "qsslcertificate_p.h"
113#include "qsslkey.h"
114#include "qsslkey_p.h"
115
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>
126
127QT_BEGIN_NAMESPACE
128
129// forward declaration
130static QMap<QString, QString> _q_mapFromOnelineName(char *name);
131
132/*!
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.
137*/
138QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
139 : d(new QSslCertificatePrivate)
140{
141 QSslSocketPrivate::ensureInitialized();
142 if (device)
143 d->init(device->readAll(), format);
144}
145
146/*!
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.
151*/
152QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
153 : d(new QSslCertificatePrivate)
154{
155 QSslSocketPrivate::ensureInitialized();
156 d->init(data, format);
157}
158
159/*!
160 Constructs an identical copy of \a other.
161*/
162QSslCertificate::QSslCertificate(const QSslCertificate &other) : d(other.d)
163{
164}
165
166/*!
167 Destroys the QSslCertificate.
168*/
169QSslCertificate::~QSslCertificate()
170{
171}
172
173/*!
174 Copies the contents of \a other into this certificate, making the two
175 certificates identical.
176*/
177QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
178{
179 d = other.d;
180 return *this;
181}
182
183/*!
184 Returns true if this certificate is the same as \a other; otherwise
185 returns false.
186*/
187bool QSslCertificate::operator==(const QSslCertificate &other) const
188{
189 if (d == other.d)
190 return true;
191 if (d->null && other.d->null)
192 return true;
193 if (d->x509 && other.d->x509)
194 return q_X509_cmp(d->x509, other.d->x509) == 0;
195 return false;
196}
197
198/*!
199 \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
200
201 Returns true if this certificate is not the same as \a other; otherwise
202 returns false.
203*/
204
205/*!
206 Returns true if this is a null certificate (i.e., a certificate
207 with no contents); otherwise returns false.
208
209 By default, QSslCertificate constructs a null certificate.
210
211 \sa isValid(), clear()
212*/
213bool QSslCertificate::isNull() const
214{
215 return d->null;
216}
217
218/*!
219 Returns true if this certificate is valid; otherwise returns
220 false.
221
222 Note: Currently, this function only checks that the current
223 data-time is within the date-time range during which the
224 certificate is considered valid. No other checks are
225 currently performed.
226
227 \sa isNull()
228*/
229bool QSslCertificate::isValid() const
230{
231 const QDateTime currentTime = QDateTime::currentDateTime();
232 return currentTime >= d->notValidBefore && currentTime <= d->notValidAfter;
233}
234
235/*!
236 Clears the contents of this certificate, making it a null
237 certificate.
238
239 \sa isNull()
240*/
241void QSslCertificate::clear()
242{
243 if (isNull())
244 return;
245 d = new QSslCertificatePrivate;
246}
247
248/*!
249 Returns the certificate's version string.
250*/
251QByteArray QSslCertificate::version() const
252{
253 if (d->versionString.isEmpty() && d->x509)
254 d->versionString =
255 QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1);
256
257 return d->versionString;
258}
259
260/*!
261 Returns the certificate's serial number string in decimal format.
262*/
263QByteArray QSslCertificate::serialNumber() const
264{
265 if (d->serialNumberString.isEmpty() && d->x509)
266 d->serialNumberString =
267 QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->serialNumber)));
268
269 return d->serialNumberString;
270}
271
272/*!
273 Returns a cryptographic digest of this certificate. By default,
274 an MD5 digest will be generated, but you can also specify a
275 custom \a algorithm.
276*/
277QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const
278{
279 return QCryptographicHash::hash(toDer(), algorithm);
280}
281
282static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info)
283{
284 QString str;
285 switch (info) {
286 case QSslCertificate::Organization: str = QLatin1String("O"); break;
287 case QSslCertificate::CommonName: str = QLatin1String("CN"); break;
288 case QSslCertificate::LocalityName: str = QLatin1String("L"); break;
289 case QSslCertificate::OrganizationalUnitName: str = QLatin1String("OU"); break;
290 case QSslCertificate::CountryName: str = QLatin1String("C"); break;
291 case QSslCertificate::StateOrProvinceName: str = QLatin1String("ST"); break;
292 }
293 return str;
294}
295
296/*!
297 \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const
298
299 Returns the issuer information for the \a subject from the
300 certificate, or an empty string if there is no information for
301 \a subject in the certificate.
302
303 \sa subjectInfo()
304*/
305QString QSslCertificate::issuerInfo(SubjectInfo info) const
306{
307 if (d->issuerInfo.isEmpty() && d->x509)
308 d->issuerInfo =
309 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
310
311 return d->issuerInfo.value(_q_SubjectInfoToString(info));
312}
313
314/*!
315 Returns the issuer information for \a tag from the certificate,
316 or an empty string if there is no information for \a tag in the
317 certificate.
318
319 \sa subjectInfo()
320*/
321QString QSslCertificate::issuerInfo(const QByteArray &tag) const
322{
323 // ### Use a QByteArray for the keys in the map
324 return d->issuerInfo.value(QString::fromLatin1(tag));
325}
326
327/*!
328
329 \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
330
331 Returns the information for the \a subject, or an empty string if
332 there is no information for \a subject in the certificate.
333
334 \sa issuerInfo()
335*/
336QString QSslCertificate::subjectInfo(SubjectInfo info) const
337{
338 if (d->subjectInfo.isEmpty() && d->x509)
339 d->subjectInfo =
340 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
341
342 return d->subjectInfo.value(_q_SubjectInfoToString(info));
343}
344
345/*!
346 Returns the subject information for \a tag, or an empty string if
347 there is no information for \a tag in the certificate.
348
349 \sa issuerInfo()
350*/
351QString QSslCertificate::subjectInfo(const QByteArray &tag) const
352{
353 // ### Use a QByteArray for the keys in the map
354 return d->subjectInfo.value(QString::fromLatin1(tag));
355}
356
357/*!
358 Returns the list of alternative subject names for this
359 certificate. The alternate subject names typically contain host
360 names, optionally with wildcards, that are valid for this
361 certificate.
362
363 These names are tested against the connected peer's host name, if
364 either the subject information for \l CommonName doesn't define a
365 valid host name, or the subject info name doesn't match the peer's
366 host name.
367
368 \sa subjectInfo()
369*/
370QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
371{
372 QMultiMap<QSsl::AlternateNameEntryType, QString> result;
373
374 if (!d->x509)
375 return result;
376
377 STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0);
378
379 if (altNames) {
380 for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
381 const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
382 if (genName->type != GEN_DNS && genName->type != GEN_EMAIL)
383 continue;
384
385 int len = q_ASN1_STRING_length(genName->d.ia5);
386 if (len < 0 || len >= 8192) {
387 // broken name
388 continue;
389 }
390
391 const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_data(genName->d.ia5));
392 const QString altName = QString::fromLatin1(altNameStr, len);
393 if (genName->type == GEN_DNS)
394 result.insert(QSsl::DnsEntry, altName);
395 else if (genName->type == GEN_EMAIL)
396 result.insert(QSsl::EmailEntry, altName);
397 }
398 q_sk_pop_free((STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_sk_free));
399 }
400
401 return result;
402}
403
404/*!
405 Returns the date-time that the certificate becomes valid, or an
406 empty QDateTime if this is a null certificate.
407
408 \sa expiryDate()
409*/
410QDateTime QSslCertificate::effectiveDate() const
411{
412 return d->notValidBefore;
413}
414
415/*!
416 Returns the date-time that the certificate expires, or an empty
417 QDateTime if this is a null certificate.
418
419 \sa effectiveDate()
420*/
421QDateTime QSslCertificate::expiryDate() const
422{
423 return d->notValidAfter;
424}
425
426/*!
427 Returns a pointer to the native certificate handle, if there is
428 one, or a null pointer otherwise.
429
430 You can use this handle, together with the native API, to access
431 extended information about the certificate.
432
433 \warning Use of this function has a high probability of being
434 non-portable, and its return value may vary from platform to
435 platform or change from minor release to minor release.
436*/
437Qt::HANDLE QSslCertificate::handle() const
438{
439 return Qt::HANDLE(d->x509);
440}
441
442/*!
443 Returns the certificate subject's public key.
444*/
445QSslKey QSslCertificate::publicKey() const
446{
447 if (!d->x509)
448 return QSslKey();
449
450 QSslKey key;
451
452 key.d->type = QSsl::PublicKey;
453 X509_PUBKEY *xkey = d->x509->cert_info->key;
454 EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey);
455 Q_ASSERT(pkey);
456
457 if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) {
458 key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
459 key.d->algorithm = QSsl::Rsa;
460 key.d->isNull = false;
461 } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
462 key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
463 key.d->algorithm = QSsl::Dsa;
464 key.d->isNull = false;
465 } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) {
466 // DH unsupported
467 } else {
468 // error?
469 }
470
471 q_EVP_PKEY_free(pkey);
472 return key;
473}
474
475/*!
476 Returns this certificate converted to a PEM (Base64) encoded
477 representation.
478*/
479QByteArray QSslCertificate::toPem() const
480{
481 if (!d->x509)
482 return QByteArray();
483 return d->QByteArray_from_X509(d->x509, QSsl::Pem);
484}
485
486/*!
487 Returns this certificate converted to a DER (binary) encoded
488 representation.
489*/
490QByteArray QSslCertificate::toDer() const
491{
492 if (!d->x509)
493 return QByteArray();
494 return d->QByteArray_from_X509(d->x509, QSsl::Der);
495}
496
497/*!
498 Searches all files in the \a path for certificates encoded in the
499 specified \a format and returns them in a list. \e must be a file or a
500 pattern matching one or more files, as specified by \a syntax.
501
502 Example:
503
504 \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0
505
506 \sa fromData()
507*/
508QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
509 QSsl::EncodingFormat format,
510 QRegExp::PatternSyntax syntax)
511{
512 // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
513 int pos = -1;
514 if (syntax == QRegExp::Wildcard)
515 pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\*\\?\\[\\]]")));
516 else if (syntax != QRegExp::FixedString)
517 pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
518 QString pathPrefix = path.left(pos); // == path if pos < 0
519 if (pos != -1)
520 pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/')));
521
522 // Special case - if the prefix ends up being nothing, use "." instead and
523 // chop off the first two characters from the glob'ed paths.
524 int startIndex = 0;
525 if (pathPrefix.trimmed().isEmpty()) {
526 startIndex = 2;
527 pathPrefix = QLatin1String(".");
528 }
529
530 // The path is a file.
531 if (pos == -1 && QFileInfo(pathPrefix).isFile()) {
532 QFile file(pathPrefix);
533 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
534 return QSslCertificate::fromData(file.readAll(),format);
535 return QList<QSslCertificate>();
536 }
537
538 // The path can be a file or directory.
539 QList<QSslCertificate> certs;
540 QRegExp pattern(path, Qt::CaseSensitive, syntax);
541 QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
542 while (it.hasNext()) {
543 QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
544 if (!pattern.exactMatch(filePath))
545 continue;
546
547 QFile file(filePath);
548 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
549 certs += QSslCertificate::fromData(file.readAll(),format);
550 }
551 return certs;
552}
553
554/*!
555 Searches for and parses all certificates in \a device that are
556 encoded in the specified \a format and returns them in a list of
557 certificates.
558
559 \sa fromData()
560*/
561QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format)
562{
563 if (!device) {
564 qWarning("QSslCertificate::fromDevice: cannot read from a null device");
565 return QList<QSslCertificate>();
566 }
567 return fromData(device->readAll(), format);
568}
569
570/*!
571 Searches for and parses all certificates in \a data that are
572 encoded in the specified \a format and returns them in a list of
573 certificates.
574
575 \sa fromDevice()
576*/
577QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
578{
579 return (format == QSsl::Pem)
580 ? QSslCertificatePrivate::certificatesFromPem(data)
581 : QSslCertificatePrivate::certificatesFromDer(data);
582}
583
584void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
585{
586 if (!data.isEmpty()) {
587 QList<QSslCertificate> certs = (format == QSsl::Pem)
588 ? certificatesFromPem(data, 1)
589 : certificatesFromDer(data, 1);
590 if (!certs.isEmpty()) {
591 *this = *certs.first().d;
592 if (x509)
593 x509 = q_X509_dup(x509);
594 }
595 }
596}
597
598#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
599#define ENDCERTSTRING "-----END CERTIFICATE-----"
600
601// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
602QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
603{
604 if (!x509) {
605 qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
606 return QByteArray();
607 }
608
609 // Use i2d_X509 to convert the X509 to an array.
610 int length = q_i2d_X509(x509, 0);
611 QByteArray array;
612 array.resize(length);
613 char *data = array.data();
614 char **dataP = &data;
615 unsigned char **dataPu = (unsigned char **)dataP;
616 if (q_i2d_X509(x509, dataPu) < 0)
617 return QByteArray();
618
619 if (format == QSsl::Der)
620 return array;
621
622 // Convert to Base64 - wrap at 64 characters.
623 array = array.toBase64();
624 QByteArray tmp;
625 for (int i = 0; i <= array.size() - 64; i += 64) {
626 tmp += QByteArray::fromRawData(array.data() + i, 64);
627 tmp += "\n";
628 }
629 if (int remainder = array.size() % 64) {
630 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
631 tmp += "\n";
632 }
633
634 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
635}
636
637static QMap<QString, QString> _q_mapFromOnelineName(char *name)
638{
639 QMap<QString, QString> info;
640 QString infoStr = QString::fromLocal8Bit(name);
641 q_CRYPTO_free(name);
642
643 // ### The right-hand encoding seems to allow hex (Regulierungsbeh\xC8orde)
644 //entry.replace(QLatin1String("\\x"), QLatin1String("%"));
645 //entry = QUrl::fromPercentEncoding(entry.toLatin1());
646 // ### See RFC-4630 for more details!
647
648 QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)"));
649
650 int pos = 0;
651 while ((pos = rx.indexIn(infoStr, pos)) != -1) {
652 const QString name = rx.cap(1);
653
654 QString value = rx.cap(2);
655 const int valuePos = rx.pos(2);
656
657 const int next = rx.indexIn(value);
658 if (next == -1) {
659 info.insert(name, value);
660 break;
661 }
662
663 value = value.left(next);
664 info.insert(name, value);
665 pos = valuePos + value.length();
666 }
667
668 return info;
669}
670
671QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
672{
673 QSslCertificate certificate;
674 if (!x509 || !QSslSocket::supportsSsl())
675 return certificate;
676
677 ASN1_TIME *nbef = q_X509_get_notBefore(x509);
678 ASN1_TIME *naft = q_X509_get_notAfter(x509);
679 certificate.d->notValidBefore = q_getTimeFromASN1(nbef);
680 certificate.d->notValidAfter = q_getTimeFromASN1(naft);
681 certificate.d->null = false;
682 certificate.d->x509 = q_X509_dup(x509);
683
684 return certificate;
685}
686
687static bool matchLineFeed(const QByteArray &pem, int *offset)
688{
689 char ch = pem.at(*offset);
690
691 // ignore extra whitespace at the end of the line
692 while (ch == ' ' && *offset < pem.size())
693 ch = pem.at(++*offset);
694
695 if (ch == '\n') {
696 *offset += 1;
697 return true;
698 }
699 if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
700 *offset += 2;
701 return true;
702 }
703 return false;
704}
705
706QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
707{
708 QList<QSslCertificate> certificates;
709 QSslSocketPrivate::ensureInitialized();
710
711 int offset = 0;
712 while (count == -1 || certificates.size() < count) {
713 int startPos = pem.indexOf(BEGINCERTSTRING, offset);
714 if (startPos == -1)
715 break;
716 startPos += sizeof(BEGINCERTSTRING) - 1;
717 if (!matchLineFeed(pem, &startPos))
718 break;
719
720 int endPos = pem.indexOf(ENDCERTSTRING, startPos);
721 if (endPos == -1)
722 break;
723
724 offset = endPos + sizeof(ENDCERTSTRING) - 1;
725 if (!matchLineFeed(pem, &offset))
726 break;
727
728 QByteArray decoded = QByteArray::fromBase64(
729 QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
730#if OPENSSL_VERSION_NUMBER >= 0x00908000L
731 const unsigned char *data = (const unsigned char *)decoded.data();
732#else
733 unsigned char *data = (unsigned char *)decoded.data();
734#endif
735
736 if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
737 certificates << QSslCertificate_from_X509(x509);
738 q_X509_free(x509);
739 }
740 }
741
742 return certificates;
743}
744
745QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
746{
747 QList<QSslCertificate> certificates;
748 QSslSocketPrivate::ensureInitialized();
749
750
751#if OPENSSL_VERSION_NUMBER >= 0x00908000L
752 const unsigned char *data = (const unsigned char *)der.data();
753#else
754 unsigned char *data = (unsigned char *)der.data();
755#endif
756 int size = der.size();
757
758 while (count == -1 || certificates.size() < count) {
759 if (X509 *x509 = q_d2i_X509(0, &data, size)) {
760 certificates << QSslCertificate_from_X509(x509);
761 q_X509_free(x509);
762 } else {
763 break;
764 }
765 size -= ((char *)data - der.data());
766 }
767
768 return certificates;
769}
770
771#ifndef QT_NO_DEBUG_STREAM
772QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
773{
774 debug << "QSslCertificate("
775 << certificate.version()
776 << ',' << certificate.serialNumber()
777 << ',' << certificate.digest().toBase64()
778 << ',' << certificate.issuerInfo(QSslCertificate::Organization)
779 << ',' << certificate.subjectInfo(QSslCertificate::Organization)
780 << ',' << certificate.alternateSubjectNames()
781#ifndef QT_NO_TEXTSTREAM
782 << ',' << certificate.effectiveDate()
783 << ',' << certificate.expiryDate()
784#endif
785 << ')';
786 return debug;
787}
788QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info)
789{
790 switch (info) {
791 case QSslCertificate::Organization: debug << "Organization"; break;
792 case QSslCertificate::CommonName: debug << "CommonName"; break;
793 case QSslCertificate::CountryName: debug << "CountryName"; break;
794 case QSslCertificate::LocalityName: debug << "LocalityName"; break;
795 case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break;
796 case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break;
797 }
798 return debug;
799}
800#endif
801
802QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.