diff --git a/PdfForQtLib/sources/pdfsignaturehandler.cpp b/PdfForQtLib/sources/pdfsignaturehandler.cpp
index 9e274bb..24f9bad 100644
--- a/PdfForQtLib/sources/pdfsignaturehandler.cpp
+++ b/PdfForQtLib/sources/pdfsignaturehandler.cpp
@@ -861,8 +861,8 @@ QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME*
tm internalTime = { };
if (ASN1_TIME_to_tm(time, &internalTime) > 0)
{
- time_t localTime = mktime(&internalTime);
- result = QDateTime::fromSecsSinceEpoch(localTime, Qt::LocalTime);
+ time_t localTime = _mkgmtime(&internalTime);
+ result = QDateTime::fromSecsSinceEpoch(localTime, Qt::UTC);
}
}
diff --git a/PdfForQtLib/sources/pdfsignaturehandler.h b/PdfForQtLib/sources/pdfsignaturehandler.h
index 34470bc..fb3288d 100644
--- a/PdfForQtLib/sources/pdfsignaturehandler.h
+++ b/PdfForQtLib/sources/pdfsignaturehandler.h
@@ -280,6 +280,8 @@ public:
Error_Signatures_Mask = Error_Signature_Invalid | Error_Signature_SourceCertificateMissing | Error_Signature_NoSignaturesFound |
Error_Signature_DigestFailure | Error_Signature_DataOther | Error_Signature_DataCoveredBySignatureMissing,
+
+ Warnings_Mask = Warning_Signature_NotCoveredBytes
};
Q_DECLARE_FLAGS(VerificationFlags, VerificationFlag)
@@ -309,6 +311,7 @@ public:
bool isCertificateValid() const { return hasFlag(Certificate_OK); }
bool isSignatureValid() const { return hasFlag(Signature_OK); }
bool hasError() const { return !isValid(); }
+ bool hasWarning() const { return m_flags & Warnings_Mask; }
bool hasCertificateError() const { return m_flags & Error_Certificates_Mask; }
bool hasSignatureError() const { return m_flags & Error_Signatures_Mask; }
bool hasFlag(VerificationFlag flag) const { return m_flags.testFlag(flag); }
@@ -318,6 +321,7 @@ public:
const QString& getSignatureFieldQualifiedName() const { return m_signatureFieldQualifiedName; }
const QStringList& getErrors() const { return m_errors; }
const QStringList& getWarnings() const { return m_warnings; }
+ const PDFCertificateInfos& getCertificateInfos() const { return m_certificateInfos; }
void setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName);
void setSignatureFieldReference(PDFObjectReference signatureFieldReference);
diff --git a/PdfForQtViewer/pdfforqtviewer.qrc b/PdfForQtViewer/pdfforqtviewer.qrc
index 9c7d081..e74c5b3 100644
--- a/PdfForQtViewer/pdfforqtviewer.qrc
+++ b/PdfForQtViewer/pdfforqtviewer.qrc
@@ -46,5 +46,9 @@
resources/form-settings.svg
resources/undo.svg
resources/redo.svg
+ resources/result-error.svg
+ resources/result-information.svg
+ resources/result-ok.svg
+ resources/result-warning.svg
diff --git a/PdfForQtViewer/pdfsidebarwidget.cpp b/PdfForQtViewer/pdfsidebarwidget.cpp
index dfb06d0..f03e976 100644
--- a/PdfForQtViewer/pdfsidebarwidget.cpp
+++ b/PdfForQtViewer/pdfsidebarwidget.cpp
@@ -24,6 +24,7 @@
#include "pdfdocument.h"
#include "pdfitemmodels.h"
#include "pdfexception.h"
+#include "pdfsignaturehandler.h"
#include "pdfdrawspacecontroller.h"
#include
@@ -97,6 +98,7 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpee
m_pageInfo[Thumbnails] = { ui->thumbnailsButton, ui->thumbnailsPage };
m_pageInfo[Attachments] = { ui->attachmentsButton, ui->attachmentsPage };
m_pageInfo[Speech] = { ui->speechButton, ui->speechPage };
+ m_pageInfo[Signatures] = { ui->signaturesButton, ui->signaturesPage };
for (const auto& pageInfo : m_pageInfo)
{
@@ -121,7 +123,7 @@ PDFSidebarWidget::~PDFSidebarWidget()
delete ui;
}
-void PDFSidebarWidget::setDocument(const pdf::PDFModifiedDocument& document)
+void PDFSidebarWidget::setDocument(const pdf::PDFModifiedDocument& document, const std::vector& signatures)
{
m_document = document;
m_optionalContentActivity = document.getOptionalContentActivity();
@@ -196,6 +198,7 @@ void PDFSidebarWidget::setDocument(const pdf::PDFModifiedDocument& document)
// Update GUI
updateGUI(preferred);
updateButtons();
+ updateSignatures(signatures);
}
bool PDFSidebarWidget::isEmpty() const
@@ -233,6 +236,9 @@ bool PDFSidebarWidget::isEmpty(Page page) const
case Speech:
return !m_textToSpeech->isValid();
+ case Signatures:
+ return !m_signatures.empty();
+
default:
Q_ASSERT(false);
break;
@@ -331,6 +337,201 @@ void PDFSidebarWidget::updateButtons()
}
}
+void PDFSidebarWidget::updateSignatures(const std::vector& signatures)
+{
+ ui->signatureTreeWidget->setUpdatesEnabled(false);
+ ui->signatureTreeWidget->clear();
+
+ QIcon okIcon(":/resources/result-ok.svg");
+ QIcon errorIcon(":/resources/result-error.svg");
+ QIcon warningIcon(":/resources/result-warning.svg");
+ QIcon infoIcon(":/resources/result-information.svg");
+
+ for (const pdf::PDFSignatureVerificationResult& signature : signatures)
+ {
+ const pdf::PDFCertificateInfos& certificateInfos = signature.getCertificateInfos();
+ const pdf::PDFCertificateInfo* certificateInfo = !certificateInfos.empty() ? &certificateInfos.front() : nullptr;
+
+ QString text = tr("Signed by - %1").arg(certificateInfo ? certificateInfo->getName(pdf::PDFCertificateInfo::CommonName) : tr("Unknown"));
+ QTreeWidgetItem* rootItem = new QTreeWidgetItem(QStringList(text));
+
+ if (signature.hasError())
+ {
+ rootItem->setIcon(0, errorIcon);
+ }
+ else if (signature.hasWarning())
+ {
+ rootItem->setIcon(0, warningIcon);
+ }
+ else
+ {
+ rootItem->setIcon(0, okIcon);
+ }
+
+ if (signature.isCertificateValid())
+ {
+ QTreeWidgetItem* certificateItem = new QTreeWidgetItem(rootItem, QStringList(tr("Certificate is valid.")));
+ certificateItem->setIcon(0, okIcon);
+ }
+
+ if (signature.isSignatureValid())
+ {
+ QTreeWidgetItem* signatureItem = new QTreeWidgetItem(rootItem, QStringList(tr("Signature is valid.")));
+ signatureItem->setIcon(0, okIcon);
+ }
+
+ for (const QString& error : signature.getErrors())
+ {
+ QTreeWidgetItem* item = new QTreeWidgetItem(rootItem, QStringList(error));
+ item->setIcon(0, errorIcon);
+ }
+
+ for (const QString& error : signature.getWarnings())
+ {
+ QTreeWidgetItem* item = new QTreeWidgetItem(rootItem, QStringList(error));
+ item->setIcon(0, warningIcon);
+ }
+
+ if (certificateInfo)
+ {
+ QTreeWidgetItem* certChainRoot = new QTreeWidgetItem(rootItem, QStringList(tr("Certificate validation chain")));
+ certChainRoot->setIcon(0, infoIcon);
+ for (const pdf::PDFCertificateInfo& currentCertificateInfo : certificateInfos)
+ {
+ QTreeWidgetItem* certRoot = new QTreeWidgetItem(certChainRoot, QStringList(currentCertificateInfo.getName(pdf::PDFCertificateInfo::CommonName)));
+ certRoot->setIcon(0, infoIcon);
+
+ auto addName = [certRoot, ¤tCertificateInfo, &infoIcon](pdf::PDFCertificateInfo::NameEntry nameEntry, QString caption)
+ {
+ QString text = currentCertificateInfo.getName(nameEntry);
+ if (!text.isEmpty())
+ {
+ QTreeWidgetItem* item = new QTreeWidgetItem(certRoot, QStringList(QString("%1: %2").arg(caption, text)));
+ item->setIcon(0, infoIcon);
+ }
+ };
+
+ QString publicKeyMethod;
+ switch (currentCertificateInfo.getPublicKey())
+ {
+ case pdf::PDFCertificateInfo::KeyRSA:
+ publicKeyMethod = tr("Protected by RSA method, %1-bit key").arg(currentCertificateInfo.getKeySize());
+ break;
+
+ case pdf::PDFCertificateInfo::KeyDSA:
+ publicKeyMethod = tr("Protected by DSA method, %1-bit key").arg(currentCertificateInfo.getKeySize());
+ break;
+
+ case pdf::PDFCertificateInfo::KeyEC:
+ publicKeyMethod = tr("Protected by EC method, %1-bit key").arg(currentCertificateInfo.getKeySize());
+ break;
+
+ case pdf::PDFCertificateInfo::KeyDH:
+ publicKeyMethod = tr("Protected by DH method, %1-bit key").arg(currentCertificateInfo.getKeySize());
+ break;
+
+ case pdf::PDFCertificateInfo::KeyUnknown:
+ publicKeyMethod = tr("Unknown protection method, %1-bit key").arg(currentCertificateInfo.getKeySize());
+ break;
+
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ addName(pdf::PDFCertificateInfo::CountryName, tr("Country"));
+ addName(pdf::PDFCertificateInfo::OrganizationName, tr("Organization"));
+ addName(pdf::PDFCertificateInfo::OrganizationalUnitName, tr("Org. unit"));
+ addName(pdf::PDFCertificateInfo::DistinguishedName, tr("Name"));
+ addName(pdf::PDFCertificateInfo::StateOrProvinceName, tr("State"));
+ addName(pdf::PDFCertificateInfo::SerialNumber, tr("Serial number"));
+ addName(pdf::PDFCertificateInfo::LocalityName, tr("Locality"));
+ addName(pdf::PDFCertificateInfo::Title, tr("Title"));
+ addName(pdf::PDFCertificateInfo::Surname, tr("Surname"));
+ addName(pdf::PDFCertificateInfo::GivenName, tr("Forename"));
+ addName(pdf::PDFCertificateInfo::Initials, tr("Initials"));
+ addName(pdf::PDFCertificateInfo::Pseudonym, tr("Pseudonym"));
+ addName(pdf::PDFCertificateInfo::GenerationalQualifier, tr("Qualifier"));
+ addName(pdf::PDFCertificateInfo::Email, tr("Email"));
+
+ QTreeWidgetItem* publicKeyItem = new QTreeWidgetItem(certRoot, QStringList(publicKeyMethod));
+ publicKeyItem->setIcon(0, infoIcon);
+
+ QDateTime notValidBefore = currentCertificateInfo.getNotValidBefore().toLocalTime();
+ QDateTime notValidAfter = currentCertificateInfo.getNotValidAfter().toLocalTime();
+
+ if (notValidBefore.isValid())
+ {
+ QTreeWidgetItem* item = new QTreeWidgetItem(certRoot, QStringList(QString("Valid from: %2").arg(notValidBefore.toString(Qt::DefaultLocaleShortDate))));
+ item->setIcon(0, infoIcon);
+ }
+
+ if (notValidAfter.isValid())
+ {
+ QTreeWidgetItem* item = new QTreeWidgetItem(certRoot, QStringList(QString("Valid to: %2").arg(notValidAfter.toString(Qt::DefaultLocaleShortDate))));
+ item->setIcon(0, infoIcon);
+ }
+
+ QStringList keyUsages;
+ pdf::PDFCertificateInfo::KeyUsageFlags keyUsageFlags = currentCertificateInfo.getKeyUsage();
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageDigitalSignature))
+ {
+ keyUsages << tr("Digital signatures");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageNonRepudiation))
+ {
+ keyUsages << tr("Non-repudiation");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageKeyEncipherment))
+ {
+ keyUsages << tr("Key encipherement");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageDataEncipherment))
+ {
+ keyUsages << tr("Application data encipherement");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageAgreement))
+ {
+ keyUsages << tr("Key agreement");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageCertSign))
+ {
+ keyUsages << tr("Verify signatures on certificates");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageCrlSign))
+ {
+ keyUsages << tr("Verify signatures on revocation information");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageEncipherOnly))
+ {
+ keyUsages << tr("Encipher data during key agreement");
+ }
+ if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageDecipherOnly))
+ {
+ keyUsages << tr("Decipher data during key agreement");
+ }
+
+ if (!keyUsages.isEmpty())
+ {
+ QTreeWidgetItem* keyUsageRoot = new QTreeWidgetItem(certRoot, QStringList(tr("Key usages")));
+ keyUsageRoot->setIcon(0, infoIcon);
+
+ for (const QString& keyUsage : keyUsages)
+ {
+ QTreeWidgetItem* keyUsageItem = new QTreeWidgetItem(keyUsageRoot, QStringList(keyUsage));
+ keyUsageItem->setIcon(0, infoIcon);
+ }
+ }
+ }
+ }
+
+ ui->signatureTreeWidget->addTopLevelItem(rootItem);
+ }
+
+ ui->signatureTreeWidget->expandToDepth(1);
+ ui->signatureTreeWidget->setUpdatesEnabled(true);
+}
+
void PDFSidebarWidget::onPageButtonClicked()
{
QObject* pushButton = sender();
diff --git a/PdfForQtViewer/pdfsidebarwidget.h b/PdfForQtViewer/pdfsidebarwidget.h
index 0808d91..2374f8d 100644
--- a/PdfForQtViewer/pdfsidebarwidget.h
+++ b/PdfForQtViewer/pdfsidebarwidget.h
@@ -37,10 +37,11 @@ class PDFAction;
class PDFDocument;
class PDFDrawWidgetProxy;
class PDFModifiedDocument;
-class PDFOutlineTreeItemModel;
class PDFThumbnailsItemModel;
-class PDFAttachmentsTreeItemModel;
+class PDFOutlineTreeItemModel;
class PDFOptionalContentActivity;
+class PDFAttachmentsTreeItemModel;
+class PDFSignatureVerificationResult;
class PDFOptionalContentTreeItemModel;
}
@@ -67,10 +68,11 @@ public:
OptionalContent,
Attachments,
Speech,
+ Signatures,
_END
};
- void setDocument(const pdf::PDFModifiedDocument& document);
+ void setDocument(const pdf::PDFModifiedDocument& document, const std::vector& signatures);
/// Returns true, if all items in sidebar are empty
bool isEmpty() const;
@@ -93,6 +95,7 @@ signals:
private:
void updateGUI(Page preferredPage);
void updateButtons();
+ void updateSignatures(const std::vector& signatures);
void onPageButtonClicked();
void onOutlineItemClicked(const QModelIndex& index);
@@ -116,6 +119,7 @@ private:
pdf::PDFOptionalContentActivity* m_optionalContentActivity;
pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel;
std::map m_pageInfo;
+ std::vector m_signatures;
};
} // namespace pdfviewer
diff --git a/PdfForQtViewer/pdfsidebarwidget.ui b/PdfForQtViewer/pdfsidebarwidget.ui
index 0eca9a9..66c3e9d 100644
--- a/PdfForQtViewer/pdfsidebarwidget.ui
+++ b/PdfForQtViewer/pdfsidebarwidget.ui
@@ -93,6 +93,19 @@
+ -
+
+
+ Signatures
+
+
+ true
+
+
+ true
+
+
+
-
@@ -111,7 +124,7 @@
-
- 5
+ 6
@@ -392,6 +405,37 @@
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ true
+
+
+ true
+
+
+
+ 1
+
+
+
+
+
+
diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp
index cd0d7eb..6ea7d03 100644
--- a/PdfForQtViewer/pdfviewermainwindow.cpp
+++ b/PdfForQtViewer/pdfviewermainwindow.cpp
@@ -967,8 +967,7 @@ void PDFViewerMainWindow::openDocument(const QString& fileName)
{
// Verify signatures
pdf::PDFForm form = pdf::PDFForm::parse(&document, document.getCatalog()->getFormObject());
- std::vector signaturesVerifications = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource());
-
+ result.signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource());
result.document.reset(new pdf::PDFDocument(qMove(document)));
}
@@ -1001,7 +1000,8 @@ void PDFViewerMainWindow::onDocumentReadingFinished()
// We add file to recent files only, if we have successfully read the document
m_recentFileManager->addRecentFile(m_fileInfo.originalFileName);
- m_pdfDocument = result.document;
+ m_pdfDocument = qMove(result.document);
+ m_signatures = qMove(result.signatures);
pdf::PDFModifiedDocument document(m_pdfDocument.data(), m_optionalContentActivity);
setDocument(document);
@@ -1072,7 +1072,7 @@ void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
m_toolManager->setDocument(document);
m_textToSpeech->setDocument(document);
m_pdfWidget->setDocument(document);
- m_sidebarWidget->setDocument(document);
+ m_sidebarWidget->setDocument(document, m_signatures);
m_advancedFindWidget->setDocument(document);
if (m_sidebarWidget->isEmpty())
@@ -1109,6 +1109,7 @@ void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
void PDFViewerMainWindow::closeDocument()
{
+ m_signatures.clear();
setDocument(pdf::PDFModifiedDocument());
m_pdfDocument.reset();
updateActionsAvailability();
diff --git a/PdfForQtViewer/pdfviewermainwindow.h b/PdfForQtViewer/pdfviewermainwindow.h
index 64d7fed..698a206 100644
--- a/PdfForQtViewer/pdfviewermainwindow.h
+++ b/PdfForQtViewer/pdfviewermainwindow.h
@@ -158,6 +158,7 @@ private:
pdf::PDFDocumentPointer document;
QString errorMessage;
pdf::PDFDocumentReader::Result result = pdf::PDFDocumentReader::Result::Cancelled;
+ std::vector signatures;
};
Ui::PDFViewerMainWindow* ui;
@@ -179,6 +180,7 @@ private:
QWinTaskbarButton* m_taskbarButton;
QWinTaskbarProgress* m_progressTaskbarIndicator;
PDFFileInfo m_fileInfo;
+ std::vector m_signatures;
QFuture m_future;
QFutureWatcher* m_futureWatcher;
diff --git a/PdfForQtViewer/resources/result-error.svg b/PdfForQtViewer/resources/result-error.svg
new file mode 100644
index 0000000..cad25bb
--- /dev/null
+++ b/PdfForQtViewer/resources/result-error.svg
@@ -0,0 +1,101 @@
+
+
+
+
diff --git a/PdfForQtViewer/resources/result-information.svg b/PdfForQtViewer/resources/result-information.svg
new file mode 100644
index 0000000..b9e5514
--- /dev/null
+++ b/PdfForQtViewer/resources/result-information.svg
@@ -0,0 +1,101 @@
+
+
+
+
diff --git a/PdfForQtViewer/resources/result-ok.svg b/PdfForQtViewer/resources/result-ok.svg
new file mode 100644
index 0000000..6ab69f4
--- /dev/null
+++ b/PdfForQtViewer/resources/result-ok.svg
@@ -0,0 +1,101 @@
+
+
+
+
diff --git a/PdfForQtViewer/resources/result-warning.svg b/PdfForQtViewer/resources/result-warning.svg
new file mode 100644
index 0000000..61866da
--- /dev/null
+++ b/PdfForQtViewer/resources/result-warning.svg
@@ -0,0 +1,101 @@
+
+
+
+