Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[systemsettings] Allow certificate model to decode in-memory PEMs. Co…
…ntributes 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.
  • Loading branch information
llewelld committed Sep 20, 2019
1 parent 4bbaa50 commit c2dc492
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 15 deletions.
67 changes: 56 additions & 11 deletions src/certificatemodel.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 Jolla Ltd.
* COntact: Matt Vogt <matthew.vogt@jollamobile.com>
* 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:
*
Expand Down Expand Up @@ -426,9 +426,33 @@ struct PKCS7File
if (BIO_read_filename(input, const_cast<char *>(filename.constData())) <= 0) {
qWarning() << "Unable to open PKCS7 file:" << path;
} else {
read_pem_from_bio(input);
}

BIO_free(input);
}
}
}

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 file:" << path;
qWarning() << "Unable to read PKCS7 data";
} else {
while (sk_X509_INFO_num(certificateStack)) {
X509_INFO *certificateInfo = sk_X509_INFO_shift(certificateStack);
Expand All @@ -443,11 +467,6 @@ struct PKCS7File
}
}

BIO_free(input);
}
}
}

~PKCS7File()
{
}
Expand Down Expand Up @@ -498,11 +517,17 @@ class LibCrypto
static Initializer init;

public:
static QList<Certificate> getCertificates(const QString &bundlePath)
template<class T>
static QList<Certificate> getCertificates(const T &bundleData)
{
QList<Certificate> certificates;
PKCS7File bundle(bundleData);

PKCS7File bundle(bundlePath);
return bundleToCertificates(bundle);
}
private:
static QList<Certificate> bundleToCertificates(PKCS7File &bundle)
{
QList<Certificate> certificates;
if (bundle.isValid() && bundle.count() > 0) {
certificates.reserve(bundle.count());
bundle.getCertificates().for_each([&certificates](const X509Certificate &cert) {
Expand All @@ -514,6 +539,7 @@ class LibCrypto
}
};


LibCrypto::Initializer LibCrypto::init;

const QList<QPair<QString, CertificateModel::BundleType> > &bundlePaths()
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -753,3 +794,7 @@ QList<Certificate> CertificateModel::getCertificates(const QString &bundlePath)
return LibCrypto::getCertificates(bundlePath);
}

QList<Certificate> CertificateModel::getCertificates(const QByteArray &pem)
{
return LibCrypto::getCertificates(pem);
}
13 changes: 9 additions & 4 deletions src/certificatemodel.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 Jolla Ltd.
* Contact: Matt Vogt <matthew.vogt@jollamobile.com>
* 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:
*
Expand Down Expand Up @@ -38,7 +38,7 @@
#include <QList>
#include <QVariantMap>

#include <systemsettingsglobal.h>
#include "systemsettingsglobal.h"


struct X509Certificate;
Expand All @@ -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;
Expand All @@ -71,6 +73,8 @@ class SYSTEMSETTINGS_EXPORT Certificate
QDateTime m_notValidBefore;
QDateTime m_notValidAfter;

QString m_issuerDisplayName;

QVariantMap m_details;
};

Expand Down Expand Up @@ -115,8 +119,9 @@ class SYSTEMSETTINGS_EXPORT CertificateModel: public QAbstractListModel
virtual QVariant data(const QModelIndex &index, int role) const;

static QList<Certificate> getCertificates(const QString &bundlePath);
static QList<Certificate> getCertificates(const QByteArray &pem);

signals:
Q_SIGNALS:
void bundleTypeChanged();
void bundlePathChanged();

Expand Down

0 comments on commit c2dc492

Please sign in to comment.