From c2dc4922153cba3ebd3c276d914ba12c0514e380 Mon Sep 17 00:00:00 2001 From: David Llewellyn-Jones Date: Mon, 26 Aug 2019 12:01:54 +0000 Subject: [PATCH] [systemsettings] Allow certificate model to decode in-memory PEMs. Contributes to JB#40089 The certificate model code allows an X509 certificate (or bundle of certificates) to be loaded from disk so they can be used to the UI (e.g. for displaying certificate details). This change allows PEM data to be provided as a QByteArray, rather than as a file, allowing certificates store in memory to be accessed by the UI as well, without having to generate a temprory file. This change also exposes certificate summary info, providing some addition properties that help when displaying a summary of the certificate. --- src/certificatemodel.cpp | 85 ++++++++++++++++++++++++++++++---------- src/certificatemodel.h | 13 ++++-- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/certificatemodel.cpp b/src/certificatemodel.cpp index 2cbbef1..f568fec 100644 --- a/src/certificatemodel.cpp +++ b/src/certificatemodel.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2016 Jolla Ltd. - * COntact: Matt Vogt + * Copyright (c) 2016 - 2019 Jolla Ltd. + * Copyright (c) 2019 Open Mobile Platform LLC. * * You may use this file under the terms of the BSD license as follows: * @@ -426,21 +426,7 @@ struct PKCS7File if (BIO_read_filename(input, const_cast(filename.constData())) <= 0) { qWarning() << "Unable to open PKCS7 file:" << path; } else { - STACK_OF(X509_INFO) *certificateStack = PEM_X509_INFO_read_bio(input, NULL, NULL, NULL); - if (!certificateStack) { - qWarning() << "Unable to read PKCS7 file:" << path; - } else { - while (sk_X509_INFO_num(certificateStack)) { - X509_INFO *certificateInfo = sk_X509_INFO_shift(certificateStack); - if (certificateInfo->x509 != NULL) { - certs.append(certificateInfo->x509); - certificateInfo->x509 = NULL; - } - X509_INFO_free(certificateInfo); - } - - sk_X509_INFO_free(certificateStack); - } + read_pem_from_bio(input); } BIO_free(input); @@ -448,6 +434,39 @@ struct PKCS7File } } + explicit PKCS7File(const QByteArray &pem) + { + if (!isValid()) { + qWarning() << "Unable to prepare X509 certificates structure"; + } else { + BIO *input = BIO_new_mem_buf(pem.constData(), pem.length()); + if (!input) { + qWarning() << "Unable to allocate new BIO while importing in-memory PEM"; + } else { + read_pem_from_bio(input); + BIO_free(input); + } + } + } + + void read_pem_from_bio(BIO *input) { + STACK_OF(X509_INFO) *certificateStack = PEM_X509_INFO_read_bio(input, NULL, NULL, NULL); + if (!certificateStack) { + qWarning() << "Unable to read PKCS7 data"; + } else { + while (sk_X509_INFO_num(certificateStack)) { + X509_INFO *certificateInfo = sk_X509_INFO_shift(certificateStack); + if (certificateInfo->x509 != NULL) { + certs.append(certificateInfo->x509); + certificateInfo->x509 = NULL; + } + X509_INFO_free(certificateInfo); + } + + sk_X509_INFO_free(certificateStack); + } + } + ~PKCS7File() { } @@ -498,11 +517,17 @@ class LibCrypto static Initializer init; public: - static QList getCertificates(const QString &bundlePath) + template + static QList getCertificates(const T &bundleData) { - QList certificates; + PKCS7File bundle(bundleData); - PKCS7File bundle(bundlePath); + return bundleToCertificates(bundle); + } +private: + static QList bundleToCertificates(PKCS7File &bundle) + { + QList certificates; if (bundle.isValid() && bundle.count() > 0) { certificates.reserve(bundle.count()); bundle.getCertificates().for_each([&certificates](const X509Certificate &cert) { @@ -514,6 +539,7 @@ class LibCrypto } }; + LibCrypto::Initializer LibCrypto::init; const QList > &bundlePaths() @@ -579,9 +605,24 @@ Certificate::Certificate(const X509Certificate &cert) } } + // Matches QSslCertificate::issuerDisplayName() introducd in Qt 5.12 + // Returns a name that describes the issuer. It returns the CommonName if + // available, otherwise falls back to the Organization or the first + // OrganizationalUnitName. + m_issuerDisplayName = cert.issuerElement(NID_commonName); + if (m_issuerDisplayName.isEmpty()) { + m_issuerDisplayName = cert.issuerElement(NID_countryName); + } + if (m_issuerDisplayName.isEmpty()) { + m_issuerDisplayName = cert.issuerElement(NID_organizationName); + } + // Populate the details map m_details.insert(QStringLiteral("Version"), QVariant(cert.version())); m_details.insert(QStringLiteral("SerialNumber"), QVariant(cert.serialNumber())); + m_details.insert(QStringLiteral("SubjectDisplayName"), QVariant(m_primaryName)); + m_details.insert(QStringLiteral("OrganizationName"), QVariant(m_organizationName)); + m_details.insert(QStringLiteral("IssuerDisplayName"), QVariant(m_issuerDisplayName)); QVariantMap validity; validity.insert(QStringLiteral("NotBefore"), QVariant(cert.notBefore())); @@ -753,3 +794,7 @@ QList CertificateModel::getCertificates(const QString &bundlePath) return LibCrypto::getCertificates(bundlePath); } +QList CertificateModel::getCertificates(const QByteArray &pem) +{ + return LibCrypto::getCertificates(pem); +} diff --git a/src/certificatemodel.h b/src/certificatemodel.h index b6cad18..8573ac4 100644 --- a/src/certificatemodel.h +++ b/src/certificatemodel.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2016 Jolla Ltd. - * Contact: Matt Vogt + * Copyright (c) 2016 - 2019 Jolla Ltd. + * Copyright (c) 2019 Open Mobile Platform LLC. * * You may use this file under the terms of the BSD license as follows: * @@ -38,7 +38,7 @@ #include #include -#include +#include "systemsettingsglobal.h" struct X509Certificate; @@ -60,6 +60,8 @@ class SYSTEMSETTINGS_EXPORT Certificate QVariantMap details() const { return m_details; } + QString issuerDisplayName() const { return m_issuerDisplayName; } + private: QString m_commonName; QString m_countryName; @@ -71,6 +73,8 @@ class SYSTEMSETTINGS_EXPORT Certificate QDateTime m_notValidBefore; QDateTime m_notValidAfter; + QString m_issuerDisplayName; + QVariantMap m_details; }; @@ -115,8 +119,9 @@ class SYSTEMSETTINGS_EXPORT CertificateModel: public QAbstractListModel virtual QVariant data(const QModelIndex &index, int role) const; static QList getCertificates(const QString &bundlePath); + static QList getCertificates(const QByteArray &pem); -signals: +Q_SIGNALS: void bundleTypeChanged(); void bundlePathChanged();