mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Issue #55: Refactoring of certificates
This commit is contained in:
@ -60,19 +60,6 @@ namespace pdf
|
||||
template<typename T>
|
||||
using openssl_ptr = std::unique_ptr<T, void(*)(T*)>;
|
||||
|
||||
static QRecursiveMutex s_globalOpenSSLMutex;
|
||||
|
||||
/// OpenSSL is not thread safe.
|
||||
class PDFOpenSSLGlobalLock
|
||||
{
|
||||
public:
|
||||
explicit inline PDFOpenSSLGlobalLock() : m_mutexLocker(&s_globalOpenSSLMutex) { }
|
||||
inline ~PDFOpenSSLGlobalLock() = default;
|
||||
|
||||
private:
|
||||
QMutexLocker<QRecursiveMutex> m_mutexLocker;
|
||||
};
|
||||
|
||||
PDFSignatureReference PDFSignatureReference::parse(const PDFObjectStorage* storage, PDFObject object)
|
||||
{
|
||||
PDFSignatureReference result;
|
||||
@ -1569,263 +1556,7 @@ BIO* PDFSignatureHandler_adbe_pkcs7_sha1::getSignedDataBuffer(PDFSignatureVerifi
|
||||
|
||||
PDFCertificateInfo PDFPublicKeySignatureHandler::getCertificateInfo(X509* certificate)
|
||||
{
|
||||
PDFCertificateInfo info;
|
||||
|
||||
if (X509_NAME* subjectName = X509_get_subject_name(certificate))
|
||||
{
|
||||
// List of these properties are in RFC 5280, section 4.1.2.4, these attributes
|
||||
// are standard and all implementations must be prepared to process them.
|
||||
QString countryName = getStringFromX509Name(subjectName, NID_countryName);
|
||||
QString organizationName = getStringFromX509Name(subjectName, NID_organizationName);
|
||||
QString organizationalUnitName = getStringFromX509Name(subjectName, NID_organizationalUnitName);
|
||||
QString distinguishedName = getStringFromX509Name(subjectName, NID_distinguishedName);
|
||||
QString stateOrProvinceName = getStringFromX509Name(subjectName, NID_stateOrProvinceName);
|
||||
QString commonName = getStringFromX509Name(subjectName, NID_commonName);
|
||||
QString serialNumber = getStringFromX509Name(subjectName, NID_serialNumber);
|
||||
|
||||
// These attributes are defined also in section 4.1.2.4, they are not mandatory,
|
||||
// but application should be able to process them.
|
||||
QString localityName = getStringFromX509Name(subjectName, NID_localityName);
|
||||
QString title = getStringFromX509Name(subjectName, NID_title);
|
||||
QString surname = getStringFromX509Name(subjectName, NID_surname);
|
||||
QString givenName = getStringFromX509Name(subjectName, NID_givenName);
|
||||
QString initials = getStringFromX509Name(subjectName, NID_initials);
|
||||
QString pseudonym = getStringFromX509Name(subjectName, NID_pseudonym);
|
||||
QString generationQualifier = getStringFromX509Name(subjectName, NID_generationQualifier);
|
||||
|
||||
// This entry is not defined in section 4.1.2.4, but is commonly used
|
||||
QString email = getStringFromX509Name(subjectName, NID_pkcs9_emailAddress);
|
||||
|
||||
info.setName(PDFCertificateInfo::CountryName, qMove(countryName));
|
||||
info.setName(PDFCertificateInfo::OrganizationName, qMove(organizationName));
|
||||
info.setName(PDFCertificateInfo::OrganizationalUnitName, qMove(organizationalUnitName));
|
||||
info.setName(PDFCertificateInfo::DistinguishedName, qMove(distinguishedName));
|
||||
info.setName(PDFCertificateInfo::StateOrProvinceName, qMove(stateOrProvinceName));
|
||||
info.setName(PDFCertificateInfo::CommonName, qMove(commonName));
|
||||
info.setName(PDFCertificateInfo::SerialNumber, qMove(serialNumber));
|
||||
|
||||
info.setName(PDFCertificateInfo::LocalityName, qMove(localityName));
|
||||
info.setName(PDFCertificateInfo::Title, qMove(title));
|
||||
info.setName(PDFCertificateInfo::Surname, qMove(surname));
|
||||
info.setName(PDFCertificateInfo::GivenName, qMove(givenName));
|
||||
info.setName(PDFCertificateInfo::Initials, qMove(initials));
|
||||
info.setName(PDFCertificateInfo::Pseudonym, qMove(pseudonym));
|
||||
info.setName(PDFCertificateInfo::GenerationalQualifier, qMove(generationQualifier));
|
||||
|
||||
info.setName(PDFCertificateInfo::Email, qMove(email));
|
||||
|
||||
const long version = X509_get_version(certificate);
|
||||
info.setVersion(version);
|
||||
|
||||
const ASN1_TIME* notBeforeTime = X509_get0_notBefore(certificate);
|
||||
const ASN1_TIME* notAfterTime = X509_get0_notAfter(certificate);
|
||||
|
||||
info.setNotValidBefore(getDateTimeFromASN(notBeforeTime));
|
||||
info.setNotValidAfter(getDateTimeFromASN(notAfterTime));
|
||||
|
||||
X509_PUBKEY* publicKey = X509_get_X509_PUBKEY(certificate);
|
||||
EVP_PKEY* evpKey = X509_PUBKEY_get(publicKey);
|
||||
const int keyType = EVP_PKEY_type(EVP_PKEY_base_id(evpKey));
|
||||
|
||||
PDFCertificateInfo::PublicKey key = PDFCertificateInfo::KeyUnknown;
|
||||
switch (keyType)
|
||||
{
|
||||
case EVP_PKEY_RSA:
|
||||
key = PDFCertificateInfo::KeyRSA;
|
||||
break;
|
||||
|
||||
case EVP_PKEY_DSA:
|
||||
key = PDFCertificateInfo::KeyDSA;
|
||||
break;
|
||||
|
||||
case EVP_PKEY_DH:
|
||||
key = PDFCertificateInfo::KeyDH;
|
||||
break;
|
||||
|
||||
case EVP_PKEY_EC:
|
||||
key = PDFCertificateInfo::KeyEC;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
info.setPublicKey(key);
|
||||
|
||||
const int bits = EVP_PKEY_bits(evpKey);
|
||||
info.setKeySize(bits);
|
||||
|
||||
uint32_t keyUsage = X509_get_key_usage(certificate);
|
||||
if (keyUsage != UINT32_MAX)
|
||||
{
|
||||
static_assert(PDFCertificateInfo::KeyUsageDigitalSignature == KU_DIGITAL_SIGNATURE, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageNonRepudiation == KU_NON_REPUDIATION, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageKeyEncipherment == KU_KEY_ENCIPHERMENT, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageDataEncipherment == KU_DATA_ENCIPHERMENT, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageAgreement == KU_KEY_AGREEMENT, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageCertSign == KU_KEY_CERT_SIGN, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageCrlSign == KU_CRL_SIGN, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageEncipherOnly == KU_ENCIPHER_ONLY, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageDecipherOnly == KU_DECIPHER_ONLY, "Fix this code!");
|
||||
|
||||
if (X509_get_extension_flags(certificate) & EXFLAG_XKUSAGE)
|
||||
{
|
||||
const uint32_t extendedKeyUsage = X509_get_extended_key_usage(certificate);
|
||||
Q_ASSERT(extendedKeyUsage != UINT32_MAX);
|
||||
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_SSL_SERVER >> 16 == XKU_SSL_SERVER, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_SSL_CLIENT >> 16 == XKU_SSL_CLIENT, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_SMIME >> 16 == XKU_SMIME, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_CODE_SIGN >> 16 == XKU_CODE_SIGN, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_SGC >> 16 == XKU_SGC, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_OCSP_SIGN >> 16 == XKU_OCSP_SIGN, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_TIMESTAMP >> 16 == XKU_TIMESTAMP, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_DVCS >> 16 == XKU_DVCS, "Fix this code!");
|
||||
static_assert(PDFCertificateInfo::KeyUsageExtended_ANYEKU >> 16 == XKU_ANYEKU, "Fix this code!");
|
||||
|
||||
keyUsage = keyUsage | (extendedKeyUsage << 16);
|
||||
}
|
||||
|
||||
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 persistVersionDeserialized = 0;
|
||||
stream >> persistVersionDeserialized;
|
||||
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;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setNotValidBefore(const QDateTime& notValidBefore)
|
||||
{
|
||||
m_notValidBefore = notValidBefore;
|
||||
}
|
||||
|
||||
QDateTime PDFCertificateInfo::getNotValidAfter() const
|
||||
{
|
||||
return m_notValidAfter;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setNotValidAfter(const QDateTime& notValidAfter)
|
||||
{
|
||||
m_notValidAfter = notValidAfter;
|
||||
}
|
||||
|
||||
int32_t PDFCertificateInfo::getVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setVersion(int32_t version)
|
||||
{
|
||||
m_version = version;
|
||||
}
|
||||
|
||||
PDFCertificateInfo::PublicKey PDFCertificateInfo::getPublicKey() const
|
||||
{
|
||||
return m_publicKey;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setPublicKey(const PublicKey& publicKey)
|
||||
{
|
||||
m_publicKey = publicKey;
|
||||
}
|
||||
|
||||
int PDFCertificateInfo::getKeySize() const
|
||||
{
|
||||
return m_keySize;
|
||||
}
|
||||
|
||||
void PDFCertificateInfo::setKeySize(int keySize)
|
||||
{
|
||||
m_keySize = keySize;
|
||||
}
|
||||
|
||||
PDFCertificateInfo::KeyUsageFlags PDFCertificateInfo::getKeyUsage() const
|
||||
{
|
||||
return m_keyUsage;
|
||||
}
|
||||
|
||||
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 = convertByteArrayToUcharPtr(certificateData);
|
||||
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 persistVersionDeserialized = 0;
|
||||
stream >> persistVersionDeserialized;
|
||||
stream >> type;
|
||||
stream >> info;
|
||||
return PDFCertificateInfo::getCertificateInfo(certificate);
|
||||
}
|
||||
|
||||
QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int nid)
|
||||
@ -1938,100 +1669,6 @@ void PDFPublicKeySignatureHandler::addSignatureDateFromSignerInfoStack(STACK_OF(
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCertificateStore::serialize(QDataStream& stream) const
|
||||
{
|
||||
stream << persist_version;
|
||||
stream << m_certificates;
|
||||
}
|
||||
|
||||
void PDFCertificateStore::deserialize(QDataStream& stream)
|
||||
{
|
||||
int persistVersionDeserialized = 0;
|
||||
stream >> persistVersionDeserialized;
|
||||
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 PDFCertificateStore::contains(const PDFCertificateInfo& info)
|
||||
{
|
||||
return std::find_if(m_certificates.cbegin(), m_certificates.cend(), [&info](const auto& entry) { return entry.info == info; }) != m_certificates.cend();
|
||||
}
|
||||
|
||||
QString PDFCertificateStore::getDefaultCertificateStoreFileName() const
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/TrustedCertStorage.bin";
|
||||
}
|
||||
|
||||
void PDFCertificateStore::loadDefaultUserCertificates()
|
||||
{
|
||||
createDirectoryForDefaultUserCertificatesStore();
|
||||
QString trustedCertificateStoreFileName = getDefaultCertificateStoreFileName();
|
||||
QString trustedCertificateStoreLockFileName = trustedCertificateStoreFileName + ".lock";
|
||||
|
||||
QLockFile lockFile(trustedCertificateStoreLockFileName);
|
||||
if (lockFile.lock())
|
||||
{
|
||||
QFile trustedCertificateStoreFile(trustedCertificateStoreFileName);
|
||||
if (trustedCertificateStoreFile.open(QFile::ReadOnly))
|
||||
{
|
||||
QDataStream stream(&trustedCertificateStoreFile);
|
||||
deserialize(stream);
|
||||
trustedCertificateStoreFile.close();
|
||||
}
|
||||
lockFile.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCertificateStore::saveDefaultUserCertificates()
|
||||
{
|
||||
createDirectoryForDefaultUserCertificatesStore();
|
||||
QString trustedCertificateStoreFileName = getDefaultCertificateStoreFileName();
|
||||
QString trustedCertificateStoreLockFileName = trustedCertificateStoreFileName + ".lock";
|
||||
|
||||
QFileInfo fileInfo(trustedCertificateStoreFileName);
|
||||
QDir dir = fileInfo.dir();
|
||||
dir.mkpath(dir.path());
|
||||
|
||||
QLockFile lockFile(trustedCertificateStoreLockFileName);
|
||||
if (lockFile.lock())
|
||||
{
|
||||
QFile trustedCertificateStoreFile(trustedCertificateStoreFileName);
|
||||
if (trustedCertificateStoreFile.open(QFile::WriteOnly | QFile::Truncate))
|
||||
{
|
||||
QDataStream stream(&trustedCertificateStoreFile);
|
||||
serialize(stream);
|
||||
trustedCertificateStoreFile.close();
|
||||
}
|
||||
lockFile.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCertificateStore::createDirectoryForDefaultUserCertificatesStore()
|
||||
{
|
||||
QFileInfo fileInfo(getDefaultCertificateStoreFileName());
|
||||
QString path = fileInfo.path();
|
||||
QDir().mkpath(path);
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
@ -2047,7 +1684,7 @@ void pdf::PDFPublicKeySignatureHandler::addTrustedCertificates(X509_STORE* store
|
||||
{
|
||||
if (m_parameters.store)
|
||||
{
|
||||
const PDFCertificateStore::CertificateEntries& certificates = m_parameters.store->getCertificates();
|
||||
const PDFCertificateEntries& certificates = m_parameters.store->getCertificates();
|
||||
for (const auto& entry : certificates)
|
||||
{
|
||||
QByteArray certificateData = entry.info.getCertificateData();
|
||||
@ -2085,36 +1722,6 @@ void pdf::PDFPublicKeySignatureHandler::addTrustedCertificates(X509_STORE* store
|
||||
#endif
|
||||
}
|
||||
|
||||
pdf::PDFCertificateStore::CertificateEntries pdf::PDFCertificateStore::getSystemCertificates()
|
||||
{
|
||||
CertificateEntries result;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
HCERTSTORE certStore = CertOpenSystemStore(0, L"ROOT");
|
||||
PCCERT_CONTEXT context = nullptr;
|
||||
if (certStore)
|
||||
{
|
||||
while (context = CertEnumCertificatesInStore(certStore, context))
|
||||
{
|
||||
const unsigned char* pointer = context->pbCertEncoded;
|
||||
QByteArray data(reinterpret_cast<const char*>(pointer), context->cbCertEncoded);
|
||||
std::optional<PDFCertificateInfo> info = PDFCertificateInfo::getCertificateInfo(data);
|
||||
if (info)
|
||||
{
|
||||
CertificateEntry entry;
|
||||
entry.type = EntryType::System;
|
||||
entry.info = qMove(*info);
|
||||
result.emplace_back(qMove(entry));
|
||||
}
|
||||
}
|
||||
|
||||
CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(PDF4QT_COMPILER_MINGW) || defined(PDF4QT_COMPILER_GCC)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user