Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qx509_openssl.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include "qtlskey_openssl_p.h"
7#include "qx509_openssl_p.h"
8#include "qtls_openssl_p.h"
9
10#include <QtNetwork/private/qsslcertificate_p.h>
11
12#include <QtNetwork/qsslsocket.h>
13#include <QtNetwork/qhostaddress.h>
14
15#include <QtCore/qendian.h>
16#include <QtCore/qdatetime.h>
17#include <QtCore/qhash.h>
18#include <QtCore/qiodevice.h>
19#include <QtCore/qscopeguard.h>
20#include <QtCore/qtimezone.h>
21#include <QtCore/qvarlengtharray.h>
22
24
25using namespace Qt::StringLiterals;
26
27namespace QTlsPrivate {
28
29namespace {
30
31QByteArray asn1ObjectId(ASN1_OBJECT *object)
32{
33 if (!object)
34 return {};
35
36 char buf[80] = {}; // The openssl docs a buffer length of 80 should be more than enough
37 q_OBJ_obj2txt(buf, sizeof(buf), object, 1); // the 1 says always use the oid not the long name
38
39 return QByteArray(buf);
40}
41
42QByteArray asn1ObjectName(ASN1_OBJECT *object)
43{
44 if (!object)
45 return {};
46
47 const int nid = q_OBJ_obj2nid(object);
48 if (nid != NID_undef)
49 return QByteArray(q_OBJ_nid2sn(nid));
50
51 return asn1ObjectId(object);
52}
53
54QMultiMap<QByteArray, QString> mapFromX509Name(X509_NAME *name)
55{
56 if (!name)
57 return {};
58
59 QMultiMap<QByteArray, QString> info;
60 for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) {
61 X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
62
63 QByteArray name = asn1ObjectName(q_X509_NAME_ENTRY_get_object(e));
64 unsigned char *data = nullptr;
66 info.insert(name, QString::fromUtf8((char*)data, size));
67 q_CRYPTO_free(data, nullptr, 0);
68 }
69
70 return info;
71}
72
73QDateTime dateTimeFromASN1(const ASN1_TIME *aTime)
74{
76 tm lTime;
77
78 if (q_ASN1_TIME_to_tm(aTime, &lTime)) {
79 QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday);
80 QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
81 result = QDateTime(resDate, resTime, QTimeZone::UTC);
82 }
83
84 return result;
85}
86
87
88#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
89#define ENDCERTSTRING "-----END CERTIFICATE-----"
90
91QByteArray x509ToQByteArray(X509 *x509, QSsl::EncodingFormat format)
92{
93 Q_ASSERT(x509);
94
95 // Use i2d_X509 to convert the X509 to an array.
96 const int length = q_i2d_X509(x509, nullptr);
97 if (length <= 0) {
99 return {};
100 }
101
104
105 char *data = array.data();
106 char **dataP = &data;
107 unsigned char **dataPu = (unsigned char **)dataP;
108 if (q_i2d_X509(x509, dataPu) < 0)
109 return QByteArray();
110
111 if (format == QSsl::Der)
112 return array;
113
114 // Convert to Base64 - wrap at 64 characters.
115 array = array.toBase64();
116 QByteArray tmp;
117 for (int i = 0; i <= array.size() - 64; i += 64) {
118 tmp += QByteArray::fromRawData(array.data() + i, 64);
119 tmp += '\n';
120 }
121 if (int remainder = array.size() % 64) {
122 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
123 tmp += '\n';
124 }
125
126 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
127}
128
129QString x509ToText(X509 *x509)
130{
131 Q_ASSERT(x509);
132
134 BIO *bio = q_BIO_new(q_BIO_s_mem());
135 if (!bio)
136 return QString();
137 const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
138
139 q_X509_print(bio, x509);
140
141 QVarLengthArray<char, 16384> data;
142 int count = q_BIO_read(bio, data.data(), 16384);
143 if ( count > 0 )
144 result = QByteArray( data.data(), count );
145
147}
148
149QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
150{
151 // Get the extension specific method object if available
152 // we cast away the const-ness here because some versions of openssl
153 // don't use const for the parameters in the functions pointers stored
154 // in the object.
155 Q_ASSERT(ext);
156
157 X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
158 if (!meth) {
159 ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext);
161 QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(value)),
163 return result;
164 }
165
166 void *ext_internal = q_X509V3_EXT_d2i(ext);
167 if (!ext_internal)
168 return {};
169
170 const auto extCleaner = qScopeGuard([meth, ext_internal]{
171 Q_ASSERT(ext_internal && meth);
172
173 if (meth->it)
174 q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
175 else if (meth->ext_free)
176 meth->ext_free(ext_internal);
177 else
178 qCWarning(lcTlsBackend, "No method to free an unknown extension, a potential memory leak?");
179 });
180
181 // If this extension can be converted
182 if (meth->i2v) {
183 STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
184 const auto stackCleaner = qScopeGuard([val]{
185 if (val)
186 q_OPENSSL_sk_pop_free((OPENSSL_STACK *)val, (void(*)(void*))q_X509V3_conf_free);
187 });
188
191 bool isMap = false;
192
193 for (int j = 0; j < q_SKM_sk_num(val); j++) {
194 CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j);
195 if (nval->name && nval->value) {
196 isMap = true;
197 map[QString::fromUtf8(nval->name)] = QString::fromUtf8(nval->value);
198 } else if (nval->name) {
199 list << QString::fromUtf8(nval->name);
200 } else if (nval->value) {
201 list << QString::fromUtf8(nval->value);
202 }
203 }
204
205 if (isMap)
206 return map;
207 else
208 return list;
209 } else if (meth->i2s) {
210 const char *hexString = meth->i2s(meth, ext_internal);
211 QVariant result(hexString ? QString::fromUtf8(hexString) : QString{});
212 q_OPENSSL_free((void *)hexString);
213 return result;
214 } else if (meth->i2r) {
216
217 BIO *bio = q_BIO_new(q_BIO_s_mem());
218 if (!bio)
219 return result;
220
221 meth->i2r(meth, ext_internal, bio, 0);
222
223 char *bio_buffer;
224 long bio_size = q_BIO_get_mem_data(bio, &bio_buffer);
225 result = QByteArray(bio_buffer, bio_size);
226
227 q_BIO_free(bio);
228 return result;
229 }
230
231 return QVariant();
232}
233
234/*
235 * Convert extensions to a variant. The naming of the keys of the map are
236 * taken from RFC 5280, however we decided the capitalisation in the RFC
237 * was too silly for the real world.
238 */
239QVariant x509ExtensionToValue(X509_EXTENSION *ext)
240{
241 ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
242 int nid = q_OBJ_obj2nid(obj);
243
244 // We cast away the const-ness here because some versions of openssl
245 // don't use const for the parameters in the functions pointers stored
246 // in the object.
247 X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
248
249 void *ext_internal = nullptr; // The value, returned by X509V3_EXT_d2i.
250 const auto extCleaner = qScopeGuard([meth, &ext_internal]() {
251 if (!meth || !ext_internal)
252 return;
253
254 if (meth->it)
255 q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
256 else if (meth->ext_free)
257 meth->ext_free(ext_internal);
258 else
259 qWarning(lcTlsBackend, "Cannot free an extension, a potential memory leak?");
260 });
261
262 const char * hexString = nullptr; // The value returned by meth->i2s.
263 const auto hexStringCleaner = qScopeGuard([&hexString](){
264 if (hexString)
265 q_OPENSSL_free((void*)hexString);
266 });
267
268 switch (nid) {
269 case NID_basic_constraints:
270 {
271 BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext));
272 if (!basic)
273 return {};
275 result["ca"_L1] = basic->ca ? true : false;
276 if (basic->pathlen)
277 result["pathLenConstraint"_L1] = (qlonglong)q_ASN1_INTEGER_get(basic->pathlen);
278
280 return result;
281 }
282 break;
283 case NID_info_access:
284 {
285 AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext));
286 if (!info)
287 return {};
289 for (int i=0; i < q_SKM_sk_num(info); i++) {
290 ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i);
291
292 GENERAL_NAME *name = ad->location;
293 if (name->type == GEN_URI) {
294 int len = q_ASN1_STRING_length(name->d.uniformResourceIdentifier);
295 if (len < 0 || len >= 8192) {
296 // broken name
297 continue;
298 }
299
300 const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier));
301 const QString uri = QString::fromUtf8(uriStr, len);
302
303 result[QString::fromUtf8(asn1ObjectName(ad->method))] = uri;
304 } else {
305 qCWarning(lcTlsBackend) << "Strange location type" << name->type;
306 }
307 }
308
310 return result;
311 }
312 break;
313 case NID_subject_key_identifier:
314 {
315 ext_internal = q_X509V3_EXT_d2i(ext);
316 if (!ext_internal)
317 return {};
318
319 hexString = meth->i2s(meth, ext_internal);
320 return QVariant(QString::fromUtf8(hexString));
321 }
322 break;
323 case NID_authority_key_identifier:
324 {
325 AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(ext));
326 if (!auth_key)
327 return {};
329
330 // keyid
331 if (auth_key->keyid) {
332 QByteArray keyid(reinterpret_cast<const char *>(auth_key->keyid->data),
333 auth_key->keyid->length);
334 result["keyid"_L1] = keyid.toHex();
335 }
336
337 // issuer
338 // TODO: GENERAL_NAMES
339
340 // serial
341 if (auth_key->serial)
342 result["serial"_L1] = (qlonglong)q_ASN1_INTEGER_get(auth_key->serial);
343
344 q_AUTHORITY_KEYID_free(auth_key);
345 return result;
346 }
347 break;
348 }
349
350 return {};
351}
352
353} // Unnamed namespace
354
355extern "C" int qt_X509Callback(int ok, X509_STORE_CTX *ctx)
356{
357 if (!ok) {
358 // Store the error and at which depth the error was detected.
359 using ErrorListPtr = QList<QSslErrorEntry> *;
360 ErrorListPtr errors = nullptr;
361
362 // Error list is attached to either 'SSL' or 'X509_STORE'.
363 if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) // We try store first:
364 errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
365
366 if (!errors) {
367 // Not found on store? Try SSL and its external data then. According to the OpenSSL's
368 // documentation:
369 //
370 // "Whenever a X509_STORE_CTX object is created for the verification of the
371 // peer's certificate during a handshake, a pointer to the SSL object is
372 // stored into the X509_STORE_CTX object to identify the connection affected.
373 // To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
374 // used with the correct index."
375
376 // TLSTODO: verification callback has to change as soon as TlsCryptographer is in place.
377 // This is a temporary solution for now to ease the transition.
380 if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
381 errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
382 }
383
384 if (!errors) {
385 qCWarning(lcTlsBackend, "Neither X509_STORE, nor SSL contains error list, verification failed");
386 return 0;
387 }
388
390 }
391 // Always return OK to allow verification to continue. We handle the
392 // errors gracefully after collecting all errors, after verification has
393 // completed.
394 return 1;
395}
396
398
404
406{
407 //TLSTODO: to make it safe I'll check the backend type later.
408 const auto &other = static_cast<const X509CertificateOpenSSL &>(rhs);
409 if (x509 && other.x509) {
410 const int ret = q_X509_cmp(x509, other.x509);
411 if (ret >= -1 && ret <= 1)
412 return ret == 0;
414 }
415
416 return false;
417}
418
420{
421 if (!x509)
422 return false;
423
424 return q_X509_check_issued(x509, x509) == X509_V_OK;
425}
426
427QMultiMap<QSsl::AlternativeNameEntryType, QString>
429{
430 QMultiMap<QSsl::AlternativeNameEntryType, QString> result;
431
432 if (!x509)
433 return result;
434
435 auto *altNames = static_cast<STACK_OF(GENERAL_NAME) *>(q_X509_get_ext_d2i(x509, NID_subject_alt_name,
436 nullptr, nullptr));
437 if (!altNames)
438 return result;
439
440 auto altName = [](ASN1_IA5STRING *ia5, int len) {
441 const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5));
442 return QString::fromLatin1(altNameStr, len);
443 };
444
445 for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
446 const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
447 if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD)
448 continue;
449
450 const int len = q_ASN1_STRING_length(genName->d.ia5);
451 if (len < 0 || len >= 8192) {
452 // broken name
453 continue;
454 }
455
456 switch (genName->type) {
457 case GEN_DNS:
458 result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len));
459 break;
460 case GEN_EMAIL:
461 result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len));
462 break;
463 case GEN_IPADD: {
464 QHostAddress ipAddress;
465 switch (len) {
466 case 4: // IPv4
467 ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(genName->d.iPAddress->data)));
468 break;
469 case 16: // IPv6
470 ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data));
471 break;
472 default: // Unknown IP address format
473 break;
474 }
475 if (!ipAddress.isNull())
476 result.insert(QSsl::IpAddressEntry, ipAddress.toString());
477 break;
478 }
479 default:
480 break;
481 }
482 }
483
484 q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free));
485
486 return result;
487}
488
490{
491 if (!x509)
492 return {};
493
495}
496
498{
499 if (!x509)
500 return {};
501
502 return x509ToQByteArray(x509, QSsl::Pem);
503}
504
506{
507 if (!x509)
508 return {};
509
510 return x509ToQByteArray(x509, QSsl::Der);
511
512}
514{
515 if (!x509)
516 return {};
517
518 return x509ToText(x509);
519}
520
522{
523 return Qt::HANDLE(x509);
524}
525
526size_t X509CertificateOpenSSL::hash(size_t seed) const noexcept
527{
528 if (x509) {
529 const EVP_MD *sha1 = q_EVP_sha1();
530 unsigned int len = 0;
531 unsigned char md[EVP_MAX_MD_SIZE];
532 q_X509_digest(x509, sha1, md, &len);
533 return qHashBits(md, len, seed);
534 }
535
536 return seed;
537}
538
540{
541 QSslCertificate certificate;
542
543 auto *backend = QTlsBackend::backend<X509CertificateOpenSSL>(certificate);
544 if (!backend || !x509)
545 return certificate;
546
547 ASN1_TIME *nbef = q_X509_getm_notBefore(x509);
548 if (nbef)
549 backend->notValidBefore = dateTimeFromASN1(nbef);
550
551 ASN1_TIME *naft = q_X509_getm_notAfter(x509);
552 if (naft)
553 backend->notValidAfter = dateTimeFromASN1(naft);
554
555 backend->null = false;
556 backend->x509 = q_X509_dup(x509);
557
558 backend->issuerInfoEntries = mapFromX509Name(q_X509_get_issuer_name(x509));
559 backend->subjectInfoEntries = mapFromX509Name(q_X509_get_subject_name(x509));
560 backend->versionString = QByteArray::number(qlonglong(q_X509_get_version(x509)) + 1);
561
562 if (ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(x509)) {
563 QByteArray hexString;
564 hexString.reserve(serialNumber->length * 3);
565 for (int a = 0; a < serialNumber->length; ++a) {
566 hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0');
567 hexString += ':';
568 }
569 hexString.chop(1);
570 backend->serialNumberString = hexString;
571 }
572
573 backend->parseExtensions();
574
575 return certificate;
576}
577
579{
580 if (!x509)
581 return {};
582
583 QList<QSslCertificate> certificates;
584 for (int i = 0; i < q_sk_X509_num(x509); ++i) {
585 if (X509 *entry = q_sk_X509_value(x509, i))
586 certificates << certificateFromX509(entry);
587 }
588
589 return certificates;
590}
591
598
599QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &chain,
600 const QString &hostName)
601{
602 // This was previously QSslSocketPrivate::verify().
603 auto roots = QSslConfiguration::defaultConfiguration().caCertificates();
604#ifndef Q_OS_WIN
605 // On Windows, system CA certificates are already set as default ones.
606 // No need to add them again (and again) and also, if the default configuration
607 // has its own set of CAs, this probably should not be amended by the ones
608 // from the 'ROOT' store, since it's not what an application chose to trust.
611#endif // Q_OS_WIN
612 return verify(roots, chain, hostName);
613}
614
615QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &caCertificates,
616 const QList<QSslCertificate> &certificateChain,
617 const QString &hostName)
618{
619 // This was previously QSslSocketPrivate::verify().
620 if (certificateChain.size() <= 0)
622
623 QList<QSslError> errors;
624 X509_STORE *certStore = q_X509_STORE_new();
625 if (!certStore) {
626 qCWarning(lcTlsBackend) << "Unable to create certificate store";
628 return errors;
629 }
630 const std::unique_ptr<X509_STORE, decltype(&q_X509_STORE_free)> storeGuard(certStore, q_X509_STORE_free);
631
633 for (const QSslCertificate &caCertificate : caCertificates) {
634 // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
635 //
636 // If several CA certificates matching the name, key identifier, and
637 // serial number condition are available, only the first one will be
638 // examined. This may lead to unexpected results if the same CA
639 // certificate is available with different expiration dates. If a
640 // ``certificate expired'' verification error occurs, no other
641 // certificate will be searched. Make sure to not have expired
642 // certificates mixed with valid ones.
643 //
644 // See also: QSslContext::sharedFromConfiguration()
645 if (caCertificate.expiryDate() >= now) {
646 q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
647 }
648 }
649
650 QList<QSslErrorEntry> lastErrors;
651 if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
652 qCWarning(lcTlsBackend) << "Unable to attach external data (error list) to a store";
654 return errors;
655 }
656
657 // Register a custom callback to get all verification errors.
659
660 // Build the chain of intermediate certificates
661 STACK_OF(X509) *intermediates = nullptr;
662 if (certificateChain.size() > 1) {
663 intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
664
665 if (!intermediates) {
667 return errors;
668 }
669
670 bool first = true;
671 for (const QSslCertificate &cert : certificateChain) {
672 if (first) {
673 first = false;
674 continue;
675 }
676
677 q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast<X509 *>(cert.handle()));
678 }
679 }
680
681 X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new();
682 if (!storeContext) {
684 return errors;
685 }
686 std::unique_ptr<X509_STORE_CTX, decltype(&q_X509_STORE_CTX_free)> ctxGuard(storeContext, q_X509_STORE_CTX_free);
687
688 if (!q_X509_STORE_CTX_init(storeContext, certStore, reinterpret_cast<X509 *>(certificateChain[0].handle()), intermediates)) {
690 return errors;
691 }
692
693 // Now we can actually perform the verification of the chain we have built.
694 // We ignore the result of this function since we process errors via the
695 // callback.
696 (void) q_X509_verify_cert(storeContext);
697 ctxGuard.reset();
698 q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
699
700 // Now process the errors
701
702 if (certificateChain[0].isBlacklisted())
703 errors << QSslError(QSslError::CertificateBlacklisted, certificateChain[0]);
704
705 // Check the certificate name against the hostname if one was specified
706 if (!hostName.isEmpty() && !TlsCryptograph::isMatchingHostname(certificateChain[0], hostName)) {
707 // No matches in common names or alternate names.
708 QSslError error(QSslError::HostNameMismatch, certificateChain[0]);
709 errors << error;
710 }
711
712 // Translate errors from the error list into QSslErrors.
713 errors.reserve(errors.size() + lastErrors.size());
714 for (const auto &error : std::as_const(lastErrors))
715 errors << openSSLErrorToQSslError(error.code, certificateChain.value(error.depth));
716
717 return errors;
718}
719
720QList<QSslCertificate> X509CertificateOpenSSL::certificatesFromPem(const QByteArray &pem, int count)
721{
722 QList<QSslCertificate> certificates;
723
724 int offset = 0;
725 while (count == -1 || certificates.size() < count) {
726 int startPos = pem.indexOf(BEGINCERTSTRING, offset);
727 if (startPos == -1)
728 break;
729 startPos += sizeof(BEGINCERTSTRING) - 1;
730 if (!matchLineFeed(pem, &startPos))
731 break;
732
733 int endPos = pem.indexOf(ENDCERTSTRING, startPos);
734 if (endPos == -1)
735 break;
736
737 offset = endPos + sizeof(ENDCERTSTRING) - 1;
738 if (offset < pem.size() && !matchLineFeed(pem, &offset))
739 break;
740
742 QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
743 const unsigned char *data = (const unsigned char *)decoded.data();
744
745 if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) {
746 certificates << certificateFromX509(x509);
747 q_X509_free(x509);
748 }
749 }
750
751 return certificates;
752}
753
754QList<QSslCertificate> X509CertificateOpenSSL::certificatesFromDer(const QByteArray &der, int count)
755{
756 QList<QSslCertificate> certificates;
757
758 const unsigned char *data = (const unsigned char *)der.data();
759 int size = der.size();
760
761 while (size > 0 && (count == -1 || certificates.size() < count)) {
762 if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) {
763 certificates << certificateFromX509(x509);
764 q_X509_free(x509);
765 } else {
766 break;
767 }
768 size -= ((const char *)data - der.data());
769 }
770
771 return certificates;
772}
773
775 QList<QSslCertificate> *caCertificates,
776 const QByteArray &passPhrase)
777{
778 // These are required
780 Q_ASSERT(key);
781 Q_ASSERT(cert);
782
783 // Read the file into a BIO
784 QByteArray pkcs12data = device->readAll();
785 if (pkcs12data.size() == 0)
786 return false;
787
788 BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pkcs12data.constData()), pkcs12data.size());
789 if (!bio) {
790 qCWarning(lcTlsBackend, "BIO_new_mem_buf returned null");
791 return false;
792 }
793 const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
794
795 // Create the PKCS#12 object
796 PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr);
797 if (!p12) {
798 qCWarning(lcTlsBackend, "Unable to read PKCS#12 structure, %s",
800 return false;
801 }
802 const auto p12Raii = qScopeGuard([p12]{q_PKCS12_free(p12);});
803
804 // Extract the data
805 EVP_PKEY *pkey = nullptr;
806 X509 *x509 = nullptr;
807 STACK_OF(X509) *ca = nullptr;
808
809 if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) {
810 qCWarning(lcTlsBackend, "Unable to parse PKCS#12 structure, %s",
812 return false;
813 }
814
815 const auto x509Raii = qScopeGuard([x509]{q_X509_free(x509);});
816 const auto keyRaii = qScopeGuard([pkey]{q_EVP_PKEY_free(pkey);});
817 const auto caRaii = qScopeGuard([ca] {
818 q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
819 reinterpret_cast<void (*)(void *)>(q_X509_free));
820 });
821
822 // Convert to Qt types
823 auto *tlsKey = QTlsBackend::backend<TlsKeyOpenSSL>(*key);
824 if (!tlsKey || !tlsKey->fromEVP_PKEY(pkey)) {
825 qCWarning(lcTlsBackend, "Unable to convert private key");
826 return false;
827 }
828
829 *cert = certificateFromX509(x509);
830
831 if (caCertificates)
832 *caCertificates = stackOfX509ToQSslCertificates(ca);
833
834 return true;
835}
836
838{
840 switch (errorCode) {
841 case X509_V_OK:
842 // X509_V_OK is also reported if the peer had no certificate.
843 break;
844 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
846 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
848 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
850 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
852 case X509_V_ERR_CERT_NOT_YET_VALID:
854 case X509_V_ERR_CERT_HAS_EXPIRED:
856 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
858 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
860 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
862 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
864 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
866 case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
868 case X509_V_ERR_CERT_REVOKED:
870 case X509_V_ERR_INVALID_CA:
872 case X509_V_ERR_PATH_LENGTH_EXCEEDED:
874 case X509_V_ERR_INVALID_PURPOSE:
876 case X509_V_ERR_CERT_UNTRUSTED:
878 case X509_V_ERR_CERT_REJECTED:
880 default:
882 }
883 return error;
884}
885
886void X509CertificateOpenSSL::parseExtensions()
887{
888 extensions.clear();
889
890 if (!x509)
891 return;
892
893 int count = q_X509_get_ext_count(x509);
894 if (count <= 0)
895 return;
896
897 extensions.reserve(count);
898
899 for (int i = 0; i < count; i++) {
900 X509_EXTENSION *ext = q_X509_get_ext(x509, i);
901 if (!ext) {
902 qCWarning(lcTlsBackend) << "Invalid (nullptr) extension at index" << i;
903 continue;
904 }
905
906 extensions << convertExtension(ext);
907 }
908
909 // Converting an extension may result in an error(s), clean them up:
911}
912
913X509CertificateBase::X509CertificateExtension X509CertificateOpenSSL::convertExtension(X509_EXTENSION *ext)
914{
915 Q_ASSERT(ext);
916
917 X509CertificateExtension result;
918
919 ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
920 if (!obj)
921 return result;
922
923 result.oid = QString::fromUtf8(asn1ObjectId(obj));
924 result.name = QString::fromUtf8(asn1ObjectName(obj));
925
926 result.critical = bool(q_X509_EXTENSION_get_critical(ext));
927
928 // Lets see if we have custom support for this one
929 QVariant extensionValue = x509ExtensionToValue(ext);
930 if (extensionValue.isValid()) {
931 result.value = extensionValue;
932 result.supported = true;
933 return result;
934 }
935
936 extensionValue = x509UnknownExtensionToValue(ext);
937 if (extensionValue.isValid())
938 result.value = extensionValue;
939
940 result.supported = false;
941
942 return result;
943}
944
945} // namespace QTlsPrivate
946
IOBluetoothDevice * device
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:634
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:499
static QByteArray fromBase64(const QByteArray &base64, Base64Options options=Base64Encoding)
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime currentDateTimeUtc()
\inmodule QtCore \reentrant
Definition qdatetime.h:29
The QHostAddress class provides an IP address.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
The QSslCertificate class provides a convenient API for an X509 certificate.
static QSslConfiguration defaultConfiguration()
Returns the default SSL configuration to be used in new SSL connections.
The QSslError class provides an SSL error.
Definition qsslerror.h:21
@ InvalidNotAfterField
Definition qsslerror.h:33
@ CertificateRejected
Definition qsslerror.h:43
@ SelfSignedCertificate
Definition qsslerror.h:34
@ UnableToVerifyFirstCertificate
Definition qsslerror.h:37
@ CertificateRevoked
Definition qsslerror.h:38
@ CertificateNotYetValid
Definition qsslerror.h:30
@ CertificateBlacklisted
Definition qsslerror.h:49
@ CertificateExpired
Definition qsslerror.h:31
@ UnableToDecodeIssuerPublicKey
Definition qsslerror.h:28
@ UnableToDecryptCertificateSignature
Definition qsslerror.h:27
@ UnableToGetLocalIssuerCertificate
Definition qsslerror.h:36
@ UnableToGetIssuerCertificate
Definition qsslerror.h:26
@ UnspecifiedError
Definition qsslerror.h:62
@ PathLengthExceeded
Definition qsslerror.h:40
@ SelfSignedCertificateInChain
Definition qsslerror.h:35
@ HostNameMismatch
Definition qsslerror.h:47
@ InvalidNotBeforeField
Definition qsslerror.h:32
@ CertificateUntrusted
Definition qsslerror.h:42
@ CertificateSignatureFailed
Definition qsslerror.h:29
@ InvalidPurpose
Definition qsslerror.h:41
@ InvalidCaCertificate
Definition qsslerror.h:39
The QSslKey class provides an interface for private and public keys.
Definition qsslkey.h:23
static bool rootCertOnDemandLoadingSupported()
static QList< QSslCertificate > systemCaCertificates()
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
\inmodule QtCore \reentrant
Definition qdatetime.h:215
static void logAndClearErrorQueue()
static TlsKeyOpenSSL * publicKeyFromX509(X509 *x)
TlsKey is an abstract class, that allows a TLS plugin to provide an underlying implementation for the...
QList< X509CertificateExtension > extensions
QByteArray serialNumber() const override
static bool matchLineFeed(const QByteArray &pem, int *offset)
static QSslErrorEntry errorEntryFromStoreContext(X509_STORE_CTX *ctx)
static QSslCertificate certificateFromX509(X509 *x)
QByteArray toPem() const override
static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList< QSslCertificate > *caCertificates, const QByteArray &passPhrase)
static QList< QSslCertificate > certificatesFromDer(const QByteArray &der, int count)
QByteArray toDer() const override
static QSslError openSSLErrorToQSslError(int errorCode, const QSslCertificate &cert)
static QList< QSslCertificate > certificatesFromPem(const QByteArray &pem, int count)
TlsKey * publicKey() const override
size_t hash(size_t seed) const noexcept override
bool isEqual(const X509Certificate &rhs) const override
Qt::HANDLE handle() const override
static QList< QSslError > verify(const QList< QSslCertificate > &chain, const QString &hostName)
static QList< QSslCertificate > stackOfX509ToQSslCertificates(STACK_OF(X509) *x509)
QMultiMap< QSsl::AlternativeNameEntryType, QString > subjectAlternativeNames() const override
X509Certificate is an abstract class that allows a TLS backend to provide an implementation of the QS...
\inmodule QtCore
Definition qvariant.h:65
EGLContext ctx
QMap< QString, QString > map
[6]
EncodingFormat
Describes supported encoding formats for certificates and keys.
Definition qssl.h:28
@ Pem
Definition qssl.h:29
@ Der
Definition qssl.h:30
@ IpAddressEntry
Definition qssl.h:46
@ EmailEntry
Definition qssl.h:44
@ DnsEntry
Definition qssl.h:45
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
int qt_X509Callback(int ok, X509_STORE_CTX *ctx)
void * HANDLE
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr T qFromBigEndian(T source)
Definition qendian.h:174
size_t qHashBits(const void *p, size_t size, size_t seed) noexcept
Definition qhash.cpp:1089
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
return ret
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLint GLsizei GLsizei GLenum format
GLhandleARB obj
[2]
GLuint GLfloat * val
GLuint entry
GLenum array
GLuint64EXT * result
[6]
GLenum GLsizei len
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
X509_STORE_CTX * q_X509_STORE_CTX_new()
int q_OBJ_obj2nid(const ASN1_OBJECT *a)
int q_ASN1_STRING_length(ASN1_STRING *a)
void * q_X509_STORE_get_ex_data(X509_STORE *r, int idx)
OPENSSL_STACK * q_OPENSSL_sk_new_null()
int q_SSL_get_ex_data_X509_STORE_CTX_idx()
const char * q_OBJ_nid2sn(int a)
int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca)
int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, STACK_OF(X509) *chain)
void q_BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *a)
unsigned long q_ERR_get_error()
int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len)
#define q_sk_GENERAL_NAME_value(st, i)
const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x)
void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data)
#define q_sk_X509_num(st)
const EVP_MD * q_EVP_sha1()
X509 * q_X509_dup(X509 *a)
PKCS12 * q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12)
int q_X509_EXTENSION_get_critical(X509_EXTENSION *a)
X509_NAME * q_X509_get_issuer_name(X509 *a)
#define q_BIO_get_mem_data(b, pp)
void * q_X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx)
void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb)
int q_X509_cmp(X509 *a, X509 *b)
void q_X509V3_conf_free(CONF_VALUE *val)
void * q_X509V3_EXT_d2i(X509_EXTENSION *a)
STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx)
void q_OPENSSL_sk_free(OPENSSL_STACK *a)
void q_CRYPTO_free(void *str, const char *file, int line)
void q_EVP_PKEY_free(EVP_PKEY *a)
void q_AUTHORITY_KEYID_free(AUTHORITY_KEYID *a)
void q_X509_STORE_free(X509_STORE *store)
ASN1_STRING * q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a)
BIO * q_BIO_new(const BIO_METHOD *a)
long q_X509_get_version(X509 *a)
int q_i2d_X509(X509 *a, unsigned char **b)
ASN1_TIME * q_X509_getm_notAfter(X509 *a)
X509_STORE * q_X509_STORE_new()
ASN1_OBJECT * q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a)
void q_PKCS12_free(PKCS12 *pkcs12)
void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void(*b)(void *))
void * q_SSL_get_ex_data(const SSL *ssl, int idx)
void q_AUTHORITY_INFO_ACCESS_free(AUTHORITY_INFO_ACCESS *a)
ASN1_INTEGER * q_X509_get_serialNumber(X509 *a)
void q_ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it)
long q_ASN1_INTEGER_get(ASN1_INTEGER *a)
int q_X509_check_issued(X509 *a, X509 *b)
const BIO_METHOD * q_BIO_s_mem()
int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b)
int q_BIO_read(BIO *a, void *b, int c)
char * q_ERR_error_string(unsigned long a, char *b)
ASN1_OCTET_STRING * q_X509_EXTENSION_get_data(X509_EXTENSION *a)
int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name)
#define q_SKM_sk_num(st)
X509_NAME_ENTRY * q_X509_NAME_get_entry(X509_NAME *a, int b)
void q_X509_print(BIO *a, X509 *b)
void q_X509_free(X509 *a)
X509_NAME * q_X509_get_subject_name(X509 *a)
int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx)
void * q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d)
void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx)
const X509V3_EXT_METHOD * q_X509V3_EXT_get(X509_EXTENSION *a)
int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
BIO * q_BIO_new_mem_buf(void *a, int b)
#define q_sk_X509_value(st, i)
int q_X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data)
int q_X509_get_ext_count(X509 *a)
void q_GENERAL_NAME_free(GENERAL_NAME *a)
int q_X509_verify_cert(X509_STORE_CTX *ctx)
X509_STORE * q_X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx)
X509 * q_d2i_X509(X509 **a, const unsigned char **b, long c)
X509_EXTENSION * q_X509_get_ext(X509 *a, int b)
int q_ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm)
int q_X509_NAME_entry_count(X509_NAME *a)
#define q_SKM_sk_value(type, st, i)
int q_BIO_free(BIO *a)
ASN1_OBJECT * q_X509_EXTENSION_get_object(X509_EXTENSION *a)
#define q_OPENSSL_free(addr)
ASN1_TIME * q_X509_getm_notBefore(X509 *a)
#define q_sk_GENERAL_NAME_num(st)
int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx)
unsigned int quint32
Definition qtypes.h:50
unsigned char quint8
Definition qtypes.h:46
qint64 qlonglong
Definition qtypes.h:63
#define ENDCERTSTRING
#define BEGINCERTSTRING
QList< int > list
[14]
QSharedPointer< T > other(t)
[5]
QList< QSslCertificate > cert
[0]
QHostInfo info
[0]