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

Last change on this file was 865, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.3 sources from branches/vendor/nokia/qt.

File size: 27.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 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.
226
227 \sa isNull()
228*/
229bool QSslCertificate::isValid() const
230{
231 const QDateTime currentTime = QDateTime::currentDateTime();
232 return currentTime >= d->notValidBefore &&
233 currentTime <= d->notValidAfter &&
234 ! QSslCertificatePrivate::isBlacklisted(*this);
235}
236
237/*!
238 Clears the contents of this certificate, making it a null
239 certificate.
240
241 \sa isNull()
242*/
243void QSslCertificate::clear()
244{
245 if (isNull())
246 return;
247 d = new QSslCertificatePrivate;
248}
249
250/*!
251 Returns the certificate's version string.
252*/
253QByteArray QSslCertificate::version() const
254{
255 if (d->versionString.isEmpty() && d->x509)
256 d->versionString =
257 QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1);
258
259 return d->versionString;
260}
261
262/*!
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.
267*/
268QByteArray QSslCertificate::serialNumber() const
269{
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');
278 hexString += ':';
279 }
280 hexString.chop(1);
281 d->serialNumberString = hexString;
282 } else {
283 d->serialNumberString = QByteArray::number(qlonglong(q_ASN1_INTEGER_get(serialNumber)));
284 }
285 }
286 return d->serialNumberString;
287}
288
289/*!
290 Returns a cryptographic digest of this certificate. By default,
291 an MD5 digest will be generated, but you can also specify a
292 custom \a algorithm.
293*/
294QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const
295{
296 return QCryptographicHash::hash(toDer(), algorithm);
297}
298
299static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info)
300{
301 QString str;
302 switch (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;
309 }
310 return str;
311}
312
313/*!
314 \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const
315
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.
319
320 \sa subjectInfo()
321*/
322QString QSslCertificate::issuerInfo(SubjectInfo info) const
323{
324 // lazy init
325 if (d->issuerInfo.isEmpty() && d->x509)
326 d->issuerInfo =
327 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
328
329 return d->issuerInfo.value(_q_SubjectInfoToString(info));
330}
331
332/*!
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
335 certificate.
336
337 \sa subjectInfo()
338*/
339QString QSslCertificate::issuerInfo(const QByteArray &tag) const
340{
341 // lazy init
342 if (d->issuerInfo.isEmpty() && d->x509)
343 d->issuerInfo =
344 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
345
346 return d->issuerInfo.value(QString::fromLatin1(tag));
347}
348
349/*!
350
351 \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
352
353 Returns the information for the \a subject, or an empty string if
354 there is no information for \a subject in the certificate.
355
356 \sa issuerInfo()
357*/
358QString QSslCertificate::subjectInfo(SubjectInfo info) const
359{
360 // lazy init
361 if (d->subjectInfo.isEmpty() && d->x509)
362 d->subjectInfo =
363 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
364
365 return d->subjectInfo.value(_q_SubjectInfoToString(info));
366}
367
368/*!
369 Returns the subject information for \a tag, or an empty string if
370 there is no information for \a tag in the certificate.
371
372 \sa issuerInfo()
373*/
374QString QSslCertificate::subjectInfo(const QByteArray &tag) const
375{
376 // lazy init
377 if (d->subjectInfo.isEmpty() && d->x509)
378 d->subjectInfo =
379 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
380
381 return d->subjectInfo.value(QString::fromLatin1(tag));
382}
383
384/*!
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
388 certificate.
389
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
393 host name.
394
395 \sa subjectInfo()
396*/
397QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
398{
399 QMultiMap<QSsl::AlternateNameEntryType, QString> result;
400
401 if (!d->x509)
402 return result;
403
404 STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0);
405
406 if (altNames) {
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)
410 continue;
411
412 int len = q_ASN1_STRING_length(genName->d.ia5);
413 if (len < 0 || len >= 8192) {
414 // broken name
415 continue;
416 }
417
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);
424 }
425 q_sk_pop_free((STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_sk_free));
426 }
427
428 return result;
429}
430
431/*!
432 Returns the date-time that the certificate becomes valid, or an
433 empty QDateTime if this is a null certificate.
434
435 \sa expiryDate()
436*/
437QDateTime QSslCertificate::effectiveDate() const
438{
439 return d->notValidBefore;
440}
441
442/*!
443 Returns the date-time that the certificate expires, or an empty
444 QDateTime if this is a null certificate.
445
446 \sa effectiveDate()
447*/
448QDateTime QSslCertificate::expiryDate() const
449{
450 return d->notValidAfter;
451}
452
453/*!
454 Returns a pointer to the native certificate handle, if there is
455 one, or a null pointer otherwise.
456
457 You can use this handle, together with the native API, to access
458 extended information about the certificate.
459
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.
463*/
464Qt::HANDLE QSslCertificate::handle() const
465{
466 return Qt::HANDLE(d->x509);
467}
468
469/*!
470 Returns the certificate subject's public key.
471*/
472QSslKey QSslCertificate::publicKey() const
473{
474 if (!d->x509)
475 return QSslKey();
476
477 QSslKey key;
478
479 key.d->type = QSsl::PublicKey;
480 X509_PUBKEY *xkey = d->x509->cert_info->key;
481 EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey);
482 Q_ASSERT(pkey);
483
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) {
493 // DH unsupported
494 } else {
495 // error?
496 }
497
498 q_EVP_PKEY_free(pkey);
499 return key;
500}
501
502/*!
503 Returns this certificate converted to a PEM (Base64) encoded
504 representation.
505*/
506QByteArray QSslCertificate::toPem() const
507{
508 if (!d->x509)
509 return QByteArray();
510 return d->QByteArray_from_X509(d->x509, QSsl::Pem);
511}
512
513/*!
514 Returns this certificate converted to a DER (binary) encoded
515 representation.
516*/
517QByteArray QSslCertificate::toDer() const
518{
519 if (!d->x509)
520 return QByteArray();
521 return d->QByteArray_from_X509(d->x509, QSsl::Der);
522}
523
524/*!
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.
528
529 Example:
530
531 \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0
532
533 \sa fromData()
534*/
535QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
536 QSsl::EncodingFormat format,
537 QRegExp::PatternSyntax syntax)
538{
539 // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
540 int pos = -1;
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
546 if (pos != -1)
547 pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/')));
548
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.
551 int startIndex = 0;
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('/')));
556 } else {
557 startIndex = 2;
558 pathPrefix = QLatin1String(".");
559 }
560 }
561
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>();
568 }
569
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))
577 continue;
578
579 QFile file(filePath);
580 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
581 certs += QSslCertificate::fromData(file.readAll(),format);
582 }
583 return certs;
584}
585
586/*!
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
589 certificates.
590
591 \sa fromData()
592*/
593QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format)
594{
595 if (!device) {
596 qWarning("QSslCertificate::fromDevice: cannot read from a null device");
597 return QList<QSslCertificate>();
598 }
599 return fromData(device->readAll(), format);
600}
601
602/*!
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
605 certificates.
606
607 \sa fromDevice()
608*/
609QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
610{
611 return (format == QSsl::Pem)
612 ? QSslCertificatePrivate::certificatesFromPem(data)
613 : QSslCertificatePrivate::certificatesFromDer(data);
614}
615
616void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
617{
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;
624 if (x509)
625 x509 = q_X509_dup(x509);
626 }
627 }
628}
629
630#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
631#define ENDCERTSTRING "-----END CERTIFICATE-----"
632
633// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
634QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
635{
636 if (!x509) {
637 qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
638 return QByteArray();
639 }
640
641 // Use i2d_X509 to convert the X509 to an array.
642 int length = q_i2d_X509(x509, 0);
643 QByteArray array;
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)
649 return QByteArray();
650
651 if (format == QSsl::Der)
652 return array;
653
654 // Convert to Base64 - wrap at 64 characters.
655 array = array.toBase64();
656 QByteArray tmp;
657 for (int i = 0; i <= array.size() - 64; i += 64) {
658 tmp += QByteArray::fromRawData(array.data() + i, 64);
659 tmp += '\n';
660 }
661 if (int remainder = array.size() % 64) {
662 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
663 tmp += '\n';
664 }
665
666 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
667}
668
669static QMap<QString, QString> _q_mapFromOnelineName(char *name)
670{
671 QMap<QString, QString> info;
672 QString infoStr = QString::fromLocal8Bit(name);
673 q_CRYPTO_free(name);
674
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!
679
680 QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)"));
681
682 int pos = 0;
683 while ((pos = rx.indexIn(infoStr, pos)) != -1) {
684 const QString name = rx.cap(1);
685
686 QString value = rx.cap(2);
687 const int valuePos = rx.pos(2);
688
689 const int next = rx.indexIn(value);
690 if (next == -1) {
691 info.insert(name, value);
692 break;
693 }
694
695 value = value.left(next);
696 info.insert(name, value);
697 pos = valuePos + value.length();
698 }
699
700 return info;
701}
702
703QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
704{
705 QSslCertificate certificate;
706 if (!x509 || !QSslSocket::supportsSsl())
707 return certificate;
708
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);
715
716 return certificate;
717}
718
719static bool matchLineFeed(const QByteArray &pem, int *offset)
720{
721 char ch = 0;
722
723 // ignore extra whitespace at the end of the line
724 while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
725 ++*offset;
726
727 if (ch == '\n') {
728 *offset += 1;
729 return true;
730 }
731 if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
732 *offset += 2;
733 return true;
734 }
735 return false;
736}
737
738QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
739{
740 QList<QSslCertificate> certificates;
741 QSslSocketPrivate::ensureInitialized();
742
743 int offset = 0;
744 while (count == -1 || certificates.size() < count) {
745 int startPos = pem.indexOf(BEGINCERTSTRING, offset);
746 if (startPos == -1)
747 break;
748 startPos += sizeof(BEGINCERTSTRING) - 1;
749 if (!matchLineFeed(pem, &startPos))
750 break;
751
752 int endPos = pem.indexOf(ENDCERTSTRING, startPos);
753 if (endPos == -1)
754 break;
755
756 offset = endPos + sizeof(ENDCERTSTRING) - 1;
757 if (offset < pem.size() && !matchLineFeed(pem, &offset))
758 break;
759
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();
764#else
765 unsigned char *data = (unsigned char *)decoded.data();
766#endif
767
768 if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
769 certificates << QSslCertificate_from_X509(x509);
770 q_X509_free(x509);
771 }
772 }
773
774 return certificates;
775}
776
777QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
778{
779 QList<QSslCertificate> certificates;
780 QSslSocketPrivate::ensureInitialized();
781
782
783#if OPENSSL_VERSION_NUMBER >= 0x00908000L
784 const unsigned char *data = (const unsigned char *)der.data();
785#else
786 unsigned char *data = (unsigned char *)der.data();
787#endif
788 int size = der.size();
789
790 while (count == -1 || certificates.size() < count) {
791 if (X509 *x509 = q_d2i_X509(0, &data, size)) {
792 certificates << QSslCertificate_from_X509(x509);
793 q_X509_free(x509);
794 } else {
795 break;
796 }
797 size -= ((char *)data - der.data());
798 }
799
800 return certificates;
801}
802
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
805static 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",
815 0
816};
817
818bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
819{
820 for (int a = 0; certificate_blacklist[a] != 0; a++) {
821 if (certificate.serialNumber() == certificate_blacklist[a])
822 return true;
823 }
824 return false;
825}
826
827#ifndef QT_NO_DEBUG_STREAM
828QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
829{
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()
840#endif
841 << ')';
842 return debug;
843}
844QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info)
845{
846 switch (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;
853 }
854 return debug;
855}
856#endif
857
858QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.