1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsslcertificate.h"
41#include "qsslcertificate_p.h"
42
43#include "qssl_p.h"
44#ifndef QT_NO_SSL
45#include "qsslkey.h"
46#include "qsslkey_p.h"
47#endif
48#include "qsslcertificateextension.h"
49#include "qsslcertificateextension_p.h"
50#include "qasn1element_p.h"
51
52#include <QtCore/qdatastream.h>
53#include <QtCore/qendian.h>
54#include <QtNetwork/qhostaddress.h>
55
56QT_BEGIN_NAMESPACE
57
58bool QSslCertificate::operator==(const QSslCertificate &other) const
59{
60 if (d == other.d)
61 return true;
62 if (d->null && other.d->null)
63 return true;
64 return d->derData == other.d->derData;
65}
66
67size_t qHash(const QSslCertificate &key, size_t seed) noexcept
68{
69 // DER is the native encoding here, so toDer() is just "return d->derData":
70 return qHash(key.toDer(), seed);
71}
72
73bool QSslCertificate::isNull() const
74{
75 return d->null;
76}
77
78bool QSslCertificate::isSelfSigned() const
79{
80 if (d->null)
81 return false;
82
83 qCWarning(lcSsl,
84 "QSslCertificate::isSelfSigned: This function does not check, whether the certificate "
85 "is actually signed. It just checks whether issuer and subject are identical");
86 return d->subjectMatchesIssuer;
87}
88
89QByteArray QSslCertificate::version() const
90{
91 return d->versionString;
92}
93
94QByteArray QSslCertificate::serialNumber() const
95{
96 return d->serialNumberString;
97}
98
99QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
100{
101 return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info));
102}
103
104QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
105{
106 return d->issuerInfo.values(attribute);
107}
108
109QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
110{
111 return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info));
112}
113
114QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
115{
116 return d->subjectInfo.values(attribute);
117}
118
119QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
120{
121 return d->subjectInfo.uniqueKeys();
122}
123
124QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
125{
126 return d->issuerInfo.uniqueKeys();
127}
128
129QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
130{
131 return d->subjectAlternativeNames;
132}
133
134QDateTime QSslCertificate::effectiveDate() const
135{
136 return d->notValidBefore;
137}
138
139QDateTime QSslCertificate::expiryDate() const
140{
141 return d->notValidAfter;
142}
143
144#if !QT_CONFIG(schannel) // implemented in qsslcertificate_schannel.cpp
145Qt::HANDLE QSslCertificate::handle() const
146{
147 Q_UNIMPLEMENTED();
148 return nullptr;
149}
150#endif
151
152#ifndef QT_NO_SSL
153QSslKey QSslCertificate::publicKey() const
154{
155 QSslKey key;
156 key.d->type = QSsl::PublicKey;
157 if (d->publicKeyAlgorithm != QSsl::Opaque) {
158 key.d->algorithm = d->publicKeyAlgorithm;
159 key.d->decodeDer(d->publicKeyDerData);
160 }
161 return key;
162}
163#endif
164
165QList<QSslCertificateExtension> QSslCertificate::extensions() const
166{
167 return d->extensions;
168}
169
170#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
171#define ENDCERTSTRING "-----END CERTIFICATE-----"
172
173QByteArray QSslCertificate::toPem() const
174{
175 QByteArray array = toDer();
176
177 // Convert to Base64 - wrap at 64 characters.
178 array = array.toBase64();
179 QByteArray tmp;
180 for (int i = 0; i <= array.size() - 64; i += 64) {
181 tmp += QByteArray::fromRawData(array.data() + i, 64);
182 tmp += '\n';
183 }
184 if (int remainder = array.size() % 64) {
185 tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
186 tmp += '\n';
187 }
188
189 return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
190}
191
192QByteArray QSslCertificate::toDer() const
193{
194 return d->derData;
195}
196
197QString QSslCertificate::toText() const
198{
199 Q_UNIMPLEMENTED();
200 return QString();
201}
202
203void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
204{
205 if (!data.isEmpty()) {
206 const QList<QSslCertificate> certs = (format == QSsl::Pem)
207 ? certificatesFromPem(data, 1)
208 : certificatesFromDer(data, 1);
209 if (!certs.isEmpty()) {
210 *this = *certs.first().d;
211#if QT_CONFIG(schannel)
212 if (certificateContext)
213 certificateContext = CertDuplicateCertificateContext(certificateContext);
214#endif
215 }
216 }
217}
218
219static bool matchLineFeed(const QByteArray &pem, int *offset)
220{
221 char ch = 0;
222
223 // ignore extra whitespace at the end of the line
224 while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
225 ++*offset;
226
227 if (ch == '\n') {
228 *offset += 1;
229 return true;
230 }
231 if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
232 *offset += 2;
233 return true;
234 }
235 return false;
236}
237
238QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
239{
240 QList<QSslCertificate> certificates;
241 int offset = 0;
242 while (count == -1 || certificates.size() < count) {
243 int startPos = pem.indexOf(BEGINCERTSTRING, offset);
244 if (startPos == -1)
245 break;
246 startPos += sizeof(BEGINCERTSTRING) - 1;
247 if (!matchLineFeed(pem, &startPos))
248 break;
249
250 int endPos = pem.indexOf(ENDCERTSTRING, startPos);
251 if (endPos == -1)
252 break;
253
254 offset = endPos + sizeof(ENDCERTSTRING) - 1;
255 if (offset < pem.size() && !matchLineFeed(pem, &offset))
256 break;
257
258 QByteArray decoded = QByteArray::fromBase64(
259 QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
260 certificates << certificatesFromDer(decoded, 1);;
261 }
262
263 return certificates;
264}
265
266QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
267{
268 QList<QSslCertificate> certificates;
269
270 QByteArray data = der;
271 while (count == -1 || certificates.size() < count) {
272 QSslCertificate cert;
273 if (!cert.d->parse(data))
274 break;
275
276 certificates << cert;
277 data.remove(0, cert.d->derData.size());
278 }
279
280 return certificates;
281}
282
283static QByteArray colonSeparatedHex(const QByteArray &value)
284{
285 const int size = value.size();
286 int i = 0;
287 while (i < size && !value.at(i)) // skip leading zeros
288 ++i;
289
290 return value.mid(i).toHex(':');
291}
292
293bool QSslCertificatePrivate::parse(const QByteArray &data)
294{
295 QAsn1Element root;
296
297 QDataStream dataStream(data);
298 if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType)
299 return false;
300
301 QDataStream rootStream(root.value());
302 QAsn1Element cert;
303 if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType)
304 return false;
305
306 // version or serial number
307 QAsn1Element elem;
308 QDataStream certStream(cert.value());
309 if (!elem.read(certStream))
310 return false;
311
312 if (elem.type() == QAsn1Element::Context0Type) {
313 QDataStream versionStream(elem.value());
314 if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType)
315 return false;
316
317 versionString = QByteArray::number(elem.value().at(0) + 1);
318 if (!elem.read(certStream))
319 return false;
320 } else {
321 versionString = QByteArray::number(1);
322 }
323
324 // serial number
325 if (elem.type() != QAsn1Element::IntegerType)
326 return false;
327 serialNumberString = colonSeparatedHex(elem.value());
328
329 // algorithm ID
330 if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
331 return false;
332
333 // issuer info
334 if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
335 return false;
336
337 QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
338 issuerInfo = elem.toInfo();
339
340 // validity period
341 if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
342 return false;
343
344 QDataStream validityStream(elem.value());
345 if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
346 return false;
347
348 notValidBefore = elem.toDateTime();
349 if (!notValidBefore.isValid())
350 return false;
351
352 if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
353 return false;
354
355 notValidAfter = elem.toDateTime();
356 if (!notValidAfter.isValid())
357 return false;
358
359
360 // subject name
361 if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
362 return false;
363
364 QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
365 subjectInfo = elem.toInfo();
366 subjectMatchesIssuer = issuerDer == subjectDer;
367
368 // public key
369 qint64 keyStart = certStream.device()->pos();
370 if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
371 return false;
372
373 publicKeyDerData.resize(certStream.device()->pos() - keyStart);
374 QDataStream keyStream(elem.value());
375 if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
376 return false;
377
378
379 // key algorithm
380 if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType)
381 return false;
382
383 const QByteArray oid = elem.toObjectId();
384 if (oid == RSA_ENCRYPTION_OID)
385 publicKeyAlgorithm = QSsl::Rsa;
386 else if (oid == DSA_ENCRYPTION_OID)
387 publicKeyAlgorithm = QSsl::Dsa;
388 else if (oid == EC_ENCRYPTION_OID)
389 publicKeyAlgorithm = QSsl::Ec;
390 else
391 publicKeyAlgorithm = QSsl::Opaque;
392
393 certStream.device()->seek(keyStart);
394 certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size());
395
396 // extensions
397 while (elem.read(certStream)) {
398 if (elem.type() == QAsn1Element::Context3Type) {
399 if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) {
400 QDataStream extStream(elem.value());
401 while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) {
402 QSslCertificateExtension extension;
403 if (!parseExtension(elem.value(), &extension))
404 return false;
405
406 if (extension.oid() == QLatin1String("2.5.29.17")) {
407 // subjectAltName
408
409 // Note, parseExtension() returns true for this extensions,
410 // but considers it to be unsupported and assignes a useless
411 // value. OpenSSL also treats this extension as unsupported,
412 // but properly creates a map with 'name' and 'value' taken
413 // from the extension. We only support 'email', 'IP' and 'DNS',
414 // but this is what our subjectAlternativeNames map can contain
415 // anyway.
416 QVariantMap extValue;
417 QAsn1Element sanElem;
418 if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) {
419 QDataStream nameStream(sanElem.value());
420 QAsn1Element nameElem;
421 while (nameElem.read(nameStream)) {
422 switch (nameElem.type()) {
423 case QAsn1Element::Rfc822NameType:
424 subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString());
425 extValue[QStringLiteral("email")] = nameElem.toString();
426 break;
427 case QAsn1Element::DnsNameType:
428 subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString());
429 extValue[QStringLiteral("DNS")] = nameElem.toString();
430 break;
431 case QAsn1Element::IpAddressType: {
432 QHostAddress ipAddress;
433 QByteArray ipAddrValue = nameElem.value();
434 switch (ipAddrValue.length()) {
435 case 4: // IPv4
436 ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data())));
437 break;
438 case 16: // IPv6
439 ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data()));
440 break;
441 default: // Unknown IP address format
442 break;
443 }
444 if (!ipAddress.isNull()) {
445 subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString());
446 extValue[QStringLiteral("IP")] = ipAddress.toString();
447 }
448 break;
449 }
450 default:
451 break;
452 }
453 }
454 extension.d->value = extValue;
455 extension.d->supported = true;
456 }
457 }
458
459 extensions << extension;
460 }
461 }
462 }
463 }
464
465 derData = data.left(dataStream.device()->pos());
466 null = false;
467 return true;
468}
469
470bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension)
471{
472 bool ok;
473 bool critical = false;
474 QAsn1Element oidElem, valElem;
475
476 QDataStream seqStream(data);
477
478 // oid
479 if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType)
480 return false;
481 const QByteArray oid = oidElem.toObjectId();
482
483 // critical and value
484 if (!valElem.read(seqStream))
485 return false;
486 if (valElem.type() == QAsn1Element::BooleanType) {
487 critical = valElem.toBool(&ok);
488 if (!ok || !valElem.read(seqStream))
489 return false;
490 }
491 if (valElem.type() != QAsn1Element::OctetStringType)
492 return false;
493
494 // interpret value
495 QAsn1Element val;
496 bool supported = true;
497 QVariant value;
498 if (oid == "1.3.6.1.5.5.7.1.1") {
499 // authorityInfoAccess
500 if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
501 return false;
502 QVariantMap result;
503 const auto elems = val.toList();
504 for (const QAsn1Element &el : elems) {
505 const auto items = el.toList();
506 if (items.size() != 2)
507 return false;
508 const QString key = QString::fromLatin1(items.at(0).toObjectName());
509 switch (items.at(1).type()) {
510 case QAsn1Element::Rfc822NameType:
511 case QAsn1Element::DnsNameType:
512 case QAsn1Element::UniformResourceIdentifierType:
513 result[key] = items.at(1).toString();
514 break;
515 }
516 }
517 value = result;
518 } else if (oid == "2.5.29.14") {
519 // subjectKeyIdentifier
520 if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType)
521 return false;
522 value = colonSeparatedHex(val.value()).toUpper();
523 } else if (oid == "2.5.29.19") {
524 // basicConstraints
525 if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
526 return false;
527
528 QVariantMap result;
529 const auto items = val.toList();
530 if (items.size() > 0) {
531 result[QStringLiteral("ca")] = items.at(0).toBool(&ok);
532 if (!ok)
533 return false;
534 } else {
535 result[QStringLiteral("ca")] = false;
536 }
537 if (items.size() > 1) {
538 result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok);
539 if (!ok)
540 return false;
541 }
542 value = result;
543 } else if (oid == "2.5.29.35") {
544 // authorityKeyIdentifier
545 if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
546 return false;
547 QVariantMap result;
548 const auto elems = val.toList();
549 for (const QAsn1Element &el : elems) {
550 if (el.type() == 0x80) {
551 const QString key = QStringLiteral("keyid");
552 result[key] = el.value().toHex();
553 } else if (el.type() == 0x82) {
554 const QString serial = QStringLiteral("serial");
555 result[serial] = colonSeparatedHex(el.value());
556 }
557 }
558 value = result;
559 } else {
560 supported = false;
561 value = valElem.value();
562 }
563
564 extension->d->critical = critical;
565 extension->d->supported = supported;
566 extension->d->oid = QString::fromLatin1(oid);
567 extension->d->name = QString::fromLatin1(oidElem.toObjectName());
568 extension->d->value = value;
569
570 return true;
571}
572
573QT_END_NAMESPACE
574