Commit c2dc4922 authored by flypig's avatar flypig

[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.
parent 4bbaa500
/* /*
* Copyright (C) 2016 Jolla Ltd. * Copyright (c) 2016 - 2019 Jolla Ltd.
* COntact: Matt Vogt <matthew.vogt@jollamobile.com> * Copyright (c) 2019 Open Mobile Platform LLC.
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
* *
...@@ -426,21 +426,7 @@ struct PKCS7File ...@@ -426,21 +426,7 @@ struct PKCS7File
if (BIO_read_filename(input, const_cast<char *>(filename.constData())) <= 0) { if (BIO_read_filename(input, const_cast<char *>(filename.constData())) <= 0) {
qWarning() << "Unable to open PKCS7 file:" << path; qWarning() << "Unable to open PKCS7 file:" << path;
} else { } else {
STACK_OF(X509_INFO) *certificateStack = PEM_X509_INFO_read_bio(input, NULL, NULL, NULL); read_pem_from_bio(input);
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);
}
} }
BIO_free(input); BIO_free(input);
...@@ -448,6 +434,39 @@ struct PKCS7File ...@@ -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() ~PKCS7File()
{ {
} }
...@@ -498,11 +517,17 @@ class LibCrypto ...@@ -498,11 +517,17 @@ class LibCrypto
static Initializer init; static Initializer init;
public: 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) { if (bundle.isValid() && bundle.count() > 0) {
certificates.reserve(bundle.count()); certificates.reserve(bundle.count());
bundle.getCertificates().for_each([&certificates](const X509Certificate &cert) { bundle.getCertificates().for_each([&certificates](const X509Certificate &cert) {
...@@ -514,6 +539,7 @@ public: ...@@ -514,6 +539,7 @@ public:
} }
}; };
LibCrypto::Initializer LibCrypto::init; LibCrypto::Initializer LibCrypto::init;
const QList<QPair<QString, CertificateModel::BundleType> > &bundlePaths() const QList<QPair<QString, CertificateModel::BundleType> > &bundlePaths()
...@@ -579,9 +605,24 @@ Certificate::Certificate(const X509Certificate &cert) ...@@ -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 // Populate the details map
m_details.insert(QStringLiteral("Version"), QVariant(cert.version())); m_details.insert(QStringLiteral("Version"), QVariant(cert.version()));
m_details.insert(QStringLiteral("SerialNumber"), QVariant(cert.serialNumber())); 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; QVariantMap validity;
validity.insert(QStringLiteral("NotBefore"), QVariant(cert.notBefore())); validity.insert(QStringLiteral("NotBefore"), QVariant(cert.notBefore()));
...@@ -753,3 +794,7 @@ QList<Certificate> CertificateModel::getCertificates(const QString &bundlePath) ...@@ -753,3 +794,7 @@ QList<Certificate> CertificateModel::getCertificates(const QString &bundlePath)
return LibCrypto::getCertificates(bundlePath); return LibCrypto::getCertificates(bundlePath);
} }
QList<Certificate> CertificateModel::getCertificates(const QByteArray &pem)
{
return LibCrypto::getCertificates(pem);
}
/* /*
* Copyright (C) 2016 Jolla Ltd. * Copyright (c) 2016 - 2019 Jolla Ltd.
* Contact: Matt Vogt <matthew.vogt@jollamobile.com> * Copyright (c) 2019 Open Mobile Platform LLC.
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
* *
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
#include <QList> #include <QList>
#include <QVariantMap> #include <QVariantMap>
#include <systemsettingsglobal.h> #include "systemsettingsglobal.h"
struct X509Certificate; struct X509Certificate;
...@@ -60,6 +60,8 @@ public: ...@@ -60,6 +60,8 @@ public:
QVariantMap details() const { return m_details; } QVariantMap details() const { return m_details; }
QString issuerDisplayName() const { return m_issuerDisplayName; }
private: private:
QString m_commonName; QString m_commonName;
QString m_countryName; QString m_countryName;
...@@ -71,6 +73,8 @@ private: ...@@ -71,6 +73,8 @@ private:
QDateTime m_notValidBefore; QDateTime m_notValidBefore;
QDateTime m_notValidAfter; QDateTime m_notValidAfter;
QString m_issuerDisplayName;
QVariantMap m_details; QVariantMap m_details;
}; };
...@@ -115,8 +119,9 @@ public: ...@@ -115,8 +119,9 @@ public:
virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant data(const QModelIndex &index, int role) const;
static QList<Certificate> getCertificates(const QString &bundlePath); static QList<Certificate> getCertificates(const QString &bundlePath);
static QList<Certificate> getCertificates(const QByteArray &pem);
signals: Q_SIGNALS:
void bundleTypeChanged(); void bundleTypeChanged();
void bundlePathChanged(); void bundlePathChanged();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment