File indexing completed on 2024-04-28 15:51:42

0001 /*
0002     SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "certificateviewer.h"
0008 
0009 #include "gui/certificatemodel.h"
0010 
0011 #include <KColumnResizer>
0012 #include <KLocalizedString>
0013 #include <KMessageBox>
0014 
0015 #include <QCryptographicHash>
0016 #include <QDebug>
0017 #include <QDialogButtonBox>
0018 #include <QFileDialog>
0019 #include <QFormLayout>
0020 #include <QGroupBox>
0021 #include <QLabel>
0022 #include <QPushButton>
0023 #include <QTextEdit>
0024 #include <QTreeView>
0025 #include <QVBoxLayout>
0026 
0027 #include "DistinguishedNameParser.h"
0028 #include "gui/signatureguiutils.h"
0029 
0030 QString splitDNAttributes(const QString &input)
0031 {
0032     auto parsed = DN::parseString(input.toStdString());
0033     QStringList result;
0034     for (auto &&[key, value] : parsed) {
0035         result.push_back(QString::fromStdString(key) + QLatin1Char('=') + QString::fromStdString(value));
0036     }
0037     return result.join(QStringLiteral("\n"));
0038 }
0039 
0040 CertificateViewer::CertificateViewer(const Okular::CertificateInfo &certInfo, QWidget *parent)
0041     : KPageDialog(parent)
0042     , m_certificateInfo(certInfo)
0043 {
0044     setModal(true);
0045     setMinimumSize(QSize(500, 500));
0046     setFaceType(Tabbed);
0047     setWindowTitle(i18n("Certificate Viewer"));
0048     setStandardButtons(QDialogButtonBox::Close);
0049 
0050     auto exportBtn = new QPushButton(i18n("Export..."));
0051     connect(exportBtn, &QPushButton::clicked, this, &CertificateViewer::exportCertificate);
0052     addActionButton(exportBtn);
0053 
0054     // General tab
0055     auto generalPage = new QFrame(this);
0056     addPage(generalPage, i18n("General"));
0057 
0058     auto issuerBox = new QGroupBox(i18n("Issued By"), generalPage);
0059     auto issuerFormLayout = new QFormLayout(issuerBox);
0060     issuerFormLayout->setLabelAlignment(Qt::AlignLeft);
0061     issuerFormLayout->addRow(i18n("Common Name(CN)"), new QLabel(m_certificateInfo.issuerInfo(Okular::CertificateInfo::CommonName, Okular::CertificateInfo::EmptyString::TranslatedNotAvailable)));
0062     issuerFormLayout->addRow(i18n("EMail"), new QLabel(m_certificateInfo.issuerInfo(Okular::CertificateInfo::EmailAddress, Okular::CertificateInfo::EmptyString::TranslatedNotAvailable)));
0063     issuerFormLayout->addRow(i18n("Organization(O)"), new QLabel(m_certificateInfo.issuerInfo(Okular::CertificateInfo::Organization, Okular::CertificateInfo::EmptyString::TranslatedNotAvailable)));
0064 
0065     auto subjectBox = new QGroupBox(i18n("Issued To"), generalPage);
0066     auto subjectFormLayout = new QFormLayout(subjectBox);
0067     subjectFormLayout->setLabelAlignment(Qt::AlignLeft);
0068     subjectFormLayout->addRow(i18n("Common Name(CN)"), new QLabel(m_certificateInfo.subjectInfo(Okular::CertificateInfo::CommonName, Okular::CertificateInfo::EmptyString::TranslatedNotAvailable)));
0069     subjectFormLayout->addRow(i18n("EMail"), new QLabel(m_certificateInfo.subjectInfo(Okular::CertificateInfo::EmailAddress, Okular::CertificateInfo::EmptyString::TranslatedNotAvailable)));
0070     subjectFormLayout->addRow(i18n("Organization(O)"), new QLabel(m_certificateInfo.subjectInfo(Okular::CertificateInfo::Organization, Okular::CertificateInfo::EmptyString::TranslatedNotAvailable)));
0071 
0072     auto validityBox = new QGroupBox(i18n("Validity"), generalPage);
0073     auto validityFormLayout = new QFormLayout(validityBox);
0074     validityFormLayout->setLabelAlignment(Qt::AlignLeft);
0075     validityFormLayout->addRow(i18n("Issued On"), new QLabel(QLocale().toString(m_certificateInfo.validityStart(), QLocale::LongFormat)));
0076     validityFormLayout->addRow(i18n("Expires On"), new QLabel(QLocale().toString(m_certificateInfo.validityEnd(), QLocale::LongFormat)));
0077 
0078     auto fingerprintBox = new QGroupBox(i18n("Fingerprints"), generalPage);
0079     auto fingerprintFormLayout = new QFormLayout(fingerprintBox);
0080     fingerprintFormLayout->setLabelAlignment(Qt::AlignLeft);
0081     QByteArray certData = m_certificateInfo.certificateData();
0082     auto sha1Label = new QLabel(QString::fromLatin1(QCryptographicHash::hash(certData, QCryptographicHash::Sha1).toHex(' ')));
0083     sha1Label->setWordWrap(true);
0084     auto sha256Label = new QLabel(QString::fromLatin1(QCryptographicHash::hash(certData, QCryptographicHash::Sha256).toHex(' ')));
0085     sha256Label->setWordWrap(true);
0086     fingerprintFormLayout->addRow(i18n("SHA-1 Fingerprint"), sha1Label);
0087     fingerprintFormLayout->addRow(i18n("SHA-256 Fingerprint"), sha256Label);
0088 
0089     auto generalPageLayout = new QVBoxLayout(generalPage);
0090     generalPageLayout->addWidget(issuerBox);
0091     generalPageLayout->addWidget(subjectBox);
0092     generalPageLayout->addWidget(validityBox);
0093     generalPageLayout->addWidget(fingerprintBox);
0094     generalPageLayout->addStretch();
0095 
0096     // force column 1 to have same width
0097     auto resizer = new KColumnResizer(this);
0098     resizer->addWidgetsFromLayout(issuerBox->layout(), 0);
0099     resizer->addWidgetsFromLayout(subjectBox->layout(), 0);
0100     resizer->addWidgetsFromLayout(validityBox->layout(), 0);
0101     resizer->addWidgetsFromLayout(fingerprintBox->layout(), 0);
0102 
0103     // Details tab
0104     auto detailsFrame = new QFrame(this);
0105     addPage(detailsFrame, i18n("Details"));
0106     auto certDataLabel = new QLabel(i18n("Certificate Data:"));
0107     auto certTree = new QTreeView(this);
0108     certTree->setIndentation(0);
0109     m_certificateModel = new CertificateModel(m_certificateInfo, this);
0110     certTree->setModel(m_certificateModel);
0111     connect(certTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &CertificateViewer::updateText);
0112     m_propertyText = new QTextEdit(this);
0113     m_propertyText->setReadOnly(true);
0114 
0115     auto detailsPageLayout = new QVBoxLayout(detailsFrame);
0116     detailsPageLayout->addWidget(certDataLabel);
0117     detailsPageLayout->addWidget(certTree);
0118     detailsPageLayout->addWidget(m_propertyText);
0119 }
0120 
0121 void CertificateViewer::updateText(const QModelIndex &index)
0122 {
0123     QString text;
0124     const CertificateModel::Property key = m_certificateModel->data(index, CertificateModel::PropertyKeyRole).value<CertificateModel::Property>();
0125     switch (key) {
0126     case CertificateModel::SerialNumber:
0127     case CertificateModel::Version:
0128     case CertificateModel::IssuedOn:
0129     case CertificateModel::ExpiresOn:
0130         text = m_certificateModel->data(index, CertificateModel::PropertyVisibleValueRole).toString();
0131         break;
0132     case CertificateModel::Issuer:
0133     case CertificateModel::Subject:
0134         text = splitDNAttributes(m_certificateModel->data(index, CertificateModel::PropertyVisibleValueRole).toString());
0135         break;
0136     case CertificateModel::PublicKey:
0137         text = QString::fromLatin1(m_certificateInfo.publicKey().toHex(' '));
0138         break;
0139     case CertificateModel::KeyUsage:
0140         text = SignatureGuiUtils::getReadableKeyUsageNewLineSeparated(m_certificateInfo.keyUsageExtensions());
0141         break;
0142     case CertificateModel::IssuerName:
0143     case CertificateModel::IssuerEmail:
0144     case CertificateModel::IssuerOrganization:
0145     case CertificateModel::SubjectName:
0146     case CertificateModel::SubjectEmail:
0147     case CertificateModel::SubjectOrganization:
0148     case CertificateModel::Sha1:
0149     case CertificateModel::Sha256:
0150         Q_ASSERT(false);
0151         qWarning() << "Unused";
0152     }
0153     m_propertyText->setText(text);
0154 }
0155 
0156 void CertificateViewer::exportCertificate()
0157 {
0158     const QString caption = i18n("Where do you want to save this certificate?");
0159     const QString path = QFileDialog::getSaveFileName(this, caption, QStringLiteral("Certificate.cer"), i18n("Certificate File (*.cer)"));
0160     if (!path.isEmpty()) {
0161         if (!m_certificateModel->exportCertificateTo(path)) {
0162             KMessageBox::error(this, i18n("Could not export the certificate"));
0163         }
0164     }
0165 }