diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/SignaturePlugin.pro b/Pdf4QtViewerPlugins/SignaturePlugin/SignaturePlugin.pro index cdf7725..9f4e2c9 100644 --- a/Pdf4QtViewerPlugins/SignaturePlugin/SignaturePlugin.pro +++ b/Pdf4QtViewerPlugins/SignaturePlugin/SignaturePlugin.pro @@ -32,10 +32,23 @@ DESTDIR = $$OUT_PWD/../../pdfplugins CONFIG += c++11 +Pdf4Qt_OPENSSL_PATH = $$absolute_path(../../Tools, $$[QT_INSTALL_PREFIX]) + +# Link OpenSSL +LIBS += -L$$Pdf4Qt_OPENSSL_PATH/OpenSSL/Win_x64/bin -L$$Pdf4Qt_OPENSSL_PATH/OpenSSL/Win_x64/lib -llibcrypto -llibssl +INCLUDEPATH += $$Pdf4Qt_OPENSSL_PATH/OpenSSL/Win_x64/include +DEPENDPATH += $$Pdf4Qt_OPENSSL_PATH/OpenSSL/Win_x64/include + SOURCES += \ + certificatemanager.cpp \ + certificatemanagerdialog.cpp \ + createcertificatedialog.cpp \ signatureplugin.cpp HEADERS += \ + certificatemanager.h \ + certificatemanagerdialog.h \ + createcertificatedialog.h \ signatureplugin.h CONFIG += force_debug_info @@ -46,3 +59,7 @@ DISTFILES += \ RESOURCES += \ icons.qrc +FORMS += \ + certificatemanagerdialog.ui \ + createcertificatedialog.ui + diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanager.cpp b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanager.cpp new file mode 100644 index 0000000..8158178 --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanager.cpp @@ -0,0 +1,112 @@ +// Copyright (C) 2022 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "certificatemanager.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pdfplugin +{ + +CertificateManager::CertificateManager() +{ + +} + +template +using openssl_ptr = std::unique_ptr; + +void CertificateManager::createCertificate(const NewCertificateInfo& info) +{ + openssl_ptr certificateBuffer(BIO_new(BIO_s_mem()), &BIO_free_all); + openssl_ptr privateKeyBuffer(BIO_new(BIO_s_mem()), &BIO_free_all); + + if (certificateBuffer && privateKeyBuffer) + { + openssl_ptr bignumber(BN_new(), &BN_free); + openssl_ptr rsaKey(RSA_new(), &RSA_free); + + BN_set_word(bignumber.get(), RSA_F4); + const int rsaResult = RSA_generate_key_ex(rsaKey.get(), info.rsaKeyLength, bignumber.get(), nullptr); + if (rsaResult) + { + openssl_ptr certificate(X509_new(), &X509_free); + openssl_ptr privateKey(EVP_PKEY_new(), &EVP_PKEY_free); + + EVP_PKEY_set1_RSA(privateKey.get(), rsaKey.get()); + ASN1_INTEGER* serialNumber = X509_get_serialNumber(certificate.get()); + ASN1_INTEGER_set(serialNumber, info.serialNumber); + + // Set validity of the certificate + X509_gmtime_adj(X509_getm_notBefore(certificate.get()), 0); + X509_gmtime_adj(X509_getm_notBefore(certificate.get()), info.validityInSeconds); + + // Set name + X509_NAME* name = X509_get_subject_name(certificate.get()); + + auto addString = [name](const char* identifier, QString string) + { + if (string.isEmpty()) + { + return; + } + + QByteArray stringUtf8 = string.toUtf8(); + X509_NAME_add_entry_by_txt(name, identifier, MBSTRING_UTF8, reinterpret_cast(stringUtf8.constData()), stringUtf8.length(), -1, 0); + }; + addString("C", info.certCountryCode); + addString("O", info.certOrganization); + addString("OU", info.certOrganizationUnit); + addString("CN", info.certCommonName); + addString("E", info.certEmail); + + X509_EXTENSION* extension = nullptr; + X509V3_CTX context; + X509V3_set_ctx_nodb(&context); + X509V3_set_ctx(&context, certificate.get(), certificate.get(), nullptr, nullptr, 0); + extension = X509V3_EXT_conf_nid (NULL, &context, NID_key_usage, "digitalSignature, keyAgreement"); + + X509_set_issuer_name(certificate.get(), name); + + // Set public key + X509_set_pubkey(certificate.get(), privateKey.get()); + X509_sign(certificate.get(), privateKey.get(), EVP_sha512()); + + // Private key password + QByteArray privateKeyPaswordUtf8 = info.privateKeyPasword.toUtf8(); + + // Write the data + const int retWritePrivateKey = PEM_write_bio_PKCS8PrivateKey(privateKeyBuffer.get(), privateKey.get(), EVP_aes_256_cbc(), privateKeyPaswordUtf8.data(), privateKeyPaswordUtf8.size(), nullptr, nullptr); + const int retWriteCertificate = PEM_write_bio_X509(certificateBuffer.get(), certificate.get()); + + if (retWritePrivateKey == 1 && retWriteCertificate == 1) + { + + } + } + } +} + +} // namespace pdfplugin diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanager.h b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanager.h new file mode 100644 index 0000000..2aa706a --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanager.h @@ -0,0 +1,53 @@ +// Copyright (C) 2022 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef CERTIFICATEMANAGER_H +#define CERTIFICATEMANAGER_H + +#include + +namespace pdfplugin +{ + +class CertificateManager +{ +public: + CertificateManager(); + + struct NewCertificateInfo + { + QString certificateFileName; + QString privateKeyFileName; + QString privateKeyPasword; + + QString certCountryCode; + QString certOrganization; + QString certOrganizationUnit; + QString certCommonName; + QString certEmail; + + int rsaKeyLength = 1024; + int validityInSeconds = 2 * 365 * 24 * 3600; + long serialNumber = 1; + }; + + void createCertificate(const NewCertificateInfo& info); +}; + +} // namespace pdfplugin + +#endif // CERTIFICATEMANAGER_H diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.cpp b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.cpp new file mode 100644 index 0000000..946f61b --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.cpp @@ -0,0 +1,61 @@ +// Copyright (C) 2022 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "certificatemanagerdialog.h" +#include "ui_certificatemanagerdialog.h" +#include "createcertificatedialog.h" + +#include "pdfwidgetutils.h" + +#include +#include + +namespace pdfplugin +{ + +CertificateManagerDialog::CertificateManagerDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CertificateManagerDialog), + m_newCertificateButton(nullptr), + m_openCertificateDirectoryButton(nullptr) +{ + ui->setupUi(this); + + m_newCertificateButton = ui->buttonBox->addButton(tr("Create Certificate"), QDialogButtonBox::ActionRole); + m_openCertificateDirectoryButton = ui->buttonBox->addButton(tr("Show Certificate Directory"), QDialogButtonBox::ActionRole); + + connect(m_newCertificateButton, &QPushButton::clicked, this, &CertificateManagerDialog::onNewCertificateClicked); + + setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(640, 480))); +} + +CertificateManagerDialog::~CertificateManagerDialog() +{ + delete ui; +} + +void CertificateManagerDialog::onNewCertificateClicked() +{ + CreateCertificateDialog dialog(this); + if (dialog.exec() == CreateCertificateDialog::Accepted) + { + const CertificateManager::NewCertificateInfo info = dialog.getNewCertificateInfo(); + m_certificateManager.createCertificate(info); + } +} + +} // namespace pdfplugin diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.h b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.h new file mode 100644 index 0000000..ac74b45 --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.h @@ -0,0 +1,54 @@ +// Copyright (C) 2022 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef CERTIFICATEMANAGERDIALOG_H +#define CERTIFICATEMANAGERDIALOG_H + +#include "certificatemanager.h" + +#include + +class QAction; + +namespace Ui +{ +class CertificateManagerDialog; +} + +namespace pdfplugin +{ + +class CertificateManagerDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CertificateManagerDialog(QWidget* parent); + virtual ~CertificateManagerDialog() override; + +private: + void onNewCertificateClicked(); + + Ui::CertificateManagerDialog* ui; + CertificateManager m_certificateManager; + QPushButton* m_newCertificateButton; + QPushButton* m_openCertificateDirectoryButton; +}; + +} // namespace pdfplugin + +#endif // CERTIFICATEMANAGERDIALOG_H diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.ui b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.ui new file mode 100644 index 0000000..5ff7748 --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/certificatemanagerdialog.ui @@ -0,0 +1,71 @@ + + + CertificateManagerDialog + + + + 0 + 0 + 789 + 511 + + + + Certificate Manager + + + + + + Certificates + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + CertificateManagerDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CertificateManagerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.cpp b/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.cpp new file mode 100644 index 0000000..f4bbb74 --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.cpp @@ -0,0 +1,156 @@ +// Copyright (C) 2022 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "createcertificatedialog.h" +#include "ui_createcertificatedialog.h" + +#include +#include +#include +#include + +namespace pdfplugin +{ + +CreateCertificateDialog::CreateCertificateDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CreateCertificateDialog) +{ + ui->setupUi(this); + + ui->keyLengthCombo->addItem(tr("1024 bits"), 1024); + ui->keyLengthCombo->addItem(tr("2048 bits"), 2048); + ui->keyLengthCombo->addItem(tr("4096 bits"), 4096); + ui->keyLengthCombo->setCurrentIndex(ui->keyLengthCombo->findData(2048)); + + QList locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); + std::sort(locales.begin(), locales.end(), [](const QLocale& left, const QLocale& right) { return QString::compare(left.nativeCountryName(), right.nativeCountryName(), Qt::CaseInsensitive) < 0; }); + + int currentIndex = 0; + QLocale currentLocale = QLocale::system(); + + for (const QLocale& locale : locales) + { + if (locale.country() == QLocale::AnyCountry) + { + continue; + } + + if (locale.nativeCountryName().isEmpty()) + { + continue; + } + + QString localeName = locale.name(); + QString countryCode = localeName.split(QChar('_')).back(); + QString text = QString("%1 | %2").arg(countryCode, locale.nativeCountryName()); + + if (ui->countryCombo->findText(text) >= 0) + { + continue; + } + + if (locale.bcp47Name() == currentLocale.bcp47Name()) + { + currentIndex = ui->countryCombo->count(); + } + + ui->countryCombo->addItem(text, countryCode); + } + + ui->countryCombo->setCurrentIndex(currentIndex); + ui->countryCombo->setMaxVisibleItems(25); + + QDate minDate = QDate::currentDate(); + ui->validTillEdit->setMinimumDate(minDate); + + QDate selectedDate = minDate; + selectedDate = selectedDate.addYears(5, ui->validTillEdit->calendar()); + ui->validTillEdit->setSelectedDate(selectedDate); +} + +CreateCertificateDialog::~CreateCertificateDialog() +{ + delete ui; +} + +void CreateCertificateDialog::accept() +{ + if (validate()) + { + bool ok = false; + QString password1 = QInputDialog::getText(this, tr("Certificate Protection"), tr("Enter password to protect your certificate."), QLineEdit::Password, QString(), &ok); + + if (!ok) + { + return; + } + + QString password2 = QInputDialog::getText(this, tr("Certificate Protection"), tr("Enter password again to verify password text."), QLineEdit::Password, QString(), &ok); + + if (password1 != password2) + { + QMessageBox::critical(this, tr("Error"), tr("Reentered password is not equal to the first one!")); + return; + } + + QDate date = ui->validTillEdit->selectedDate(); + QDate currentDate = QDate::currentDate(); + int days = currentDate.daysTo(date); + + // Fill certificate info + m_newCertificateInfo.privateKeyPasword = password1; + m_newCertificateInfo.certCountryCode = ui->countryCombo->currentData().toString(); + m_newCertificateInfo.certOrganization = ui->organizationEdit->text(); + m_newCertificateInfo.certOrganizationUnit = ui->organizationUnitEdit->text(); + m_newCertificateInfo.certCommonName = ui->commonNameEdit->text(); + m_newCertificateInfo.certEmail = ui->emailEdit->text(); + m_newCertificateInfo.rsaKeyLength = ui->keyLengthCombo->currentData().toInt(); + m_newCertificateInfo.validityInSeconds = days * 24 * 3600; + + BaseClass::accept(); + } +} + +bool CreateCertificateDialog::validate() +{ + // validate empty text fields + if (ui->commonNameEdit->text().isEmpty()) + { + QMessageBox::critical(this, tr("Error"), tr("Please enter a name!")); + ui->commonNameEdit->setFocus(); + return false; + } + + if (ui->organizationEdit->text().isEmpty()) + { + QMessageBox::critical(this, tr("Error"), tr("Please enter an organization name!")); + ui->organizationEdit->setFocus(); + return false; + } + + if (ui->emailEdit->text().isEmpty()) + { + QMessageBox::critical(this, tr("Error"), tr("Please enter an email address!")); + ui->emailEdit->setFocus(); + return false; + } + + return true; +} + +} // namespace plugin diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.h b/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.h new file mode 100644 index 0000000..173323f --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.h @@ -0,0 +1,59 @@ +// Copyright (C) 2022 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef CREATECERTIFICATEDIALOG_H +#define CREATECERTIFICATEDIALOG_H + +#include "certificatemanager.h" + +#include + +namespace Ui +{ +class CreateCertificateDialog; +} + +namespace pdfplugin +{ + +class CreateCertificateDialog : public QDialog +{ + Q_OBJECT + +private: + using BaseClass = QDialog; + +public: + explicit CreateCertificateDialog(QWidget* parent); + virtual ~CreateCertificateDialog() override; + + const CertificateManager::NewCertificateInfo& getNewCertificateInfo() const { return m_newCertificateInfo; } + +public slots: + virtual void accept() override; + +private: + bool validate(); + + CertificateManager::NewCertificateInfo m_newCertificateInfo; + + Ui::CreateCertificateDialog* ui; +}; + +} // namespace plugin + +#endif // CREATECERTIFICATEDIALOG_H diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.ui b/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.ui new file mode 100644 index 0000000..0ea5165 --- /dev/null +++ b/Pdf4QtViewerPlugins/SignaturePlugin/createcertificatedialog.ui @@ -0,0 +1,172 @@ + + + CreateCertificateDialog + + + + 0 + 0 + 514 + 488 + + + + Create Certificate + + + + + + Create Self Signed Certificate + + + + + + Name + + + + + + + true + + + + + + + Organization + + + + + + + Organization Unit + + + + + + + Email + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + Country + + + + + + + Key length + + + + + + + Valid till + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + CreateCertificateDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CreateCertificateDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.cpp b/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.cpp index 9cb4f6f..d23beb2 100644 --- a/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.cpp +++ b/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.cpp @@ -21,6 +21,7 @@ #include "pdfpagecontenteditorwidget.h" #include "pdfpagecontenteditorstylesettings.h" #include "pdfdocumentbuilder.h" +#include "certificatemanagerdialog.h" #include #include @@ -155,6 +156,7 @@ void SignaturePlugin::setWidget(pdf::PDFWidget* widget) connect(clearAction, &QAction::triggered, &m_scene, &pdf::PDFPageContentScene::clear); connect(activateAction, &QAction::triggered, this, &SignaturePlugin::setActive); connect(signElectronicallyAction, &QAction::triggered, this, &SignaturePlugin::onSignElectronically); + connect(certificatesAction, &QAction::triggered, this, &SignaturePlugin::onOpenCertificatesManager); updateActions(); } @@ -303,6 +305,12 @@ void SignaturePlugin::onSignElectronically() } } +void SignaturePlugin::onOpenCertificatesManager() +{ + CertificateManagerDialog dialog(m_dataExchangeInterface->getMainWindow()); + dialog.exec(); +} + void SignaturePlugin::onPenChanged(const QPen& pen) { if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) diff --git a/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.h b/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.h index e6cf7cd..8e69b2a 100644 --- a/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.h +++ b/Pdf4QtViewerPlugins/SignaturePlugin/signatureplugin.h @@ -54,6 +54,7 @@ private: void onToolActivityChanged(); void onSceneEditElement(const std::set& elements); void onSignElectronically(); + void onOpenCertificatesManager(); void onPenChanged(const QPen& pen); void onBrushChanged(const QBrush& brush);