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 
56 QT_BEGIN_NAMESPACE
57 
operator ==(const QSslCertificate & other) const58 bool 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 
qHash(const QSslCertificate & key,uint seed)67 uint qHash(const QSslCertificate &key, uint 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 
isNull() const73 bool QSslCertificate::isNull() const
74 {
75     return d->null;
76 }
77 
isSelfSigned() const78 bool 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 
version() const89 QByteArray QSslCertificate::version() const
90 {
91     return d->versionString;
92 }
93 
serialNumber() const94 QByteArray QSslCertificate::serialNumber() const
95 {
96     return d->serialNumberString;
97 }
98 
issuerInfo(SubjectInfo info) const99 QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
100 {
101     return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info));
102 }
103 
issuerInfo(const QByteArray & attribute) const104 QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
105 {
106     return d->issuerInfo.values(attribute);
107 }
108 
subjectInfo(SubjectInfo info) const109 QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
110 {
111     return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info));
112 }
113 
subjectInfo(const QByteArray & attribute) const114 QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
115 {
116     return d->subjectInfo.values(attribute);
117 }
118 
subjectInfoAttributes() const119 QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
120 {
121     return d->subjectInfo.uniqueKeys();
122 }
123 
issuerInfoAttributes() const124 QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
125 {
126     return d->issuerInfo.uniqueKeys();
127 }
128 
subjectAlternativeNames() const129 QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
130 {
131     return d->subjectAlternativeNames;
132 }
133 
effectiveDate() const134 QDateTime QSslCertificate::effectiveDate() const
135 {
136     return d->notValidBefore;
137 }
138 
expiryDate() const139 QDateTime QSslCertificate::expiryDate() const
140 {
141     return d->notValidAfter;
142 }
143 
144 #if !defined(Q_OS_WINRT) && !QT_CONFIG(schannel) // implemented in qsslcertificate_{winrt,schannel}.cpp
handle() const145 Qt::HANDLE QSslCertificate::handle() const
146 {
147     Q_UNIMPLEMENTED();
148     return nullptr;
149 }
150 #endif
151 
152 #ifndef QT_NO_SSL
publicKey() const153 QSslKey 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 
extensions() const165 QList<QSslCertificateExtension> QSslCertificate::extensions() const
166 {
167     return d->extensions;
168 }
169 
170 #define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
171 #define ENDCERTSTRING "-----END CERTIFICATE-----"
172 
toPem() const173 QByteArray 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 
toDer() const192 QByteArray QSslCertificate::toDer() const
193 {
194     return d->derData;
195 }
196 
toText() const197 QString QSslCertificate::toText() const
198 {
199     Q_UNIMPLEMENTED();
200     return QString();
201 }
202 
init(const QByteArray & data,QSsl::EncodingFormat format)203 void 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 
matchLineFeed(const QByteArray & pem,int * offset)219 static 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 
certificatesFromPem(const QByteArray & pem,int count)238 QList<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 
certificatesFromDer(const QByteArray & der,int count)266 QList<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 
colonSeparatedHex(const QByteArray & value)283 static 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 
parse(const QByteArray & data)293 bool 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)
315             || elem.type() != QAsn1Element::IntegerType
316             || elem.value().isEmpty())
317             return false;
318 
319         versionString = QByteArray::number(elem.value().at(0) + 1);
320         if (!elem.read(certStream))
321             return false;
322     } else {
323         versionString = QByteArray::number(1);
324     }
325 
326     // serial number
327     if (elem.type() != QAsn1Element::IntegerType)
328         return false;
329     serialNumberString = colonSeparatedHex(elem.value());
330 
331     // algorithm ID
332     if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
333         return false;
334 
335     // issuer info
336     if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
337         return false;
338 
339     QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
340     issuerInfo = elem.toInfo();
341 
342     // validity period
343     if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
344         return false;
345 
346     QDataStream validityStream(elem.value());
347     if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
348         return false;
349 
350     notValidBefore = elem.toDateTime();
351     if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
352         return false;
353 
354     notValidAfter = elem.toDateTime();
355 
356     // subject name
357     if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
358         return false;
359 
360     QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
361     subjectInfo = elem.toInfo();
362     subjectMatchesIssuer = issuerDer == subjectDer;
363 
364     // public key
365     qint64 keyStart = certStream.device()->pos();
366     if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
367         return false;
368 
369     publicKeyDerData.resize(certStream.device()->pos() - keyStart);
370     QDataStream keyStream(elem.value());
371     if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
372         return false;
373 
374 
375     // key algorithm
376     if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType)
377         return false;
378 
379     const QByteArray oid = elem.toObjectId();
380     if (oid == RSA_ENCRYPTION_OID)
381         publicKeyAlgorithm = QSsl::Rsa;
382     else if (oid == DSA_ENCRYPTION_OID)
383         publicKeyAlgorithm = QSsl::Dsa;
384     else if (oid == EC_ENCRYPTION_OID)
385         publicKeyAlgorithm = QSsl::Ec;
386     else
387         publicKeyAlgorithm = QSsl::Opaque;
388 
389     certStream.device()->seek(keyStart);
390     certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size());
391 
392     // extensions
393     while (elem.read(certStream)) {
394         if (elem.type() == QAsn1Element::Context3Type) {
395             if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) {
396                 QDataStream extStream(elem.value());
397                 while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) {
398                     QSslCertificateExtension extension;
399                     if (!parseExtension(elem.value(), &extension))
400                         return false;
401                     extensions << extension;
402 
403                     if (extension.oid() == QLatin1String("2.5.29.17")) {
404                         // subjectAltName
405                         QAsn1Element sanElem;
406                         if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) {
407                             QDataStream nameStream(sanElem.value());
408                             QAsn1Element nameElem;
409                             while (nameElem.read(nameStream)) {
410                                 switch (nameElem.type()) {
411                                 case QAsn1Element::Rfc822NameType:
412                                     subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString());
413                                     break;
414                                 case QAsn1Element::DnsNameType:
415                                     subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString());
416                                     break;
417                                 case QAsn1Element::IpAddressType: {
418                                     QHostAddress ipAddress;
419                                     QByteArray ipAddrValue = nameElem.value();
420                                     switch (ipAddrValue.length()) {
421                                     case 4: // IPv4
422                                         ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data())));
423                                         break;
424                                     case 16: // IPv6
425                                         ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data()));
426                                         break;
427                                     default: // Unknown IP address format
428                                         break;
429                                     }
430                                     if (!ipAddress.isNull())
431                                         subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString());
432                                     break;
433                                 }
434                                 default:
435                                     break;
436                                 }
437                             }
438                         }
439                     }
440                 }
441             }
442         }
443     }
444 
445     derData = data.left(dataStream.device()->pos());
446     null = false;
447     return true;
448 }
449 
parseExtension(const QByteArray & data,QSslCertificateExtension * extension)450 bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension)
451 {
452     bool ok;
453     bool critical = false;
454     QAsn1Element oidElem, valElem;
455 
456     QDataStream seqStream(data);
457 
458     // oid
459     if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType)
460         return false;
461     const QByteArray oid = oidElem.toObjectId();
462 
463     // critical and value
464     if (!valElem.read(seqStream))
465         return false;
466     if (valElem.type() == QAsn1Element::BooleanType) {
467         critical = valElem.toBool(&ok);
468         if (!ok || !valElem.read(seqStream))
469             return false;
470     }
471     if (valElem.type() != QAsn1Element::OctetStringType)
472         return false;
473 
474     // interpret value
475     QAsn1Element val;
476     bool supported = true;
477     QVariant value;
478     if (oid == "1.3.6.1.5.5.7.1.1") {
479         // authorityInfoAccess
480         if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
481             return false;
482         QVariantMap result;
483         const auto elems = val.toVector();
484         for (const QAsn1Element &el : elems) {
485             QVector<QAsn1Element> items = el.toVector();
486             if (items.size() != 2)
487                 return false;
488             const QString key = QString::fromLatin1(items.at(0).toObjectName());
489             switch (items.at(1).type()) {
490             case QAsn1Element::Rfc822NameType:
491             case QAsn1Element::DnsNameType:
492             case QAsn1Element::UniformResourceIdentifierType:
493                 result[key] = items.at(1).toString();
494                 break;
495             }
496         }
497         value = result;
498     } else if (oid == "2.5.29.14") {
499         // subjectKeyIdentifier
500         if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType)
501             return false;
502         value = colonSeparatedHex(val.value()).toUpper();
503     } else if (oid == "2.5.29.19") {
504         // basicConstraints
505         if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
506             return false;
507 
508         QVariantMap result;
509         QVector<QAsn1Element> items = val.toVector();
510         if (items.size() > 0) {
511             result[QStringLiteral("ca")] = items.at(0).toBool(&ok);
512             if (!ok)
513                 return false;
514         } else {
515             result[QStringLiteral("ca")] = false;
516         }
517         if (items.size() > 1) {
518             result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok);
519             if (!ok)
520                 return false;
521         }
522         value = result;
523     } else if (oid == "2.5.29.35") {
524         // authorityKeyIdentifier
525         if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
526             return false;
527         QVariantMap result;
528         const auto elems = val.toVector();
529         for (const QAsn1Element &el : elems) {
530             if (el.type() == 0x80) {
531                 const QString key = QStringLiteral("keyid");
532                 result[key] = el.value().toHex();
533             } else if (el.type() == 0x82) {
534                 const QString serial = QStringLiteral("serial");
535                 result[serial] = colonSeparatedHex(el.value());
536             }
537         }
538         value = result;
539     } else {
540         supported = false;
541         value = valElem.value();
542     }
543 
544     extension->d->critical = critical;
545     extension->d->supported = supported;
546     extension->d->oid = QString::fromLatin1(oid);
547     extension->d->name = QString::fromLatin1(oidElem.toObjectName());
548     extension->d->value = value;
549 
550     return true;
551 }
552 
553 QT_END_NAMESPACE
554