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

Last change on this file since 807 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

File size: 25.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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 // lazy init
308 if (d->issuerInfo.isEmpty() && d->x509)
309 d->issuerInfo =
310 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
311
312 return d->issuerInfo.value(_q_SubjectInfoToString(info));
313}
314
315/*!
316 Returns the issuer information for \a tag from the certificate,
317 or an empty string if there is no information for \a tag in the
318 certificate.
319
320 \sa subjectInfo()
321*/
322QString QSslCertificate::issuerInfo(const QByteArray &tag) 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(QString::fromLatin1(tag));
330}
331
332/*!
333
334 \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
335
336 Returns the information for the \a subject, or an empty string if
337 there is no information for \a subject in the certificate.
338
339 \sa issuerInfo()
340*/
341QString QSslCertificate::subjectInfo(SubjectInfo info) const
342{
343 // lazy init
344 if (d->subjectInfo.isEmpty() && d->x509)
345 d->subjectInfo =
346 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
347
348 return d->subjectInfo.value(_q_SubjectInfoToString(info));
349}
350
351/*!
352 Returns the subject information for \a tag, or an empty string if
353 there is no information for \a tag in the certificate.
354
355 \sa issuerInfo()
356*/
357QString QSslCertificate::subjectInfo(const QByteArray &tag) const
358{
359 // lazy init
360 if (d->subjectInfo.isEmpty() && d->x509)
361 d->subjectInfo =
362 _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
363
364 return d->subjectInfo.value(QString::fromLatin1(tag));
365}
366
367/*!
368 Returns the list of alternative subject names for this
369 certificate. The alternate subject names typically contain host
370 names, optionally with wildcards, that are valid for this
371 certificate.
372
373 These names are tested against the connected peer's host name, if
374 either the subject information for \l CommonName doesn't define a
375 valid host name, or the subject info name doesn't match the peer's
376 host name.
377
378 \sa subjectInfo()
379*/
380QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
381{
382 QMultiMap<QSsl::AlternateNameEntryType, QString> result;
383
384 if (!d->x509)
385 return result;
386
387 STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0);
388
389 if (altNames) {
390 for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
391 const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
392 if (genName->type != GEN_DNS && genName->type != GEN_EMAIL)
393 continue;
394
395 int len = q_ASN1_STRING_length(genName->d.ia5);
396 if (len < 0 || len >= 8192) {
397 // broken name
398 continue;
399 }
400
401 const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_data(genName->d.ia5));
402 const QString altName = QString::fromLatin1(altNameStr, len);
403 if (genName->type == GEN_DNS)
404 result.insert(QSsl::DnsEntry, altName);
405 else if (genName->type == GEN_EMAIL)
406 result.insert(QSsl::EmailEntry, altName);
407 }
408 q_sk_pop_free((STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_sk_free));
409 }
410
411 return result;
412}
413
414/*!
415 Returns the date-time that the certificate becomes valid, or an
416 empty QDateTime if this is a null certificate.
417
418 \sa expiryDate()
419*/
420QDateTime QSslCertificate::effectiveDate() const
421{
422 return d->notValidBefore;
423}
424
425/*!
426 Returns the date-time that the certificate expires, or an empty
427 QDateTime if this is a null certificate.
428
429 \sa effectiveDate()
430*/
431QDateTime QSslCertificate::expiryDate() const
432{
433 return d->notValidAfter;
434}
435
436/*!
437 Returns a pointer to the native certificate handle, if there is
438 one, or a null pointer otherwise.
439
440 You can use this handle, together with the native API, to access
441 extended information about the certificate.
442
443 \warning Use of this function has a high probability of being
444 non-portable, and its return value may vary from platform to
445 platform or change from minor release to minor release.
446*/
447Qt::HANDLE QSslCertificate::handle() const
448{
449 return Qt::HANDLE(d->x509);
450}
451
452/*!
453 Returns the certificate subject's public key.
454*/
455QSslKey QSslCertificate::publicKey() const
456{
457 if (!d->x509)
458 return QSslKey();
459
460 QSslKey key;
461
462 key.d->type = QSsl::PublicKey;
463 X509_PUBKEY *xkey = d->x509->cert_info->key;
464 EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey);
465 Q_ASSERT(pkey);
466
467 if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) {
468 key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
469 key.d->algorithm = QSsl::Rsa;
470 key.d->isNull = false;
471 } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
472 key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
473 key.d->algorithm = QSsl::Dsa;
474 key.d->isNull = false;
475 } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) {
476 // DH unsupported
477 } else {
478 // error?
479 }
480
481 q_EVP_PKEY_free(pkey);
482 return key;
483}
484
485/*!
486 Returns this certificate converted to a PEM (Base64) encoded
487 representation.
488*/
489QByteArray QSslCertificate::toPem() const
490{
491 if (!d->x509)
492 return QByteArray();
493 return d->QByteArray_from_X509(d->x509, QSsl::Pem);
494}
495
496/*!
497 Returns this certificate converted to a DER (binary) encoded
498 representation.
499*/
500QByteArray QSslCertificate::toDer() const
501{
502 if (!d->x509)
503 return QByteArray();
504 return d->QByteArray_from_X509(d->x509, QSsl::Der);
505}
506
507/*!
508 Searches all files in the \a path for certificates encoded in the
509 specified \a format and returns them in a list. \e must be a file or a
510 pattern matching one or more files, as specified by \a syntax.
511
512 Example:
513
514 \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0
515
516 \sa fromData()
517*/
518QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
519 QSsl::EncodingFormat format,
520 QRegExp::PatternSyntax syntax)
521{
522 // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
523 int pos = -1;
524 if (syntax == QRegExp::Wildcard)
525 pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\*\\?\\[\\]]")));
526 else if (syntax != QRegExp::FixedString)
527 pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
528 QString pathPrefix = path.left(pos); // == path if pos < 0
529 if (pos != -1)
530 pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/')));
531
532 // Special case - if the prefix ends up being nothing, use "." instead and
533 // chop off the first two characters from the glob'ed paths.
534 int startIndex = 0;
535 if (pathPrefix.trimmed().isEmpty()) {
536 startIndex = 2;
537 pathPrefix = QLatin1String(".");
538 }
539
540 // The path is a file.
541 if (pos == -1 && QFileInfo(pathPrefix).isFile()) {
542 QFile file(pathPrefix);
543 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
544 return QSslCertificate::fromData(file.readAll(),format);
545 return QList<QSslCertificate>();
546 }
547
548 // The path can be a file or directory.
549 QList<QSslCertificate> certs;
550 QRegExp pattern(path, Qt::CaseSensitive, syntax);
551 QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
552 while (it.hasNext()) {
553 QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
554 if (!pattern.exactMatch(filePath))
555 continue;
556
557 QFile file(filePath);
558 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
559 certs += QSslCertificate::fromData(file.readAll(),format);
560 }
561 return certs;
562}
563
564/*!
565 Searches for and parses all certificates in \a device that are
566 encoded in the specified \a format and returns them in a list of
567 certificates.
568
569 \sa fromData()
570*/
571QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format)
572{
573 if (!device) {
574 qWarning("QSslCertificate::fromDevice: cannot read from a null device");
575 return QList<QSslCertificate>();
576 }
577 return fromData(device->readAll(), format);
578}
579
580/*!
581 Searches for and parses all certificates in \a data that are
582 encoded in the specified \a format and returns them in a list of
583 certificates.
584
585 \sa fromDevice()
586*/
587QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
588{
589 return (format == QSsl::Pem)
590 ? QSslCertificatePrivate::certificatesFromPem(data)
591 : QSslCertificatePrivate::certificatesFromDer(data);
592}
593
594void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
595{
596 if (!data.isEmpty()) {
597 QList<QSslCertificate> certs = (format == QSsl::Pem)
598 ? certificatesFromPem(data, 1)
599 : certificatesFromDer(data, 1);
600 if (!certs.isEmpty()) {
601 *this = *certs.first().d;
602 if (x509)
603 x509 = q_X509_dup(x509);
604 }
605 }
606}
607
608#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
609#define ENDCERTSTRING "-----END CERTIFICATE-----"
610
611// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
612QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
613{
614 if (!x509) {
615 qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
616 return QByteArray();
617 }
618
619 // Use i2d_X509 to convert the X509 to an array.
620 int length = q_i2d_X509(x509, 0);
621 QByteArray array;
622 array.resize(length);
623 char *data = array.data();
624 char **dataP = &data;
625 unsigned char **dataPu = (unsigned char **)dataP;
626 if (q_i2d_X509(x509, dataPu) < 0)
627 return QByteArray();
628
629 if (format == QSsl::Der)
630 return array;
631
632 // Convert to Base64 - wrap at 64 characters.
633 array = array.toBase64();
634 QByteArray tmp;
635 for (int i = 0; i <= array.size() - 64; i += 64) {
636 tmp += QByteArray::fromRawData(array.data() + i, 64);
637 tmp += '\n';
638 }
639 if (int remainder = array.size() % 64) {
640 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
641 tmp += '\n';
642 }
643
644 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
645}
646
647static QMap<QString, QString> _q_mapFromOnelineName(char *name)
648{
649 QMap<QString, QString> info;
650 QString infoStr = QString::fromLocal8Bit(name);
651 q_CRYPTO_free(name);
652
653 // ### The right-hand encoding seems to allow hex (Regulierungsbeh\xC8orde)
654 //entry.replace(QLatin1String("\\x"), QLatin1String("%"));
655 //entry = QUrl::fromPercentEncoding(entry.toLatin1());
656 // ### See RFC-4630 for more details!
657
658 QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)"));
659
660 int pos = 0;
661 while ((pos = rx.indexIn(infoStr, pos)) != -1) {
662 const QString name = rx.cap(1);
663
664 QString value = rx.cap(2);
665 const int valuePos = rx.pos(2);
666
667 const int next = rx.indexIn(value);
668 if (next == -1) {
669 info.insert(name, value);
670 break;
671 }
672
673 value = value.left(next);
674 info.insert(name, value);
675 pos = valuePos + value.length();
676 }
677
678 return info;
679}
680
681QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
682{
683 QSslCertificate certificate;
684 if (!x509 || !QSslSocket::supportsSsl())
685 return certificate;
686
687 ASN1_TIME *nbef = q_X509_get_notBefore(x509);
688 ASN1_TIME *naft = q_X509_get_notAfter(x509);
689 certificate.d->notValidBefore = q_getTimeFromASN1(nbef);
690 certificate.d->notValidAfter = q_getTimeFromASN1(naft);
691 certificate.d->null = false;
692 certificate.d->x509 = q_X509_dup(x509);
693
694 return certificate;
695}
696
697static bool matchLineFeed(const QByteArray &pem, int *offset)
698{
699 char ch;
700
701 // ignore extra whitespace at the end of the line
702 while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
703 ++*offset;
704
705 if (ch == '\n') {
706 *offset += 1;
707 return true;
708 }
709 if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
710 *offset += 2;
711 return true;
712 }
713 return false;
714}
715
716QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
717{
718 QList<QSslCertificate> certificates;
719 QSslSocketPrivate::ensureInitialized();
720
721 int offset = 0;
722 while (count == -1 || certificates.size() < count) {
723 int startPos = pem.indexOf(BEGINCERTSTRING, offset);
724 if (startPos == -1)
725 break;
726 startPos += sizeof(BEGINCERTSTRING) - 1;
727 if (!matchLineFeed(pem, &startPos))
728 break;
729
730 int endPos = pem.indexOf(ENDCERTSTRING, startPos);
731 if (endPos == -1)
732 break;
733
734 offset = endPos + sizeof(ENDCERTSTRING) - 1;
735 if (offset < pem.size() && !matchLineFeed(pem, &offset))
736 break;
737
738 QByteArray decoded = QByteArray::fromBase64(
739 QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
740#if OPENSSL_VERSION_NUMBER >= 0x00908000L
741 const unsigned char *data = (const unsigned char *)decoded.data();
742#else
743 unsigned char *data = (unsigned char *)decoded.data();
744#endif
745
746 if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
747 certificates << QSslCertificate_from_X509(x509);
748 q_X509_free(x509);
749 }
750 }
751
752 return certificates;
753}
754
755QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
756{
757 QList<QSslCertificate> certificates;
758 QSslSocketPrivate::ensureInitialized();
759
760
761#if OPENSSL_VERSION_NUMBER >= 0x00908000L
762 const unsigned char *data = (const unsigned char *)der.data();
763#else
764 unsigned char *data = (unsigned char *)der.data();
765#endif
766 int size = der.size();
767
768 while (count == -1 || certificates.size() < count) {
769 if (X509 *x509 = q_d2i_X509(0, &data, size)) {
770 certificates << QSslCertificate_from_X509(x509);
771 q_X509_free(x509);
772 } else {
773 break;
774 }
775 size -= ((char *)data - der.data());
776 }
777
778 return certificates;
779}
780
781#ifndef QT_NO_DEBUG_STREAM
782QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
783{
784 debug << "QSslCertificate("
785 << certificate.version()
786 << ',' << certificate.serialNumber()
787 << ',' << certificate.digest().toBase64()
788 << ',' << certificate.issuerInfo(QSslCertificate::Organization)
789 << ',' << certificate.subjectInfo(QSslCertificate::Organization)
790 << ',' << certificate.alternateSubjectNames()
791#ifndef QT_NO_TEXTSTREAM
792 << ',' << certificate.effectiveDate()
793 << ',' << certificate.expiryDate()
794#endif
795 << ')';
796 return debug;
797}
798QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info)
799{
800 switch (info) {
801 case QSslCertificate::Organization: debug << "Organization"; break;
802 case QSslCertificate::CommonName: debug << "CommonName"; break;
803 case QSslCertificate::CountryName: debug << "CountryName"; break;
804 case QSslCertificate::LocalityName: debug << "LocalityName"; break;
805 case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break;
806 case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break;
807 }
808 return debug;
809}
810#endif
811
812QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.