mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Issue #55: Windows implementation
This commit is contained in:
@ -116,9 +116,16 @@ void PDFCertificateManager::createCertificate(const NewCertificateInfo& info)
|
|||||||
QByteArray privateKeyPaswordUtf8 = info.privateKeyPasword.toUtf8();
|
QByteArray privateKeyPaswordUtf8 = info.privateKeyPasword.toUtf8();
|
||||||
|
|
||||||
// Write the data
|
// Write the data
|
||||||
openssl_ptr<PKCS12> pkcs12(PKCS12_init(NID_pkcs7_data), &PKCS12_free);
|
openssl_ptr<PKCS12> pkcs12(PKCS12_create(privateKeyPaswordUtf8.constData(),
|
||||||
PKCS12_add_cert(&pkcs12->certs, certificate.get());
|
nullptr,
|
||||||
PKCS12_add_key(&pkcs12->keybags, privateKey.get(), 0, PKCS12_DEFAULT_ITER, NID_pbe_WithSHA1And3_Key_TripleDES_CBC, privateKeyPaswordUtf8.data()));
|
privateKey.get(),
|
||||||
|
certificate.get(),
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
PKCS12_DEFAULT_ITER,
|
||||||
|
PKCS12_DEFAULT_ITER,
|
||||||
|
0), &PKCS12_free);
|
||||||
i2d_PKCS12_bio(pksBuffer.get(), pkcs12.get());
|
i2d_PKCS12_bio(pksBuffer.get(), pkcs12.get());
|
||||||
|
|
||||||
BUF_MEM* pksMemoryBuffer = nullptr;
|
BUF_MEM* pksMemoryBuffer = nullptr;
|
||||||
@ -139,11 +146,7 @@ void PDFCertificateManager::createCertificate(const NewCertificateInfo& info)
|
|||||||
|
|
||||||
PDFCertificateEntries PDFCertificateManager::getCertificates()
|
PDFCertificateEntries PDFCertificateManager::getCertificates()
|
||||||
{
|
{
|
||||||
PDFCertificateEntries entries;
|
PDFCertificateEntries entries = PDFCertificateStore::getPersonalCertificates();
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
entries = PDFCertificateStore::getPersonalCertificates();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QDir directory(getCertificateDirectory());
|
QDir directory(getCertificateDirectory());
|
||||||
QFileInfoList pfxFiles = directory.entryInfoList(QStringList() << "*.pfx", QDir::Files | QDir::NoDotAndDotDot | QDir::Readable, QDir::Name);
|
QFileInfoList pfxFiles = directory.entryInfoList(QStringList() << "*.pfx", QDir::Files | QDir::NoDotAndDotDot | QDir::Readable, QDir::Name);
|
||||||
@ -161,25 +164,31 @@ PDFCertificateEntries PDFCertificateManager::getCertificates()
|
|||||||
openssl_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(pksBuffer.get(), nullptr), &PKCS12_free);
|
openssl_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(pksBuffer.get(), nullptr), &PKCS12_free);
|
||||||
if (pkcs12)
|
if (pkcs12)
|
||||||
{
|
{
|
||||||
EVP_PKEY* key = nullptr;
|
|
||||||
X509* certificatePtr = nullptr;
|
X509* certificatePtr = nullptr;
|
||||||
STACK_OF(X509)* certificates = nullptr;
|
|
||||||
|
PDFCertificateEntry entry;
|
||||||
|
|
||||||
// Parse PKCS12 with password
|
// Parse PKCS12 with password
|
||||||
bool isParsed = PKCS12_parse(pkcs12.get(), nullptr, &key, &certificatePtr, &certificates) == 1;
|
bool isParsed = PKCS12_parse(pkcs12.get(), nullptr, nullptr, &certificatePtr, nullptr) == 1;
|
||||||
if (isParsed)
|
if (isParsed)
|
||||||
{
|
{
|
||||||
std::optional<PDFCertificateInfo> info = PDFCertificateInfo::getCertificateInfo(certificatePtr);
|
std::optional<PDFCertificateInfo> info = PDFCertificateInfo::getCertificateInfo(certificatePtr);
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
PDFCertificateEntry entry;
|
|
||||||
entry.type = PDFCertificateEntry::EntryType::System;
|
entry.type = PDFCertificateEntry::EntryType::System;
|
||||||
entry.info = qMove(*info);
|
entry.info = qMove(*info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certificatePtr)
|
||||||
|
{
|
||||||
|
X509_free(certificatePtr);
|
||||||
|
}
|
||||||
|
|
||||||
entry.pkcs12 = data;
|
entry.pkcs12 = data;
|
||||||
|
entry.pkcs12fileName = fileInfo.fileName();
|
||||||
entries.emplace_back(qMove(entry));
|
entries.emplace_back(qMove(entry));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
@ -215,10 +224,10 @@ QString PDFCertificateManager::generateCertificateFileName()
|
|||||||
|
|
||||||
bool PDFCertificateManager::isCertificateValid(const PDFCertificateEntry& certificateEntry, QString password)
|
bool PDFCertificateManager::isCertificateValid(const PDFCertificateEntry& certificateEntry, QString password)
|
||||||
{
|
{
|
||||||
QByteArray data = certificateEntry.info.getCertificateData();
|
QByteArray pkcs12data = certificateEntry.pkcs12;
|
||||||
|
|
||||||
openssl_ptr<BIO> pksBuffer(BIO_new(BIO_s_mem()), &BIO_free_all);
|
openssl_ptr<BIO> pksBuffer(BIO_new(BIO_s_mem()), &BIO_free_all);
|
||||||
BIO_write(pksBuffer.get(), data.constData(), data.length());
|
BIO_write(pksBuffer.get(), pkcs12data.constData(), pkcs12data.length());
|
||||||
|
|
||||||
openssl_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(pksBuffer.get(), nullptr), &PKCS12_free);
|
openssl_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(pksBuffer.get(), nullptr), &PKCS12_free);
|
||||||
if (pkcs12)
|
if (pkcs12)
|
||||||
@ -233,17 +242,19 @@ bool PDFCertificateManager::isCertificateValid(const PDFCertificateEntry& certif
|
|||||||
return PKCS12_parse(pkcs12.get(), passwordPointer, nullptr, nullptr, nullptr) == 1;
|
return PKCS12_parse(pkcs12.get(), passwordPointer, nullptr, nullptr, nullptr) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return pkcs12data.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PDFSignatureFactory::sign(const PDFCertificateEntry& certificateEntry, QString password, QByteArray data, QByteArray& result)
|
bool PDFSignatureFactory::sign(const PDFCertificateEntry& certificateEntry, QString password, QByteArray data, QByteArray& result)
|
||||||
{
|
{
|
||||||
QByteArray certificateData = certificateEntry.pkcs12;
|
QByteArray pkcs12Data = certificateEntry.pkcs12;
|
||||||
|
|
||||||
openssl_ptr<BIO> certificateBuffer(BIO_new(BIO_s_mem()), &BIO_free_all);
|
if (!pkcs12Data.isEmpty())
|
||||||
BIO_write(certificateBuffer.get(), certificateData.constData(), certificateData.length());
|
{
|
||||||
|
openssl_ptr<BIO> pkcs12Buffer(BIO_new(BIO_s_mem()), &BIO_free_all);
|
||||||
|
BIO_write(pkcs12Buffer.get(), pkcs12Data.constData(), pkcs12Data.length());
|
||||||
|
|
||||||
openssl_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(certificateBuffer.get(), nullptr), &PKCS12_free);
|
openssl_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(pkcs12Buffer.get(), nullptr), &PKCS12_free);
|
||||||
if (pkcs12)
|
if (pkcs12)
|
||||||
{
|
{
|
||||||
const char* passwordPointer = nullptr;
|
const char* passwordPointer = nullptr;
|
||||||
@ -284,12 +295,113 @@ bool PDFSignatureFactory::sign(const PDFCertificateEntry& certificateEntry, QStr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return signImpl_Win(certificateEntry, password, data, result);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
#include <ncrypt.h>
|
||||||
|
#if defined(PDF4QT_USE_PRAGMA_LIB)
|
||||||
|
#pragma comment(lib, "crypt32.lib")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool pdf::PDFSignatureFactory::signImpl_Win(const pdf::PDFCertificateEntry& certificateEntry, QString password, QByteArray data, QByteArray& result)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Q_UNUSED(password);
|
||||||
|
|
||||||
|
HCERTSTORE certStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
|
||||||
|
if (certStore)
|
||||||
|
{
|
||||||
|
PCCERT_CONTEXT context = nullptr;
|
||||||
|
|
||||||
|
while (context = CertEnumCertificatesInStore(certStore, context))
|
||||||
|
{
|
||||||
|
const unsigned char* pointer = context->pbCertEncoded;
|
||||||
|
QByteArray testData(reinterpret_cast<const char*>(pointer), context->cbCertEncoded);
|
||||||
|
|
||||||
|
if (testData == certificateEntry.info.getCertificateData())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
{
|
||||||
|
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv{};
|
||||||
|
if (CryptAcquireCertificatePrivateKey(context, CRYPT_ACQUIRE_SILENT_FLAG, nullptr, &hCryptProv, NULL, NULL))
|
||||||
|
{
|
||||||
|
HCRYPTHASH hHash{};
|
||||||
|
|
||||||
|
CryptCreateHash(hCryptProv, CALG_SHA_256, 0, 0, &hHash);
|
||||||
|
if (!hHash)
|
||||||
|
{
|
||||||
|
CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hHash)
|
||||||
|
{
|
||||||
|
const BYTE* pDataToSign = reinterpret_cast<const BYTE*>(data.constData());
|
||||||
|
const DWORD dwDataLen = data.size();
|
||||||
|
|
||||||
|
// Hash the data
|
||||||
|
if (CryptHashData(hHash, pDataToSign, dwDataLen, 0))
|
||||||
|
{
|
||||||
|
DWORD dwSigLen = 0;
|
||||||
|
|
||||||
|
// Retrieve length of signature
|
||||||
|
if (CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &dwSigLen))
|
||||||
|
{
|
||||||
|
// Allocate memory for signature
|
||||||
|
BYTE* pbSignature = new BYTE[dwSigLen];
|
||||||
|
if(pbSignature != nullptr)
|
||||||
|
{
|
||||||
|
// Sign the hash
|
||||||
|
if(CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, pbSignature, &dwSigLen))
|
||||||
|
{
|
||||||
|
result = QByteArray(reinterpret_cast<const char*>(pbSignature), dwSigLen);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
delete[] pbSignature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptDestroyHash(hHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptReleaseContext(hCryptProv, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CertFreeCertificateContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(certificateEntry);
|
||||||
|
Q_UNUSED(password);
|
||||||
|
Q_UNUSED(data);
|
||||||
|
Q_UNUSED(result);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(PDF4QT_COMPILER_MINGW) || defined(PDF4QT_COMPILER_GCC)
|
#if defined(PDF4QT_COMPILER_MINGW) || defined(PDF4QT_COMPILER_GCC)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,6 +60,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFSignatureFactory
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool sign(const PDFCertificateEntry& certificateEntry, QString password, QByteArray data, QByteArray& result);
|
static bool sign(const PDFCertificateEntry& certificateEntry, QString password, QByteArray data, QByteArray& result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool signImpl_Win(const PDFCertificateEntry& certificateEntry, QString password, QByteArray data, QByteArray& result);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -182,6 +182,7 @@ struct PDFCertificateEntry
|
|||||||
EntryType type = EntryType::User;
|
EntryType type = EntryType::User;
|
||||||
PDFCertificateInfo info;
|
PDFCertificateInfo info;
|
||||||
QByteArray pkcs12;
|
QByteArray pkcs12;
|
||||||
|
QString pkcs12fileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PDFCertificateEntries = std::vector<PDFCertificateEntry>;
|
using PDFCertificateEntries = std::vector<PDFCertificateEntry>;
|
||||||
|
@ -71,11 +71,21 @@ void PDFCertificateListHelper::fillComboBox(QComboBox* comboBox, const PDFCertif
|
|||||||
secondInfoEntry = entry.info.getName(PDFCertificateInfo::NameEntry::Email);
|
secondInfoEntry = entry.info.getName(PDFCertificateInfo::NameEntry::Email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.pkcs12fileName.isEmpty())
|
||||||
|
{
|
||||||
model->setItem(i, 0, new QStandardItem(entry.info.getName(PDFCertificateInfo::NameEntry::CommonName)));
|
model->setItem(i, 0, new QStandardItem(entry.info.getName(PDFCertificateInfo::NameEntry::CommonName)));
|
||||||
model->setItem(i, 1, new QStandardItem(secondInfoEntry));
|
model->setItem(i, 1, new QStandardItem(secondInfoEntry));
|
||||||
model->setItem(i, 2, new QStandardItem(entry.info.getNotValidBefore().toLocalTime().toString()));
|
model->setItem(i, 2, new QStandardItem(entry.info.getNotValidBefore().toLocalTime().toString()));
|
||||||
model->setItem(i, 3, new QStandardItem(entry.info.getNotValidAfter().toLocalTime().toString()));
|
model->setItem(i, 3, new QStandardItem(entry.info.getNotValidAfter().toLocalTime().toString()));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
model->setItem(i, 0, new QStandardItem(entry.pkcs12fileName));
|
||||||
|
model->setItem(i, 1, new QStandardItem(tr("Password protected")));
|
||||||
|
model->setItem(i, 2, new QStandardItem(QString()));
|
||||||
|
model->setItem(i, 3, new QStandardItem(QString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
comboBox->setUpdatesEnabled(updatesEnabled);
|
comboBox->setUpdatesEnabled(updatesEnabled);
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ namespace pdf
|
|||||||
|
|
||||||
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCertificateListHelper
|
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCertificateListHelper
|
||||||
{
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(pdf::PDFCertificateListHelper)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PDFCertificateListHelper() = delete;
|
PDFCertificateListHelper() = delete;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user