1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qssl_p.h"
42#include "qsslsocket_openssl_symbols_p.h"
43#include "qsslcertificate_p.h"
44#include "qsslkey_p.h"
45#include "qsslcertificateextension_p.h"
46
47#include <QtCore/qscopeguard.h>
48#include <QtCore/qendian.h>
49#include <QtCore/qmutex.h>
50
51QT_BEGIN_NAMESPACE
52
53Q_CONSTEXPR int MutexPoolSize = 17;
54static QBasicMutex mutexPool[MutexPoolSize];
55namespace QMutexPool {
56 static QBasicMutex *globalInstanceGet(const void *addr)
57 {
58 return mutexPool + (quintptr(addr) % MutexPoolSize);
59 }
60}
61
62// forward declaration
63static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name);
64
65bool QSslCertificate::operator==(const QSslCertificate &other) const
66{
67 if (d == other.d)
68 return true;
69
70 if (d->null && other.d->null)
71 return true;
72
73 if (d->x509 && other.d->x509) {
74 const int ret = q_X509_cmp(a: d->x509, b: other.d->x509);
75 if (ret >= -1 && ret <= 1)
76 return ret == 0;
77 QSslSocketBackendPrivate::logAndClearErrorQueue();
78 }
79
80 return false;
81}
82
83uint qHash(const QSslCertificate &key, uint seed) noexcept
84{
85 if (X509 * const x509 = key.d->x509) {
86 const EVP_MD *sha1 = q_EVP_sha1();
87 unsigned int len = 0;
88 unsigned char md[EVP_MAX_MD_SIZE];
89 q_X509_digest(x509, type: sha1, md, len: &len);
90 return qHashBits(p: md, size: len, seed);
91 }
92
93 return seed;
94}
95
96bool QSslCertificate::isNull() const
97{
98 return d->null;
99}
100
101bool QSslCertificate::isSelfSigned() const
102{
103 if (!d->x509)
104 return false;
105
106 return (q_X509_check_issued(a: d->x509, b: d->x509) == X509_V_OK);
107}
108
109QByteArray QSslCertificate::version() const
110{
111#if QT_CONFIG(thread)
112 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
113#endif
114 if (d->versionString.isEmpty() && d->x509)
115 d->versionString = QByteArray::number(qlonglong(q_X509_get_version(a: d->x509)) + 1);
116
117 return d->versionString;
118}
119
120QByteArray QSslCertificate::serialNumber() const
121{
122#if QT_CONFIG(thread)
123 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
124#endif
125 if (d->serialNumberString.isEmpty() && d->x509) {
126 ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(a: d->x509);
127 QByteArray hexString;
128 hexString.reserve(asize: serialNumber->length * 3);
129 for (int a = 0; a < serialNumber->length; ++a) {
130 hexString += QByteArray::number(serialNumber->data[a], base: 16).rightJustified(width: 2, fill: '0');
131 hexString += ':';
132 }
133 hexString.chop(n: 1);
134 d->serialNumberString = hexString;
135 }
136 return d->serialNumberString;
137}
138
139QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
140{
141#if QT_CONFIG(thread)
142 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
143#endif
144 // lazy init
145 if (d->issuerInfo.isEmpty() && d->x509)
146 d->issuerInfo =
147 _q_mapFromX509Name(name: q_X509_get_issuer_name(a: d->x509));
148
149 return d->issuerInfo.values(akey: d->subjectInfoToString(info));
150}
151
152QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
153{
154#if QT_CONFIG(thread)
155 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
156#endif
157 // lazy init
158 if (d->issuerInfo.isEmpty() && d->x509)
159 d->issuerInfo =
160 _q_mapFromX509Name(name: q_X509_get_issuer_name(a: d->x509));
161
162 return d->issuerInfo.values(akey: attribute);
163}
164
165QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
166{
167#if QT_CONFIG(thread)
168 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
169#endif
170 // lazy init
171 if (d->subjectInfo.isEmpty() && d->x509)
172 d->subjectInfo =
173 _q_mapFromX509Name(name: q_X509_get_subject_name(a: d->x509));
174
175 return d->subjectInfo.values(akey: d->subjectInfoToString(info));
176}
177
178QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
179{
180#if QT_CONFIG(thread)
181 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
182#endif
183 // lazy init
184 if (d->subjectInfo.isEmpty() && d->x509)
185 d->subjectInfo =
186 _q_mapFromX509Name(name: q_X509_get_subject_name(a: d->x509));
187
188 return d->subjectInfo.values(akey: attribute);
189}
190
191QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
192{
193#if QT_CONFIG(thread)
194 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
195#endif
196 // lazy init
197 if (d->subjectInfo.isEmpty() && d->x509)
198 d->subjectInfo =
199 _q_mapFromX509Name(name: q_X509_get_subject_name(a: d->x509));
200
201 return d->subjectInfo.uniqueKeys();
202}
203
204QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
205{
206#if QT_CONFIG(thread)
207 QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data()));
208#endif
209 // lazy init
210 if (d->issuerInfo.isEmpty() && d->x509)
211 d->issuerInfo =
212 _q_mapFromX509Name(name: q_X509_get_issuer_name(a: d->x509));
213
214 return d->issuerInfo.uniqueKeys();
215}
216
217QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
218{
219 QMultiMap<QSsl::AlternativeNameEntryType, QString> result;
220
221 if (!d->x509)
222 return result;
223
224 STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i(
225 a: d->x509, NID_subject_alt_name, c: nullptr, d: nullptr);
226
227 auto altName = [](ASN1_IA5STRING *ia5, int len) {
228 const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(x: ia5));
229 return QString::fromLatin1(str: altNameStr, size: len);
230 };
231 if (altNames) {
232 for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
233 const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
234 if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD)
235 continue;
236
237 int len = q_ASN1_STRING_length(a: genName->d.ia5);
238 if (len < 0 || len >= 8192) {
239 // broken name
240 continue;
241 }
242
243 switch (genName->type) {
244 case GEN_DNS:
245 result.insert(akey: QSsl::DnsEntry, avalue: altName(genName->d.ia5, len));
246 break;
247 case GEN_EMAIL:
248 result.insert(akey: QSsl::EmailEntry, avalue: altName(genName->d.ia5, len));
249 break;
250 case GEN_IPADD: {
251 QHostAddress ipAddress;
252 switch (len) {
253 case 4: // IPv4
254 ipAddress = QHostAddress(qFromBigEndian(source: *reinterpret_cast<quint32 *>(genName->d.iPAddress->data)));
255 break;
256 case 16: // IPv6
257 ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data));
258 break;
259 default: // Unknown IP address format
260 break;
261 }
262 if (!ipAddress.isNull())
263 result.insert(akey: QSsl::IpAddressEntry, avalue: ipAddress.toString());
264 break;
265 }
266 default:
267 break;
268 }
269 }
270
271 q_OPENSSL_sk_pop_free(a: (OPENSSL_STACK*)altNames, b: reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free));
272 }
273
274 return result;
275}
276
277QDateTime QSslCertificate::effectiveDate() const
278{
279 return d->notValidBefore;
280}
281
282QDateTime QSslCertificate::expiryDate() const
283{
284 return d->notValidAfter;
285}
286
287Qt::HANDLE QSslCertificate::handle() const
288{
289 return Qt::HANDLE(d->x509);
290}
291
292QSslKey QSslCertificate::publicKey() const
293{
294 if (!d->x509)
295 return QSslKey();
296
297 QSslKey key;
298
299 key.d->type = QSsl::PublicKey;
300
301 EVP_PKEY *pkey = q_X509_get_pubkey(a: d->x509);
302 Q_ASSERT(pkey);
303 const int keyType = q_EVP_PKEY_type(a: q_EVP_PKEY_base_id(a: pkey));
304
305 if (keyType == EVP_PKEY_RSA) {
306 key.d->rsa = q_EVP_PKEY_get1_RSA(a: pkey);
307 key.d->algorithm = QSsl::Rsa;
308 key.d->isNull = false;
309 } else if (keyType == EVP_PKEY_DSA) {
310 key.d->dsa = q_EVP_PKEY_get1_DSA(a: pkey);
311 key.d->algorithm = QSsl::Dsa;
312 key.d->isNull = false;
313#ifndef OPENSSL_NO_EC
314 } else if (keyType == EVP_PKEY_EC) {
315 key.d->ec = q_EVP_PKEY_get1_EC_KEY(a: pkey);
316 key.d->algorithm = QSsl::Ec;
317 key.d->isNull = false;
318#endif
319 } else if (keyType == EVP_PKEY_DH) {
320 // DH unsupported
321 } else {
322 // error?
323 }
324
325 q_EVP_PKEY_free(a: pkey);
326 return key;
327}
328
329/*
330 * Convert unknown extensions to a QVariant.
331 */
332static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
333{
334 Q_ASSERT(ext);
335 // Get the extension specific method object if available,
336 // we cast away the const-ness here because some versions of openssl
337 // don't use const for the parameters in the functions pointers stored
338 // in the object.
339 X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(a: ext));
340 if (!meth) {
341 ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(a: ext);
342 Q_ASSERT(value);
343 QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(x: value)),
344 q_ASN1_STRING_length(a: value));
345 return result;
346 }
347
348 void *ext_internal = q_X509V3_EXT_d2i(a: ext);
349 if (!ext_internal)
350 return {};
351
352 const auto extCleaner = qScopeGuard(f: [meth, ext_internal]{
353 Q_ASSERT(ext_internal && meth);
354
355 if (meth->it)
356 q_ASN1_item_free(val: static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
357 else if (meth->ext_free)
358 meth->ext_free(ext_internal);
359 else
360 qCWarning(lcSsl, "No method to free an unknown extension, a potential memory leak?");
361 });
362
363 // If this extension can be converted
364 if (meth->i2v) {
365 STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
366 const auto stackCleaner = qScopeGuard(f: [val]{
367 if (val)
368 q_OPENSSL_sk_pop_free(a: (OPENSSL_STACK *)val, b: (void(*)(void*))q_X509V3_conf_free);
369 });
370
371 QVariantMap map;
372 QVariantList list;
373 bool isMap = false;
374
375 for (int j = 0; j < q_SKM_sk_num(val); j++) {
376 CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j);
377 if (nval->name && nval->value) {
378 isMap = true;
379 map[QString::fromUtf8(str: nval->name)] = QString::fromUtf8(str: nval->value);
380 } else if (nval->name) {
381 list << QString::fromUtf8(str: nval->name);
382 } else if (nval->value) {
383 list << QString::fromUtf8(str: nval->value);
384 }
385 }
386
387 if (isMap)
388 return map;
389 else
390 return list;
391 } else if (meth->i2s) {
392 const char *hexString = meth->i2s(meth, ext_internal);
393 QVariant result(hexString ? QString::fromUtf8(str: hexString) : QString{});
394 q_OPENSSL_free((void *)hexString);
395 return result;
396 } else if (meth->i2r) {
397 QByteArray result;
398
399 BIO *bio = q_BIO_new(a: q_BIO_s_mem());
400 if (!bio)
401 return result;
402
403 meth->i2r(meth, ext_internal, bio, 0);
404
405 char *bio_buffer;
406 long bio_size = q_BIO_get_mem_data(bio, &bio_buffer);
407 result = QByteArray(bio_buffer, bio_size);
408
409 q_BIO_free(a: bio);
410 return result;
411 }
412
413 return QVariant();
414}
415
416/*
417 * Convert extensions to a variant. The naming of the keys of the map are
418 * taken from RFC 5280, however we decided the capitalisation in the RFC
419 * was too silly for the real world.
420 */
421static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
422{
423 ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(a: ext);
424 int nid = q_OBJ_obj2nid(a: obj);
425
426 // We cast away the const-ness here because some versions of openssl
427 // don't use const for the parameters in the functions pointers stored
428 // in the object.
429 X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(a: ext));
430
431 void *ext_internal = nullptr; // The value, returned by X509V3_EXT_d2i.
432 const auto extCleaner = qScopeGuard(f: [meth, &ext_internal]() {
433 if (!meth || !ext_internal)
434 return;
435
436 if (meth->it)
437 q_ASN1_item_free(val: static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
438 else if (meth->ext_free)
439 meth->ext_free(ext_internal);
440 else
441 qWarning(catFunc: lcSsl, msg: "Cannot free an extension, a potential memory leak?");
442 });
443
444 const char * hexString = nullptr; // The value returned by meth->i2s.
445 const auto hexStringCleaner = qScopeGuard(f: [&hexString](){
446 if (hexString)
447 q_OPENSSL_free((void*)hexString);
448 });
449
450 switch (nid) {
451 case NID_basic_constraints:
452 {
453 BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(a: ext));
454 if (!basic)
455 return {};
456 QVariantMap result;
457 result[QLatin1String("ca")] = basic->ca ? true : false;
458 if (basic->pathlen)
459 result[QLatin1String("pathLenConstraint")] = (qlonglong)q_ASN1_INTEGER_get(a: basic->pathlen);
460
461 q_BASIC_CONSTRAINTS_free(a: basic);
462 return result;
463 }
464 break;
465 case NID_info_access:
466 {
467 AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(a: ext));
468 if (!info)
469 return {};
470 QVariantMap result;
471 for (int i=0; i < q_SKM_sk_num(info); i++) {
472 ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i);
473
474 GENERAL_NAME *name = ad->location;
475 if (name->type == GEN_URI) {
476 int len = q_ASN1_STRING_length(a: name->d.uniformResourceIdentifier);
477 if (len < 0 || len >= 8192) {
478 // broken name
479 continue;
480 }
481
482 const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(x: name->d.uniformResourceIdentifier));
483 const QString uri = QString::fromUtf8(str: uriStr, size: len);
484
485 result[QString::fromUtf8(str: QSslCertificatePrivate::asn1ObjectName(object: ad->method))] = uri;
486 } else {
487 qCWarning(lcSsl) << "Strange location type" << name->type;
488 }
489 }
490
491 q_AUTHORITY_INFO_ACCESS_free(a: info);
492 return result;
493 }
494 break;
495 case NID_subject_key_identifier:
496 {
497 ext_internal = q_X509V3_EXT_d2i(a: ext);
498 if (!ext_internal)
499 return {};
500
501 hexString = meth->i2s(meth, ext_internal);
502 return QVariant(QString::fromUtf8(str: hexString));
503 }
504 break;
505 case NID_authority_key_identifier:
506 {
507 AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(a: ext));
508 if (!auth_key)
509 return {};
510 QVariantMap result;
511
512 // keyid
513 if (auth_key->keyid) {
514 QByteArray keyid(reinterpret_cast<const char *>(auth_key->keyid->data),
515 auth_key->keyid->length);
516 result[QLatin1String("keyid")] = keyid.toHex();
517 }
518
519 // issuer
520 // TODO: GENERAL_NAMES
521
522 // serial
523 if (auth_key->serial)
524 result[QLatin1String("serial")] = (qlonglong)q_ASN1_INTEGER_get(a: auth_key->serial);
525
526 q_AUTHORITY_KEYID_free(a: auth_key);
527 return result;
528 }
529 break;
530 }
531
532 return {};
533}
534
535QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext)
536{
537 Q_ASSERT(ext);
538
539 QSslCertificateExtension result;
540
541 ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(a: ext);
542 if (!obj) {
543 qCWarning(lcSsl, "Invalid (nullptr) ASN1_OBJECT");
544 return result;
545 }
546
547 QByteArray oid = QSslCertificatePrivate::asn1ObjectId(object: obj);
548 QByteArray name = QSslCertificatePrivate::asn1ObjectName(object: obj);
549
550 result.d->oid = QString::fromUtf8(str: oid);
551 result.d->name = QString::fromUtf8(str: name);
552
553 bool critical = q_X509_EXTENSION_get_critical(a: ext);
554 result.d->critical = critical;
555
556 // Lets see if we have custom support for this one
557 QVariant extensionValue = x509ExtensionToValue(ext);
558 if (extensionValue.isValid()) {
559 result.d->value = extensionValue;
560 result.d->supported = true;
561
562 return result;
563 }
564
565 extensionValue = x509UnknownExtensionToValue(ext);
566 if (extensionValue.isValid()) {
567 result.d->value = extensionValue;
568 result.d->supported = false;
569 return result;
570 }
571
572 return result;
573}
574
575QList<QSslCertificateExtension> QSslCertificate::extensions() const
576{
577 QList<QSslCertificateExtension> result;
578
579 if (!d->x509)
580 return result;
581
582 int count = q_X509_get_ext_count(a: d->x509);
583 if (count <= 0)
584 return result;
585
586 result.reserve(alloc: count);
587
588 for (int i = 0; i < count; i++) {
589 X509_EXTENSION *ext = q_X509_get_ext(a: d->x509, b: i);
590 if (!ext) {
591 qCWarning(lcSsl) << "Invalid (nullptr) extension at index" << i;
592 continue;
593 }
594 result << QSslCertificatePrivate::convertExtension(ext);
595 }
596
597 // Converting an extension may result in an error(s), clean them up.
598 Q_UNUSED(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
599
600 return result;
601}
602
603QByteArray QSslCertificate::toPem() const
604{
605 if (!d->x509)
606 return QByteArray();
607 return d->QByteArray_from_X509(x509: d->x509, format: QSsl::Pem);
608}
609
610QByteArray QSslCertificate::toDer() const
611{
612 if (!d->x509)
613 return QByteArray();
614 return d->QByteArray_from_X509(x509: d->x509, format: QSsl::Der);
615}
616
617QString QSslCertificate::toText() const
618{
619 if (!d->x509)
620 return QString();
621 return d->text_from_X509(x509: d->x509);
622}
623
624#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
625#define ENDCERTSTRING "-----END CERTIFICATE-----"
626
627void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
628{
629 if (!data.isEmpty()) {
630 const QList<QSslCertificate> certs = (format == QSsl::Pem)
631 ? certificatesFromPem(pem: data, count: 1)
632 : certificatesFromDer(der: data, count: 1);
633 if (!certs.isEmpty()) {
634 *this = *certs.first().d;
635 if (x509)
636 x509 = q_X509_dup(a: x509);
637 }
638 }
639}
640
641// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
642QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
643{
644 if (!x509) {
645 qCWarning(lcSsl, "QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
646 return QByteArray();
647 }
648
649 // Use i2d_X509 to convert the X509 to an array.
650 int length = q_i2d_X509(a: x509, b: nullptr);
651 QByteArray array;
652 array.resize(size: length);
653 char *data = array.data();
654 char **dataP = &data;
655 unsigned char **dataPu = (unsigned char **)dataP;
656 if (q_i2d_X509(a: x509, b: dataPu) < 0)
657 return QByteArray();
658
659 if (format == QSsl::Der)
660 return array;
661
662 // Convert to Base64 - wrap at 64 characters.
663 array = array.toBase64();
664 QByteArray tmp;
665 for (int i = 0; i <= array.size() - 64; i += 64) {
666 tmp += QByteArray::fromRawData(array.data() + i, size: 64);
667 tmp += '\n';
668 }
669 if (int remainder = array.size() % 64) {
670 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, size: remainder);
671 tmp += '\n';
672 }
673
674 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
675}
676
677QString QSslCertificatePrivate::text_from_X509(X509 *x509)
678{
679 if (!x509) {
680 qCWarning(lcSsl, "QSslSocketBackendPrivate::text_from_X509: null X509");
681 return QString();
682 }
683
684 QByteArray result;
685 BIO *bio = q_BIO_new(a: q_BIO_s_mem());
686 if (!bio)
687 return QString();
688
689 q_X509_print(a: bio, b: x509);
690
691 QVarLengthArray<char, 16384> data;
692 int count = q_BIO_read(a: bio, b: data.data(), c: 16384);
693 if ( count > 0 ) {
694 result = QByteArray( data.data(), count );
695 }
696
697 q_BIO_free(a: bio);
698
699 return QString::fromLatin1(str: result);
700}
701
702QByteArray QSslCertificatePrivate::asn1ObjectId(ASN1_OBJECT *object)
703{
704 char buf[80]; // The openssl docs a buffer length of 80 should be more than enough
705 q_OBJ_obj2txt(buf, buf_len: sizeof(buf), obj: object, no_name: 1); // the 1 says always use the oid not the long name
706
707 return QByteArray(buf);
708}
709
710
711QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object)
712{
713 int nid = q_OBJ_obj2nid(a: object);
714 if (nid != NID_undef)
715 return QByteArray(q_OBJ_nid2sn(a: nid));
716
717 return asn1ObjectId(object);
718}
719
720static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name)
721{
722 QMultiMap<QByteArray, QString> info;
723 for (int i = 0; i < q_X509_NAME_entry_count(a: name); ++i) {
724 X509_NAME_ENTRY *e = q_X509_NAME_get_entry(a: name, b: i);
725
726 QByteArray name = QSslCertificatePrivate::asn1ObjectName(object: q_X509_NAME_ENTRY_get_object(a: e));
727 unsigned char *data = nullptr;
728 int size = q_ASN1_STRING_to_UTF8(a: &data, b: q_X509_NAME_ENTRY_get_data(a: e));
729 info.insert(akey: name, avalue: QString::fromUtf8(str: (char*)data, size));
730#if QT_CONFIG(opensslv11)
731 q_CRYPTO_free(str: data, file: nullptr, line: 0);
732#else
733 q_CRYPTO_free(data);
734#endif
735 }
736
737 return info;
738}
739
740QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
741{
742 QSslCertificate certificate;
743 if (!x509 || !QSslSocket::supportsSsl())
744 return certificate;
745
746 ASN1_TIME *nbef = q_X509_getm_notBefore(a: x509);
747 ASN1_TIME *naft = q_X509_getm_notAfter(a: x509);
748
749 certificate.d->notValidBefore = q_getTimeFromASN1(aTime: nbef);
750 certificate.d->notValidAfter = q_getTimeFromASN1(aTime: naft);
751 certificate.d->null = false;
752 certificate.d->x509 = q_X509_dup(a: x509);
753
754 return certificate;
755}
756
757static bool matchLineFeed(const QByteArray &pem, int *offset)
758{
759 char ch = 0;
760
761 // ignore extra whitespace at the end of the line
762 while (*offset < pem.size() && (ch = pem.at(i: *offset)) == ' ')
763 ++*offset;
764
765 if (ch == '\n') {
766 *offset += 1;
767 return true;
768 }
769 if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(i: *offset + 1) == '\n') {
770 *offset += 2;
771 return true;
772 }
773 return false;
774}
775
776QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
777{
778 QList<QSslCertificate> certificates;
779 QSslSocketPrivate::ensureInitialized();
780
781 int offset = 0;
782 while (count == -1 || certificates.size() < count) {
783 int startPos = pem.indexOf(BEGINCERTSTRING, from: offset);
784 if (startPos == -1)
785 break;
786 startPos += sizeof(BEGINCERTSTRING) - 1;
787 if (!matchLineFeed(pem, offset: &startPos))
788 break;
789
790 int endPos = pem.indexOf(ENDCERTSTRING, from: startPos);
791 if (endPos == -1)
792 break;
793
794 offset = endPos + sizeof(ENDCERTSTRING) - 1;
795 if (offset < pem.size() && !matchLineFeed(pem, offset: &offset))
796 break;
797
798 QByteArray decoded = QByteArray::fromBase64(
799 base64: QByteArray::fromRawData(pem.data() + startPos, size: endPos - startPos));
800 const unsigned char *data = (const unsigned char *)decoded.data();
801
802 if (X509 *x509 = q_d2i_X509(a: nullptr, b: &data, c: decoded.size())) {
803 certificates << QSslCertificate_from_X509(x509);
804 q_X509_free(a: x509);
805 }
806 }
807
808 return certificates;
809}
810
811QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
812{
813 QList<QSslCertificate> certificates;
814 QSslSocketPrivate::ensureInitialized();
815
816 const unsigned char *data = (const unsigned char *)der.data();
817 int size = der.size();
818
819 while (size > 0 && (count == -1 || certificates.size() < count)) {
820 if (X509 *x509 = q_d2i_X509(a: nullptr, b: &data, c: size)) {
821 certificates << QSslCertificate_from_X509(x509);
822 q_X509_free(a: x509);
823 } else {
824 break;
825 }
826 size -= ((const char *)data - der.data());
827 }
828
829 return certificates;
830}
831
832QT_END_NAMESPACE
833

source code of qtbase/src/network/ssl/qsslcertificate_openssl.cpp