diff options
Diffstat (limited to 'src/network/ssl/qsslcertificate_qt.cpp')
-rw-r--r-- | src/network/ssl/qsslcertificate_qt.cpp | 256 |
1 files changed, 254 insertions, 2 deletions
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp index 391ee6f7f9..6ea78a408b 100644 --- a/src/network/ssl/qsslcertificate_qt.cpp +++ b/src/network/ssl/qsslcertificate_qt.cpp @@ -47,9 +47,17 @@ #include "qsslkey_p.h" #include "qsslcertificateextension.h" #include "qsslcertificateextension_p.h" +#include "qasn1element_p.h" QT_BEGIN_NAMESPACE +enum GeneralNameType +{ + Rfc822NameType = 0x81, + DnsNameType = 0x82, + UniformResourceIdentifierType = 0x86 +}; + bool QSslCertificate::operator==(const QSslCertificate &other) const { if (d == other.d) @@ -150,8 +158,7 @@ QSslKey QSslCertificate::publicKey() const QList<QSslCertificateExtension> QSslCertificate::extensions() const { - Q_UNIMPLEMENTED(); - return QList<QSslCertificateExtension>(); + return d->extensions; } #define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" @@ -263,4 +270,249 @@ QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteAr return certificates; } +static QByteArray colonSeparatedHex(const QByteArray &value) +{ + QByteArray hexString; + hexString.reserve(value.size() * 3); + for (int a = 0; a < value.size(); ++a) { + const quint8 b = value.at(a); + if (b || !hexString.isEmpty()) { // skip leading zeros + hexString += QByteArray::number(b, 16).rightJustified(2, '0'); + hexString += ':'; + } + } + hexString.chop(1); + return hexString; +} + +bool QSslCertificatePrivate::parse(const QByteArray &data) +{ + QAsn1Element root; + + QDataStream dataStream(data); + if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType) + return false; + + QDataStream rootStream(root.value()); + QAsn1Element cert; + if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType) + return false; + + // version or serial number + QAsn1Element elem; + QDataStream certStream(cert.value()); + if (!elem.read(certStream)) + return false; + + if (elem.type() == QAsn1Element::Context0Type) { + QDataStream versionStream(elem.value()); + if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType) + return false; + + versionString = QByteArray::number(elem.value()[0] + 1); + if (!elem.read(certStream)) + return false; + } else { + versionString = QByteArray::number(1); + } + + // serial number + if (elem.type() != QAsn1Element::IntegerType) + return false; + serialNumberString = colonSeparatedHex(elem.value()); + + // algorithm ID + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + // issuer info + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); + issuerInfo = elem.toInfo(); + + // validity period + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + QDataStream validityStream(elem.value()); + if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) + return false; + + notValidBefore = elem.toDateTime(); + if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) + return false; + + notValidAfter = elem.toDateTime(); + + // subject name + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); + subjectInfo = elem.toInfo(); + subjectMatchesIssuer = issuerDer == subjectDer; + + // public key + qint64 keyStart = certStream.device()->pos(); + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + publicKeyDerData.resize(certStream.device()->pos() - keyStart); + QDataStream keyStream(elem.value()); + if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + + // key algorithm + if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType) + return false; + + const QByteArray oid = elem.toObjectId(); + if (oid == RSA_ENCRYPTION_OID) + publicKeyAlgorithm = QSsl::Rsa; + else if (oid == RSA_ENCRYPTION_OID) + publicKeyAlgorithm = QSsl::Dsa; + else + publicKeyAlgorithm = QSsl::Opaque; + + certStream.device()->seek(keyStart); + certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size()); + + // extensions + while (elem.read(certStream)) { + if (elem.type() == QAsn1Element::Context3Type) { + if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) { + QDataStream extStream(elem.value()); + while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) { + QSslCertificateExtension extension; + if (!parseExtension(elem.value(), &extension)) + return false; + extensions << extension; + + if (extension.oid() == QLatin1String("2.5.29.17")) { + // subjectAltName + QAsn1Element sanElem; + if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) { + QDataStream nameStream(sanElem.value()); + QAsn1Element nameElem; + while (nameElem.read(nameStream)) { + if (nameElem.type() == Rfc822NameType) { + subjectAlternativeNames.insert(QSsl::EmailEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size())); + } else if (nameElem.type() == DnsNameType) { + subjectAlternativeNames.insert(QSsl::DnsEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size())); + } + } + } + } + } + } + } + } + + derData = data.left(dataStream.device()->pos()); + null = false; + return true; +} + +bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension) +{ + bool ok; + bool critical = false; + QAsn1Element oidElem, valElem; + + QDataStream seqStream(data); + + // oid + if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType) + return false; + const QByteArray oid = oidElem.toObjectId(); + + // critical and value + if (!valElem.read(seqStream)) + return false; + if (valElem.type() == QAsn1Element::BooleanType) { + critical = valElem.toBool(&ok); + if (!ok || !valElem.read(seqStream)) + return false; + } + if (valElem.type() != QAsn1Element::OctetStringType) + return false; + + // interpret value + QAsn1Element val; + bool supported = true; + QVariant value; + if (oid == QByteArrayLiteral("1.3.6.1.5.5.7.1.1")) { + // authorityInfoAccess + if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) + return false; + QVariantMap result; + foreach (const QAsn1Element &el, val.toVector()) { + QVector<QAsn1Element> items = el.toVector(); + if (items.size() != 2) + return false; + const QString key = QString::fromLatin1(items.at(0).toObjectName()); + switch (items.at(1).type()) { + case Rfc822NameType: + case DnsNameType: + case UniformResourceIdentifierType: + result[key] = QString::fromLatin1(items.at(1).value(), items.at(1).value().size()); + break; + } + } + value = result; + } else if (oid == QByteArrayLiteral("2.5.29.14")) { + // subjectKeyIdentifier + if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType) + return false; + value = colonSeparatedHex(val.value()).toUpper(); + } else if (oid == QByteArrayLiteral("2.5.29.19")) { + // basicConstraints + if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) + return false; + + QVariantMap result; + QVector<QAsn1Element> items = val.toVector(); + if (items.size() > 0) { + result[QStringLiteral("ca")] = items.at(0).toBool(&ok); + if (!ok) + return false; + } else { + result[QStringLiteral("ca")] = false; + } + if (items.size() > 1) { + result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok); + if (!ok) + return false; + } + value = result; + } else if (oid == QByteArrayLiteral("2.5.29.35")) { + // authorityKeyIdentifier + if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType) + return false; + QVariantMap result; + foreach (const QAsn1Element &el, val.toVector()) { + if (el.type() == 0x80) { + result[QStringLiteral("keyid")] = el.value().toHex(); + } else if (el.type() == 0x82) { + result[QStringLiteral("serial")] = colonSeparatedHex(el.value()); + } + } + value = result; + } else { + supported = false; + value = valElem.value(); + } + + extension->d->critical = critical; + extension->d->supported = supported; + extension->d->oid = QString::fromLatin1(oid); + extension->d->name = QString::fromLatin1(oidElem.toObjectName()); + extension->d->value = value; + + return true; +} + QT_END_NAMESPACE |