Validation settings, certificate store

This commit is contained in:
Jakub Melka 2020-06-27 17:28:03 +02:00
parent aa36af6587
commit 49a2af275f
16 changed files with 732 additions and 86 deletions

View File

@ -26,6 +26,7 @@
#include <QMutex>
#include <QMutexLocker>
#include <QDataStream>
#include <array>
@ -139,24 +140,24 @@ PDFSignature PDFSignature::parse(const PDFObjectStorage* storage, PDFObject obje
return result;
}
PDFSignatureHandler* PDFSignatureHandler::createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData)
PDFSignatureHandler* PDFSignatureHandler::createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters)
{
Q_ASSERT(signatureField);
const QByteArray& subfilter = signatureField->getSignature().getSubfilter();
if (subfilter == "adbe.pkcs7.detached")
{
return new PDFSignatureHandler_adbe_pkcs7_detached(signatureField, sourceData);
return new PDFSignatureHandler_adbe_pkcs7_detached(signatureField, sourceData, parameters);
}
return nullptr;
}
std::vector<PDFSignatureVerificationResult> PDFSignatureHandler::verifySignatures(const PDFForm& form, const QByteArray& sourceData)
std::vector<PDFSignatureVerificationResult> PDFSignatureHandler::verifySignatures(const PDFForm& form, const QByteArray& sourceData, const Parameters& parameters)
{
std::vector<PDFSignatureVerificationResult> result;
if (form.isAcroForm() || form.isXFAForm())
if (parameters.enableVerification && (form.isAcroForm() || form.isXFAForm()))
{
std::vector<const PDFFormFieldSignature*> signatureFields;
auto getSignatureFields = [&signatureFields](const PDFFormField* field)
@ -173,7 +174,7 @@ std::vector<PDFSignatureVerificationResult> PDFSignatureHandler::verifySignature
for (const PDFFormFieldSignature* signatureField : signatureFields)
{
if (const PDFSignatureHandler* signatureHandler = createHandler(signatureField, sourceData))
if (const PDFSignatureHandler* signatureHandler = createHandler(signatureField, sourceData, parameters))
{
result.emplace_back(signatureHandler->verify());
delete signatureHandler;
@ -399,7 +400,12 @@ void PDFPublicKeySignatureHandler::verifyCertificate(PDFSignatureVerificationRes
break;
}
X509_STORE_CTX_set_flags(context, X509_V_FLAG_TRUSTED_FIRST);
unsigned long flags = X509_V_FLAG_TRUSTED_FIRST;
if (m_parameters.ignoreExpirationDate)
{
flags |= X509_V_FLAG_NO_CHECK_TIME;
}
X509_STORE_CTX_set_flags(context, flags);
int verificationResult = X509_verify_cert(context);
if (verificationResult <= 0)
@ -766,11 +772,47 @@ PDFCertificateInfo PDFPublicKeySignatureHandler::getCertificateInfo(X509* certif
info.setKeyUsage(static_cast<PDFCertificateInfo::KeyUsageFlags>(keyUsage));
}
unsigned char* buffer = nullptr;
int length = i2d_X509(certificate, &buffer);
if (length >= 0)
{
Q_ASSERT(buffer);
info.setCertificateData(QByteArray(reinterpret_cast<const char*>(buffer), length));
OPENSSL_free(buffer);
}
}
return info;
}
void PDFCertificateInfo::serialize(QDataStream& stream) const
{
stream << persist_version;
stream << m_version;
stream << m_keySize;
stream << m_publicKey;
stream << m_nameEntries;
stream << m_notValidBefore;
stream << m_notValidAfter;
stream << m_keyUsage;
stream << m_certificateData;
}
void PDFCertificateInfo::deserialize(QDataStream& stream)
{
int persist_version = 0;
stream >> persist_version;
stream >> m_version;
stream >> m_keySize;
stream >> m_publicKey;
stream >> m_nameEntries;
stream >> m_notValidBefore;
stream >> m_notValidAfter;
stream >> m_keyUsage;
stream >> m_certificateData;
}
QDateTime PDFCertificateInfo::getNotValidBefore() const
{
return m_notValidBefore;
@ -791,12 +833,12 @@ void PDFCertificateInfo::setNotValidAfter(const QDateTime& notValidAfter)
m_notValidAfter = notValidAfter;
}
long PDFCertificateInfo::getVersion() const
int32_t PDFCertificateInfo::getVersion() const
{
return m_version;
}
void PDFCertificateInfo::setVersion(long version)
void PDFCertificateInfo::setVersion(int32_t version)
{
m_version = version;
}
@ -831,6 +873,46 @@ void PDFCertificateInfo::setKeyUsage(KeyUsageFlags keyUsage)
m_keyUsage = keyUsage;
}
std::optional<PDFCertificateInfo> PDFCertificateInfo::getCertificateInfo(const QByteArray& certificateData)
{
std::optional<PDFCertificateInfo> result;
PDFOpenSSLGlobalLock lock;
const unsigned char* data = reinterpret_cast<const unsigned char*>(certificateData.constData());
if (X509* certificate = d2i_X509(nullptr, &data, certificateData.length()))
{
result = PDFPublicKeySignatureHandler::getCertificateInfo(certificate);
X509_free(certificate);
}
return result;
}
QByteArray PDFCertificateInfo::getCertificateData() const
{
return m_certificateData;
}
void PDFCertificateInfo::setCertificateData(const QByteArray& certificateData)
{
m_certificateData = certificateData;
}
void PDFCertificateStore::CertificateEntry::serialize(QDataStream& stream) const
{
stream << persist_version;
stream << type;
stream << info;
}
void PDFCertificateStore::CertificateEntry::deserialize(QDataStream& stream)
{
int persist_version = 0;
stream >> persist_version;
stream >> type;
stream >> info;
}
QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int nid)
{
QString result;
@ -852,7 +934,7 @@ QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int
return result;
}
QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME* time)
QDateTime PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME* time)
{
QDateTime result;
@ -869,6 +951,45 @@ QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME*
return result;
}
void PDFCertificateStore::serialize(QDataStream& stream) const
{
stream << persist_version;
stream << m_certificates;
}
void pdf::PDFCertificateStore::deserialize(QDataStream& stream)
{
int persist_version = 0;
stream >> persist_version;
stream >> m_certificates;
}
bool PDFCertificateStore::add(EntryType type, const QByteArray& certificate)
{
if (auto certificateInfo = PDFCertificateInfo::getCertificateInfo(certificate))
{
return add(type, qMove(*certificateInfo));
}
return false;
}
bool PDFCertificateStore::add(EntryType type, PDFCertificateInfo info)
{
auto it = std::find_if(m_certificates.cbegin(), m_certificates.cend(), [&info](const auto& entry) { return entry.info == info; });
if (it == m_certificates.cend())
{
m_certificates.push_back({ type, qMove(info) });
}
return true;
}
bool pdf::PDFCertificateStore::contains(const pdf::PDFCertificateInfo& info)
{
return std::find_if(m_certificates.cbegin(), m_certificates.cend(), [&info](const auto& entry) { return entry.info == info; }) != m_certificates.cend();
}
} // namespace pdf
#ifdef Q_OS_WIN
@ -879,23 +1000,42 @@ QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME*
void pdf::PDFPublicKeySignatureHandler::addTrustedCertificates(X509_STORE* store) const
{
#ifdef Q_OS_WIN
HCERTSTORE certStore = CertOpenSystemStore(NULL, L"ROOT");
PCCERT_CONTEXT context = nullptr;
if (certStore)
if (m_parameters.store)
{
while (context = CertEnumCertificatesInStore(certStore, context))
const PDFCertificateStore::CertificateEntries& certificates = m_parameters.store->getCertificates();
for (const auto& entry : certificates)
{
const unsigned char* pointer = context->pbCertEncoded;
X509* certificate = d2i_X509(nullptr, &pointer, context->cbCertEncoded);
QByteArray certificateData = entry.info.getCertificateData();
const unsigned char* pointer = reinterpret_cast<const unsigned char*>(certificateData.constData());
X509* certificate = d2i_X509(nullptr, &pointer, certificateData.length());
if (certificate)
{
X509_STORE_add_cert(store, certificate);
X509_free(certificate);
}
}
}
CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
#ifdef Q_OS_WIN
if (m_parameters.useSystemCertificateStore)
{
HCERTSTORE certStore = CertOpenSystemStore(NULL, L"ROOT");
PCCERT_CONTEXT context = nullptr;
if (certStore)
{
while (context = CertEnumCertificatesInStore(certStore, context))
{
const unsigned char* pointer = context->pbCertEncoded;
X509* certificate = d2i_X509(nullptr, &pointer, context->cbCertEncoded);
if (certificate)
{
X509_STORE_add_cert(store, certificate);
X509_free(certificate);
}
}
CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
}
}
#endif
}

View File

@ -26,10 +26,13 @@
#include <optional>
class QDataStream;
namespace pdf
{
class PDFForm;
class PDFObjectStorage;
class PDFCertificateStore;
class PDFFormFieldSignature;
/// Signature reference dictionary.
@ -153,6 +156,15 @@ class PDFFORQTLIBSHARED_EXPORT PDFCertificateInfo
public:
explicit inline PDFCertificateInfo() = default;
void serialize(QDataStream& stream) const;
void deserialize(QDataStream& stream);
friend inline QDataStream& operator<<(QDataStream& stream, const PDFCertificateInfo& info) { info.serialize(stream); return stream; }
friend inline QDataStream& operator>>(QDataStream& stream, PDFCertificateInfo& info) { info.deserialize(stream); return stream; }
bool operator ==(const PDFCertificateInfo&) const = default;
bool operator !=(const PDFCertificateInfo&) const = default;
/// These entries are taken from RFC 5280, section 4.1.2.4,
/// they are supported and loaded from the certificate, with
/// exception of Email entry.
@ -210,8 +222,8 @@ public:
QDateTime getNotValidAfter() const;
void setNotValidAfter(const QDateTime& notValidAfter);
long getVersion() const;
void setVersion(long version);
int32_t getVersion() const;
void setVersion(int32_t version);
PublicKey getPublicKey() const;
void setPublicKey(const PublicKey& publicKey);
@ -222,14 +234,25 @@ public:
KeyUsageFlags getKeyUsage() const;
void setKeyUsage(KeyUsageFlags keyUsage);
QByteArray getCertificateData() const;
void setCertificateData(const QByteArray& certificateData);
/// Creates certificate info from binary data. Binary data must
/// contain DER-encoded certificate.
/// \param certificateData Data
static std::optional<PDFCertificateInfo> getCertificateInfo(const QByteArray& certificateData);
private:
long m_version = 0;
static constexpr int persist_version = 1;
int32_t m_version = 0;
int m_keySize = 0;
PublicKey m_publicKey = KeyUnknown;
std::array<QString, NameEnd> m_nameEntries;
QDateTime m_notValidBefore;
QDateTime m_notValidAfter;
KeyUsageFlags m_keyUsage;
QByteArray m_certificateData;
};
using PDFCertificateInfos = std::vector<PDFCertificateInfo>;
@ -349,11 +372,20 @@ public:
virtual PDFSignatureVerificationResult verify() const = 0;
struct Parameters
{
const PDFCertificateStore* store = nullptr;
bool enableVerification = true;
bool ignoreExpirationDate = false;
bool useSystemCertificateStore = true;
};
/// Tries to verify all signatures in the form. If form is invalid, then
/// empty vector is returned.
/// \param form Form
/// \param sourceData Source data
static std::vector<PDFSignatureVerificationResult> verifySignatures(const PDFForm& form, const QByteArray& sourceData);
/// \param parameters Verification settings
static std::vector<PDFSignatureVerificationResult> verifySignatures(const PDFForm& form, const QByteArray& sourceData, const Parameters& parameters);
private:
@ -361,7 +393,71 @@ private:
/// If signature format is unknown, then nullptr is returned.
/// \param signatureField Signature field
/// \param sourceData
static PDFSignatureHandler* createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData);
/// \param parameters Verification settings
static PDFSignatureHandler* createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters);
};
/// Trusted certificate store. Contains list of trusted certificates. Store
/// can be persisted to the persistent storage trough serialization/deserialization.
/// Persisting method is versioned.
class PDFFORQTLIBSHARED_EXPORT PDFCertificateStore
{
public:
explicit inline PDFCertificateStore() = default;
void serialize(QDataStream& stream) const;
void deserialize(QDataStream& stream);
enum class EntryType : int
{
User, ///< Certificate has been added manually by the user
EUTL ///< Certificate comes EU trusted list
};
struct CertificateEntry
{
void serialize(QDataStream& stream) const;
void deserialize(QDataStream& stream);
friend inline QDataStream& operator<<(QDataStream& stream, const CertificateEntry& entry) { entry.serialize(stream); return stream; }
friend inline QDataStream& operator>>(QDataStream& stream, CertificateEntry& entry) { entry.deserialize(stream); return stream; }
static constexpr int persist_version = 1;
EntryType type = EntryType::User;
PDFCertificateInfo info;
};
using CertificateEntries = std::vector<CertificateEntry>;
/// Tries to add new certificate to the certificate store. If certificate
/// is already here, then nothing happens and function returns true.
/// Otherwise data are checked, if they really contains a certificate,
/// and if yes, then it is added. Function returns false if, and only if,
/// error occured and certificate was not added.
/// \param type Type
/// \param certificate Certificate
bool add(EntryType type, const QByteArray& certificate);
/// Tries to add new certificate to the certificate store. If certificate
/// is already here, then nothing happens and function returns true.
/// Otherwise data are checked, if they really contains a certificate,
/// and if yes, then it is added. Function returns false if, and only if,
/// error occured and certificate was not added.
/// \param type Type
/// \param info Certificate info
bool add(EntryType type, PDFCertificateInfo info);
/// Returns true, if storage contains given certificate
/// \param info Certificate info
bool contains(const PDFCertificateInfo& info);
/// Get certificates stored in the store
const CertificateEntries& getCertificates() const { return m_certificates; }
private:
static constexpr int persist_version = 1;
CertificateEntries m_certificates;
};
} // namespace pdf

View File

@ -31,9 +31,10 @@ namespace pdf
class PDFPublicKeySignatureHandler : public PDFSignatureHandler
{
protected:
explicit PDFPublicKeySignatureHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData) :
explicit PDFPublicKeySignatureHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters) :
m_signatureField(signatureField),
m_sourceData(sourceData)
m_sourceData(sourceData),
m_parameters(parameters)
{
}
@ -45,6 +46,7 @@ protected:
BIO* getSignedDataBuffer(PDFSignatureVerificationResult& result, QByteArray& outputBuffer) const;
public:
/// Return a list of certificates from PKCS7 object
static STACK_OF(X509)* getCertificates(PKCS7* pkcs7);
@ -61,13 +63,14 @@ protected:
protected:
const PDFFormFieldSignature* m_signatureField;
QByteArray m_sourceData;
Parameters m_parameters;
};
class PDFSignatureHandler_adbe_pkcs7_detached : public PDFPublicKeySignatureHandler
{
public:
explicit PDFSignatureHandler_adbe_pkcs7_detached(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData) :
PDFPublicKeySignatureHandler(signatureField, sourceData)
explicit PDFSignatureHandler_adbe_pkcs7_detached(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters) :
PDFPublicKeySignatureHandler(signatureField, sourceData, parameters)
{
}

View File

@ -248,55 +248,6 @@ size_t PDFTextCharacterSpatialIndex::queryImpl(size_t nodeIndex, const QRectF& r
}
}
template<typename T>
QDataStream& operator>>(QDataStream& stream, std::vector<T>& vector)
{
std::vector<T>::size_type size = 0;
stream >> size;
vector.resize(size);
for (T& item : vector)
{
stream >> item;
}
return stream;
}
template<typename T>
QDataStream& operator<<(QDataStream& stream, const std::vector<T>& vector)
{
stream << vector.size();
for (const T& item : vector)
{
stream << item;
}
return stream;
}
template<typename T>
QDataStream& operator>>(QDataStream& stream, std::set<T>& set)
{
std::set<T>::size_type size = 0;
stream >> size;
for (size_t i = 0; i < size; ++i)
{
T item;
stream >> item;
set.insert(set.end(), qMove(item));
}
return stream;
}
template<typename T>
QDataStream& operator<<(QDataStream& stream, const std::set<T>& set)
{
stream << set.size();
for (const T& item : set)
{
stream << item;
}
return stream;
}
PDFTextLayout::PDFTextLayout()
{

View File

@ -25,6 +25,7 @@
#include <QByteArray>
#include <QDataStream>
#include <set>
#include <vector>
#include <iterator>
#include <functional>
@ -567,6 +568,94 @@ private:
std::vector<ClosedInterval> m_intervals;
};
template<typename T>
QDataStream& operator>>(QDataStream& stream, std::vector<T>& vector)
{
std::vector<T>::size_type size = 0;
stream >> size;
vector.resize(size);
for (T& item : vector)
{
stream >> item;
}
return stream;
}
template<typename T>
QDataStream& operator<<(QDataStream& stream, const std::vector<T>& vector)
{
stream << vector.size();
for (const T& item : vector)
{
stream << item;
}
return stream;
}
template<typename T, size_t Size>
QDataStream& operator>>(QDataStream& stream, std::array<T, Size>& array)
{
std::array<T, Size>::size_type size = 0;
stream >> size;
for (size_t i = 0; i < size; ++i)
{
if (i < array.size())
{
stream >> array[i];
}
else
{
T item;
stream >> item;
}
}
// If array size was changed, then fill in empty objects
for (size_t i = size; i < array.size(); ++i)
{
array[i] = T();
}
return stream;
}
template<typename T, size_t Size>
QDataStream& operator<<(QDataStream& stream, const std::array<T, Size>& array)
{
stream << array.size();
for (const T& item : array)
{
stream << item;
}
return stream;
}
template<typename T>
QDataStream& operator>>(QDataStream& stream, std::set<T>& set)
{
std::set<T>::size_type size = 0;
stream >> size;
for (size_t i = 0; i < size; ++i)
{
T item;
stream >> item;
set.insert(set.end(), qMove(item));
}
return stream;
}
template<typename T>
QDataStream& operator<<(QDataStream& stream, const std::set<T>& set)
{
stream << set.size();
for (const T& item : set)
{
stream << item;
}
return stream;
}
} // namespace pdf
#endif // PDFUTILS_H

View File

@ -50,5 +50,6 @@
<file>resources/result-information.svg</file>
<file>resources/result-ok.svg</file>
<file>resources/result-warning.svg</file>
<file>resources/signature.svg</file>
</qresource>
</RCC>

View File

@ -18,6 +18,8 @@
#include "pdfsidebarwidget.h"
#include "ui_pdfsidebarwidget.h"
#include "pdfviewersettings.h"
#include "pdfwidgetutils.h"
#include "pdftexttospeech.h"
@ -45,11 +47,17 @@ constexpr const char* STYLESHEET =
"QWidget#speechPage { background-color: #F0F0F0 }"
"QWidget#PDFSidebarWidget { background-color: #404040; background: green;}";
PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpeech* textToSpeech, QWidget* parent) :
PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore,
PDFViewerSettings* settings,
QWidget* parent) :
QWidget(parent),
ui(new Ui::PDFSidebarWidget),
m_proxy(proxy),
m_textToSpeech(textToSpeech),
m_certificateStore(certificateStore),
m_settings(settings),
m_outlineTreeModel(nullptr),
m_thumbnailsModel(nullptr),
m_optionalContentTreeModel(nullptr),
@ -114,6 +122,9 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpee
ui->speechRateValueLabel, ui->speechPitchValueLabel, ui->speechVolumeValueLabel,
ui->speechActualTextEdit);
ui->signatureTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->signatureTreeWidget, &QTreeWidget::customContextMenuRequested, this, &PDFSidebarWidget::onSignatureCustomContextMenuRequested);
selectPage(Invalid);
updateButtons();
}
@ -342,11 +353,18 @@ void PDFSidebarWidget::updateSignatures(const std::vector<pdf::PDFSignatureVerif
ui->signatureTreeWidget->setUpdatesEnabled(false);
ui->signatureTreeWidget->clear();
m_certificateInfos.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");
if (m_settings->getSettings().m_signatureTreatWarningsAsErrors)
{
warningIcon = errorIcon;
}
for (const pdf::PDFSignatureVerificationResult& signature : signatures)
{
const pdf::PDFCertificateInfos& certificateInfos = signature.getCertificateInfos();
@ -401,6 +419,13 @@ void PDFSidebarWidget::updateSignatures(const std::vector<pdf::PDFSignatureVerif
QTreeWidgetItem* certRoot = new QTreeWidgetItem(certChainRoot, QStringList(currentCertificateInfo.getName(pdf::PDFCertificateInfo::CommonName)));
certRoot->setIcon(0, infoIcon);
pdf::PDFCertificateInfo::KeyUsageFlags keyUsageFlags = currentCertificateInfo.getKeyUsage();
if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageCertSign))
{
m_certificateInfos.push_back(currentCertificateInfo);
certRoot->setData(0, Qt::UserRole, m_certificateInfos.size() - 1);
}
auto addName = [certRoot, &currentCertificateInfo, &infoIcon](pdf::PDFCertificateInfo::NameEntry nameEntry, QString caption)
{
QString text = currentCertificateInfo.getName(nameEntry);
@ -473,7 +498,6 @@ void PDFSidebarWidget::updateSignatures(const std::vector<pdf::PDFSignatureVerif
}
QStringList keyUsages;
pdf::PDFCertificateInfo::KeyUsageFlags keyUsageFlags = currentCertificateInfo.getKeyUsage();
if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageDigitalSignature))
{
keyUsages << tr("Digital signatures");
@ -619,6 +643,37 @@ void PDFSidebarWidget::onThumbnailClicked(const QModelIndex& index)
}
}
void PDFSidebarWidget::onSignatureCustomContextMenuRequested(const QPoint& pos)
{
if (QTreeWidgetItem* item = ui->signatureTreeWidget->itemAt(pos))
{
QVariant data = item->data(0, Qt::UserRole);
if (data.isValid())
{
const pdf::PDFCertificateInfo& info = m_certificateInfos.at(data.toInt());
if (!m_certificateStore->contains(info))
{
QMenu menu;
QAction* action = menu.addAction(tr("Add to trusted certificates"));
auto addCertificate = [this, info]()
{
if (QMessageBox::question(this, tr("Add to Trusted Certificate Store"), tr("Are you sure want to add '%1' to the trusted certificate store?").arg(info.getName(pdf::PDFCertificateInfo::CommonName))) == QMessageBox::Yes)
{
if (!m_certificateStore->add(pdf::PDFCertificateStore::EntryType::User, info))
{
QMessageBox::critical(this, tr("Trusted Certificate Store Error"), tr("Failed to add certificate to the trusted certificate store."));
}
}
};
connect(action, &QAction::triggered, this, addCertificate);
menu.exec(ui->signatureTreeWidget->viewport()->mapToGlobal(pos));
}
}
}
}
void PDFSidebarWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);

View File

@ -35,7 +35,9 @@ namespace pdf
{
class PDFAction;
class PDFDocument;
class PDFCertificateInfo;
class PDFDrawWidgetProxy;
class PDFCertificateStore;
class PDFModifiedDocument;
class PDFThumbnailsItemModel;
class PDFOutlineTreeItemModel;
@ -48,13 +50,18 @@ class PDFOptionalContentTreeItemModel;
namespace pdfviewer
{
class PDFTextToSpeech;
class PDFViewerSettings;
class PDFSidebarWidget : public QWidget
{
Q_OBJECT
public:
explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpeech* textToSpeech, QWidget* parent);
explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore,
PDFViewerSettings* settings,
QWidget* parent);
virtual ~PDFSidebarWidget() override;
virtual void paintEvent(QPaintEvent* event) override;
@ -102,6 +109,7 @@ private:
void onThumbnailsSizeChanged(int size);
void onAttachmentCustomContextMenuRequested(const QPoint& pos);
void onThumbnailClicked(const QModelIndex& index);
void onSignatureCustomContextMenuRequested(const QPoint &pos);
struct PageInfo
{
@ -112,6 +120,8 @@ private:
Ui::PDFSidebarWidget* ui;
pdf::PDFDrawWidgetProxy* m_proxy;
PDFTextToSpeech* m_textToSpeech;
pdf::PDFCertificateStore* m_certificateStore;
PDFViewerSettings* m_settings;
pdf::PDFOutlineTreeItemModel* m_outlineTreeModel;
pdf::PDFThumbnailsItemModel* m_thumbnailsModel;
pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel;
@ -120,6 +130,7 @@ private:
pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel;
std::map<Page, PageInfo> m_pageInfo;
std::vector<pdf::PDFSignatureVerificationResult> m_signatures;
std::vector<pdf::PDFCertificateInfo> m_certificateInfos;
};
} // namespace pdfviewer

View File

@ -217,7 +217,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_pdfWidget->getDrawWidgetProxy()->setProgress(m_progress);
m_textToSpeech->setProxy(m_pdfWidget->getDrawWidgetProxy());
m_sidebarWidget = new PDFSidebarWidget(m_pdfWidget->getDrawWidgetProxy(), m_textToSpeech, this);
m_sidebarWidget = new PDFSidebarWidget(m_pdfWidget->getDrawWidgetProxy(), m_textToSpeech, &m_certificateStore, m_settings, this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
m_sidebarDockWidget->setObjectName("SidebarDockWidget");
m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
@ -966,8 +966,14 @@ void PDFViewerMainWindow::openDocument(const QString& fileName)
if (result.result == pdf::PDFDocumentReader::Result::OK)
{
// Verify signatures
pdf::PDFSignatureHandler::Parameters parameters;
parameters.store = &m_certificateStore;
parameters.enableVerification = m_settings->getSettings().m_signatureVerificationEnabled;
parameters.ignoreExpirationDate = m_settings->getSettings().m_signatureIgnoreCertificateValidityTime;
parameters.useSystemCertificateStore = m_settings->getSettings().m_signatureUseSystemStore;
pdf::PDFForm form = pdf::PDFForm::parse(&document, document.getCatalog()->getFormObject());
result.signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource());
result.signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource(), parameters);
result.document.reset(new pdf::PDFDocument(qMove(document)));
}

View File

@ -98,11 +98,8 @@ private slots:
void on_actionRotateLeft_triggered();
void on_actionPrint_triggered();
void on_actionRender_to_Images_triggered();
void on_actionOptimize_triggered();
void on_actionSave_As_triggered();
void on_actionSave_triggered();
private:
@ -180,6 +177,7 @@ private:
QWinTaskbarButton* m_taskbarButton;
QWinTaskbarProgress* m_progressTaskbarIndicator;
PDFFileInfo m_fileInfo;
pdf::PDFCertificateStore m_certificateStore;
std::vector<pdf::PDFSignatureVerificationResult> m_signatures;
QFuture<AsyncReadingResult> m_future;

View File

@ -86,6 +86,13 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti
m_settings.m_formAppearanceFlags = static_cast<pdf::PDFFormManager::FormAppearanceFlags>(settings.value("formAppearance", int(pdf::PDFFormManager::getDefaultApperanceFlags())).toInt());
settings.endGroup();
settings.beginGroup("Signature");
m_settings.m_signatureVerificationEnabled = settings.value("signatureVerificationEnabled", defaultSettings.m_signatureVerificationEnabled).toBool();
m_settings.m_signatureTreatWarningsAsErrors = settings.value("signatureTreatWarningsAsErrors", defaultSettings.m_signatureTreatWarningsAsErrors).toBool();
m_settings.m_signatureIgnoreCertificateValidityTime = settings.value("signatureIgnoreCertificateValidityTime", defaultSettings.m_signatureIgnoreCertificateValidityTime).toBool();
m_settings.m_signatureUseSystemStore = settings.value("signatureUseSystemStore", defaultSettings.m_signatureUseSystemStore).toBool();
settings.endGroup();
emit settingsChanged();
}
@ -139,6 +146,13 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.beginGroup("Forms");
settings.setValue("formAppearance", int(m_settings.m_formAppearanceFlags));
settings.endGroup();
settings.beginGroup("Signature");
settings.setValue("signatureVerificationEnabled", m_settings.m_signatureVerificationEnabled);
settings.setValue("signatureTreatWarningsAsErrors", m_settings.m_signatureTreatWarningsAsErrors);
settings.setValue("signatureIgnoreCertificateValidityTime", m_settings.m_signatureIgnoreCertificateValidityTime);
settings.setValue("signatureUseSystemStore", m_settings.m_signatureUseSystemStore);
settings.endGroup();
}
QString PDFViewerSettings::getDirectory() const
@ -247,7 +261,11 @@ PDFViewerSettings::Settings::Settings() :
m_magnifierZoom(2.0),
m_maximumUndoSteps(5),
m_maximumRedoSteps(5),
m_formAppearanceFlags(pdf::PDFFormManager::getDefaultApperanceFlags())
m_formAppearanceFlags(pdf::PDFFormManager::getDefaultApperanceFlags()),
m_signatureVerificationEnabled(true),
m_signatureTreatWarningsAsErrors(false),
m_signatureIgnoreCertificateValidityTime(false),
m_signatureUseSystemStore(true)
{
}

View File

@ -83,6 +83,12 @@ public:
// Form settings
pdf::PDFFormManager::FormAppearanceFlags m_formAppearanceFlags;
// Signature settings
bool m_signatureVerificationEnabled;
bool m_signatureTreatWarningsAsErrors;
bool m_signatureIgnoreCertificateValidityTime;
bool m_signatureUseSystemStore;
};
const Settings& getSettings() const { return m_settings; }

View File

@ -59,6 +59,7 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
new QListWidgetItem(QIcon(":/resources/ui.svg"), tr("UI"), ui->optionsPagesWidget, UISettings);
new QListWidgetItem(QIcon(":/resources/speech.svg"), tr("Speech"), ui->optionsPagesWidget, SpeechSettings);
new QListWidgetItem(QIcon(":/resources/form-settings.svg"), tr("Forms"), ui->optionsPagesWidget, FormSettings);
new QListWidgetItem(QIcon(":/resources/signature.svg"), tr("Signature"), ui->optionsPagesWidget, SignatureSettings);
ui->renderingEngineComboBox->addItem(tr("Software"), static_cast<int>(pdf::RendererEngine::Software));
ui->renderingEngineComboBox->addItem(tr("Hardware accelerated (OpenGL)"), static_cast<int>(pdf::RendererEngine::OpenGL));
@ -198,6 +199,10 @@ void PDFViewerSettingsDialog::on_optionsPagesWidget_currentItemChanged(QListWidg
ui->stackedWidget->setCurrentWidget(ui->formPage);
break;
case SignatureSettings:
ui->stackedWidget->setCurrentWidget(ui->signaturePage);
break;
default:
Q_ASSERT(false);
break;
@ -327,6 +332,12 @@ void PDFViewerSettingsDialog::loadData()
// Form Settings
ui->formHighlightFieldsCheckBox->setChecked(m_settings.m_formAppearanceFlags.testFlag(pdf::PDFFormManager::HighlightFields));
ui->formHighlightRequiredFieldsCheckBox->setChecked(m_settings.m_formAppearanceFlags.testFlag(pdf::PDFFormManager::HighlightRequiredFields));
// Signature Settings
ui->signatureVerificationEnableCheckBox->setChecked(m_settings.m_signatureVerificationEnabled);
ui->signatureStrictModeEnabledCheckBox->setChecked(m_settings.m_signatureTreatWarningsAsErrors);
ui->signatureIgnoreExpiredCheckBox->setChecked(m_settings.m_signatureIgnoreCertificateValidityTime);
ui->signatureUseSystemCertificateStoreCheckBox->setChecked(m_settings.m_signatureUseSystemStore);
}
void PDFViewerSettingsDialog::saveData()
@ -514,6 +525,22 @@ void PDFViewerSettingsDialog::saveData()
{
m_settings.m_maximumRedoSteps = ui->maximumRedoStepsEdit->value();
}
else if (sender == ui->signatureVerificationEnableCheckBox)
{
m_settings.m_signatureVerificationEnabled = ui->signatureVerificationEnableCheckBox->isChecked();
}
else if (sender == ui->signatureStrictModeEnabledCheckBox)
{
m_settings.m_signatureTreatWarningsAsErrors = ui->signatureStrictModeEnabledCheckBox->isChecked();
}
else if (sender == ui->signatureIgnoreExpiredCheckBox)
{
m_settings.m_signatureIgnoreCertificateValidityTime = ui->signatureIgnoreExpiredCheckBox->isChecked();
}
else if (sender == ui->signatureUseSystemCertificateStoreCheckBox)
{
m_settings.m_signatureUseSystemStore = ui->signatureUseSystemCertificateStoreCheckBox->isChecked();
}
const bool loadData = !qobject_cast<const QDoubleSpinBox*>(sender) && !qobject_cast<const QSpinBox*>(sender);
if (loadData)

View File

@ -71,7 +71,8 @@ public:
SecuritySettings,
UISettings,
SpeechSettings,
FormSettings
FormSettings,
SignatureSettings
};
const PDFViewerSettings::Settings& getSettings() const { return m_settings; }

View File

@ -26,7 +26,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
<number>10</number>
</property>
<widget class="QWidget" name="enginePage">
<layout class="QVBoxLayout" name="enginePageLayout">
@ -1176,6 +1176,156 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="signaturePage">
<layout class="QVBoxLayout" name="signaturePageLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="signatureGroupBox">
<property name="title">
<string>Digital Signature Verification</string>
</property>
<layout class="QVBoxLayout" name="signatureGroupBoxLayout">
<item>
<layout class="QGridLayout" name="signatureWidgetsLayout">
<item row="2" column="0">
<widget class="QLabel" name="signatureIgnoreExpiredLabel">
<property name="text">
<string>Ignore expired certificates</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="signatureVerificationLabel">
<property name="text">
<string>Signature verification</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="signatureStrictModeLabel">
<property name="text">
<string>Strict mode</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="signatureVerificationEnableCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="signatureStrictModeEnabledCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="signatureUseSystemCertificateStoreLabel">
<property name="text">
<string>Use system certificate store</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="signatureIgnoreExpiredCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="signatureUseSystemCertificateStoreCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="signatureCertificatesTableLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Trusted certificate store&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="trustedCertificateStoreTableWidget"/>
</item>
<item>
<layout class="QHBoxLayout" name="trustedCertificateStoreButtonsLayout">
<item>
<spacer name="trustedCertificateStoreButtonsSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="removeCertificateButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="trustedCertificateStoreDownloadEUTLButton">
<property name="text">
<string>Download EUTL</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="signatureInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Settings for verification of digital signatures. Digital signatures are verified as strict, as possible, to avoid any malicious content or signature manipulation. Verification can also be turned off, if it is not wanted. When &lt;span style=&quot; font-weight:600;&quot;&gt;Strict mode&lt;/span&gt; is turned on, every warning is treated as error. You can also ignore certificate expiration date, but do this only, if you know what you are doing, because it is dangerous. For verification, list of trusted certificates is used. System certificates can be inserted into the list, and also you can manage your own list of trusted certificates. Also, certificates from EUTL (EU trusted list) can be downloaded into the trusted certificate storage.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30mm"
height="30mm"
viewBox="0 0 30 30"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="signature.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect831"
is_visible="true" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="166.75422"
inkscape:cy="113.44001"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2035"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
<dc:creator>
<cc:Agent>
<dc:title>Jakub Melka</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Vrstva 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-267)">
<g
aria-label="✍"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25.39999962px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
id="text837">
<path
d="m 24.320405,282.11063 h 4.7625 v 12.47675 H 2.7899365 q -0.471289,0 -0.8805664,-0.17363 -0.396875,-0.17363 -0.7069336,-0.48369 -0.29765621,-0.29766 -0.47128902,-0.70693 -0.17363281,-0.39688 -0.17363281,-0.86817 0,-0.59531 0.24804687,-1.00459 0.24804686,-0.40928 0.64492186,-0.80615 v -1.15342 l 1.5875,-1.5875 q -0.3348633,-0.47129 -0.5208984,-1.00459 -0.1736329,-0.5457 -0.1736329,-1.12861 0,-0.70693 0.2604493,-1.35186 0.2728515,-0.65732 0.7813476,-1.16582 l 5.3454101,-5.34541 q 0.5084961,-0.50849 1.1534179,-0.76894 0.657324,-0.27285 1.364258,-0.27285 h 2.827734 l 6.98252,-6.98252 4.303613,4.30361 -4.526856,4.52686 z m -0.744141,1.78593 -4.638476,-4.65088 q -0.111621,0.11163 -0.496094,0.4961 -0.37207,0.38447 -0.880566,0.89297 -0.496094,0.50849 -1.066602,1.079 -0.558105,0.57051 -1.041797,1.0542 -0.483691,0.48369 -0.818554,0.81856 -0.334864,0.32246 -0.396875,0.35966 0.384472,0.13643 0.483691,0.47129 0.111621,0.32246 0.111621,0.68213 0,0.14883 -0.0124,0.29766 0,0.13642 0,0.27285 0,0.74414 -0.285254,1.40146 -0.272852,0.64493 -0.756543,1.12862 -0.483691,0.48369 -1.141016,0.76894 -0.644922,0.27285 -1.389062,0.27285 H 7.6888622 q -0.3720703,0 -0.6945312,0.13643 -0.322461,0.14883 -0.5705078,0.38447 -0.2356446,0.24805 -0.3844727,0.57051 -0.1364258,0.32246 -0.1364258,0.69453 0,0.33486 0.1364258,0.68213 0.062012,0.0992 0.1116211,0.18604 0.049609,0.0992 0.1116211,0.19843 0.2480469,0.32246 0.6325195,0.5085 0.396875,0.19844 0.79375,0.19844 H 27.296967 v -8.90489 z m -18.566308,8.01192 0.1240234,-0.11162 q -0.1240234,-0.35967 -0.1240234,-0.76895 0,-0.5581 0.2108398,-1.0418 0.2108399,-0.48369 0.5705079,-0.84335 0.3720703,-0.37208 0.8557617,-0.58292 0.4836914,-0.21083 1.0417968,-0.21083 H 8.581831 q 3.150195,-3.1254 6.238379,-6.22598 3.100586,-3.11299 6.238379,-6.25078 l -1.785938,-1.78594 q -4.005957,4.00596 -8.024316,8.01191 -4.0183595,4.00596 -8.0119141,8.03672 v 1.77354 z m 6.238379,-13.36973 q -0.731738,0 -1.2526369,0.5209 l -5.3454101,5.35781 q -0.2480468,0.24805 -0.396875,0.58291 -0.1364257,0.32246 -0.1364257,0.68213 0,0.48369 0.2232422,0.84336 0.2356445,0.35967 0.5953124,0.64492 l 8.6196291,-8.63203 z m 9.810254,-6.23838 -1.153418,1.15342 1.785937,1.78594 1.153418,-1.15342 z"
style="stroke-width:0.26458332"
id="path839"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB