// Copyright (C) 2020-2021 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 "pdfsignaturehandler.h" #include "pdfdocument.h" #include "pdfencoding.h" #include "pdfform.h" #include "pdfutils.h" #include "pdfsignaturehandler_impl.h" #include #include #include #include #include #include #include #include #include #include #include namespace pdf { static QMutex s_globalOpenSSLMutex(QMutex::Recursive); /// OpenSSL is not thread safe. class PDFOpenSSLGlobalLock { public: explicit inline PDFOpenSSLGlobalLock() : m_mutexLocker(&s_globalOpenSSLMutex) { } inline ~PDFOpenSSLGlobalLock() = default; private: QMutexLocker m_mutexLocker; }; PDFSignatureReference PDFSignatureReference::parse(const PDFObjectStorage* storage, PDFObject object) { PDFSignatureReference result; if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object)) { PDFDocumentDataLoaderDecorator loader(storage); constexpr const std::array, 3> types = { std::pair{ "DocMDP", PDFSignatureReference::TransformMethod::DocMDP }, std::pair{ "UR", PDFSignatureReference::TransformMethod::UR }, std::pair{ "FieldMDP", PDFSignatureReference::TransformMethod::FieldMDP } }; // Jakub Melka: parse the signature reference dictionary result.m_transformMethod = loader.readEnumByName(dictionary->get("TransformMethod"), types.cbegin(), types.cend(), PDFSignatureReference::TransformMethod::Invalid); result.m_transformParams = dictionary->get("TransformParams"); result.m_data = dictionary->get("Data"); result.m_digestMethod = loader.readNameFromDictionary(dictionary, "DigestMethod"); } return result; } PDFSignature PDFSignature::parse(const PDFObjectStorage* storage, PDFObject object) { PDFSignature result; if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object)) { PDFDocumentDataLoaderDecorator loader(storage); constexpr const std::array, 2> types = { std::pair{ "Sig", Type::Sig }, std::pair{ "DocTimeStamp", Type::DocTimeStamp } }; // Jakub Melka: parse the signature dictionary result.m_type = loader.readEnumByName(dictionary->get("Type"), types.cbegin(), types.cend(), Type::Sig); result.m_filter = loader.readNameFromDictionary(dictionary, "Filter"); result.m_subfilter = loader.readNameFromDictionary(dictionary, "SubFilter"); result.m_contents = loader.readStringFromDictionary(dictionary, "Contents"); if (dictionary->hasKey("Cert")) { PDFObject certificates = storage->getObject(dictionary->get("Cert")); if (certificates.isString()) { result.m_certificates = { loader.readString(certificates) }; } else if (certificates.isArray()) { result.m_certificates = loader.readStringArray(certificates); } } std::vector byteRangesArray = loader.readIntegerArrayFromDictionary(dictionary, "ByteRange"); const size_t byteRangeCount = byteRangesArray.size() / 2; result.m_byteRanges.reserve(byteRangeCount); for (size_t i = 0; i < byteRangeCount; ++i) { ByteRange byteRange = { byteRangesArray[2 * i], byteRangesArray[2 * i + 1] }; result.m_byteRanges.push_back(byteRange); } result.m_references = loader.readObjectList(dictionary->get("Reference")); std::vector changes = loader.readIntegerArrayFromDictionary(dictionary, "Changes"); if (changes.size() == 3) { result.m_changes = { changes[0], changes[1], changes[2] }; } result.m_name = loader.readTextStringFromDictionary(dictionary, "Name", QString()); result.m_signingDateTime = PDFEncoding::convertToDateTime(loader.readStringFromDictionary(dictionary, "M")); result.m_location = loader.readTextStringFromDictionary(dictionary, "Location", QString()); result.m_reason = loader.readTextStringFromDictionary(dictionary, "Reason", QString()); result.m_contactInfo = loader.readTextStringFromDictionary(dictionary, "ContactInfo", QString()); result.m_R = loader.readIntegerFromDictionary(dictionary, "R", 0); result.m_V = loader.readIntegerFromDictionary(dictionary, "V", 0); result.m_propBuild = dictionary->get("Prop_Build"); result.m_propTime = loader.readIntegerFromDictionary(dictionary, "Prop_AuthTime", 0); constexpr const std::array, 3> authentificationTypes = { std::pair{ "PIN", AuthentificationType::PIN }, std::pair{ "Password", AuthentificationType::Password }, std::pair{ "Fingerprint", AuthentificationType::Fingerprint } }; result.m_propType = loader.readEnumByName(dictionary->get("Prop_AuthType"), authentificationTypes.cbegin(), authentificationTypes.cend(), AuthentificationType::Invalid); } return result; } 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, parameters); } else if (subfilter == "adbe.pkcs7.sha1") { return new PDFSignatureHandler_adbe_pkcs7_sha1(signatureField, sourceData, parameters); } else if (subfilter == "adbe.x509.rsa_sha1") { return new PDFSignatureHandler_adbe_pkcs7_rsa_sha1(signatureField, sourceData, parameters); } else if (subfilter == "ETSI.CAdES.detached") { return new PDFSignatureHandler_ETSI_CAdES_detached(signatureField, sourceData, parameters); } else if (subfilter == "ETSI.RFC3161") { return new PDFSignatureHandler_ETSI_RFC3161(signatureField, sourceData, parameters); } return nullptr; } std::vector PDFSignatureHandler::verifySignatures(const PDFForm& form, const QByteArray& sourceData, const Parameters& parameters) { std::vector result; if (parameters.enableVerification && (form.isAcroForm() || form.isXFAForm())) { std::vector signatureFields; auto getSignatureFields = [&signatureFields](const PDFFormField* field) { if (field->getFieldType() == PDFFormField::FieldType::Signature) { const PDFFormFieldSignature* signatureField = dynamic_cast(field); Q_ASSERT(signatureField); signatureFields.push_back(signatureField); } }; form.apply(getSignatureFields); result.reserve(signatureFields.size()); for (const PDFFormFieldSignature* signatureField : signatureFields) { if (const PDFSignatureHandler* signatureHandler = createHandler(signatureField, sourceData, parameters)) { result.emplace_back(signatureHandler->verify()); delete signatureHandler; } else { PDFObjectReference signatureFieldReference = signatureField->getSelfReference(); QString qualifiedName = signatureField->getName(PDFFormField::NameType::FullyQualified); PDFSignatureVerificationResult verificationResult(signatureField->getSignature().getType(), signatureFieldReference, qMove(qualifiedName)); verificationResult.addNoHandlerError(signatureField->getSignature().getSubfilter()); result.emplace_back(qMove(verificationResult)); } } } return result; } void PDFSignatureVerificationResult::addNoHandlerError(const QByteArray& format) { m_flags.setFlag(Error_NoHandler); m_errors << PDFTranslationContext::tr("No signature handler for signature format '%1'.").arg(QString::fromLatin1(format)); } void PDFSignatureVerificationResult::addInvalidCertificateError() { m_flags.setFlag(Error_Certificate_Invalid); m_errors << PDFTranslationContext::tr("Certificate format is invalid."); } void PDFSignatureVerificationResult::addNoSignaturesError() { m_flags.setFlag(Error_Certificate_NoSignatures); m_errors << PDFTranslationContext::tr("No signatures in certificate data."); } void PDFSignatureVerificationResult::addCertificateMissingError() { m_flags.setFlag(Error_Certificate_Missing); m_errors << PDFTranslationContext::tr("Certificate is missing."); } void PDFSignatureVerificationResult::addCertificateGenericError() { m_flags.setFlag(Error_Certificate_Generic); m_errors << PDFTranslationContext::tr("Generic error occured during certificate validation."); } void PDFSignatureVerificationResult::addCertificateExpiredError() { m_flags.setFlag(Error_Certificate_Expired); m_errors << PDFTranslationContext::tr("Certificate has expired."); } void PDFSignatureVerificationResult::addCertificateSelfSignedError() { m_flags.setFlag(Error_Certificate_SelfSigned); m_errors << PDFTranslationContext::tr("Certificate is self-signed."); } void PDFSignatureVerificationResult::addCertificateSelfSignedInChainError() { m_flags.setFlag(Error_Certificate_SelfSignedChain); m_errors << PDFTranslationContext::tr("Self-signed certificate in chain."); } void PDFSignatureVerificationResult::addCertificateTrustedNotFoundError() { m_flags.setFlag(Error_Certificate_TrustedNotFound); m_errors << PDFTranslationContext::tr("Trusted certificate not found."); } void PDFSignatureVerificationResult::addCertificateRevokedError() { m_flags.setFlag(Error_Certificate_Revoked); m_errors << PDFTranslationContext::tr("Certificate has been revoked."); } void PDFSignatureVerificationResult::addCertificateOtherError(int error) { m_flags.setFlag(Error_Certificate_Other); m_errors << PDFTranslationContext::tr("Certificate validation failed with code %1.").arg(error); } void PDFSignatureVerificationResult::addInvalidSignatureError() { m_flags.setFlag(Error_Signature_Invalid); m_errors << PDFTranslationContext::tr("Signature is invalid."); } void PDFSignatureVerificationResult::addSignatureNoSignaturesFoundError() { m_flags.setFlag(Error_Signature_NoSignaturesFound); m_errors << PDFTranslationContext::tr("No signatures found in certificate."); } void PDFSignatureVerificationResult::addSignatureCertificateMissingError() { m_flags.setFlag(Error_Signature_SourceCertificateMissing); m_errors << PDFTranslationContext::tr("Signature certificate is missing."); } void PDFSignatureVerificationResult::addSignatureDigestFailureError() { m_flags.setFlag(Error_Signature_DigestFailure); m_errors << PDFTranslationContext::tr("Signed data has different hash function digest."); } void PDFSignatureVerificationResult::addSignatureDataOtherError() { m_flags.setFlag(Error_Signature_DataOther); m_errors << PDFTranslationContext::tr("Signed data are invalid."); } void PDFSignatureVerificationResult::addSignatureDataCoveredBySignatureMissingError() { m_flags.setFlag(Error_Signature_DataCoveredBySignatureMissing); m_errors << PDFTranslationContext::tr("Data covered by signature are not present."); } void PDFSignatureVerificationResult::addSignatureNotCoveredBytesWarning(PDFInteger count) { if (!m_flags.testFlag(Warning_Signature_NotCoveredBytes)) { m_flags.setFlag(Warning_Signature_NotCoveredBytes); m_warnings << PDFTranslationContext::tr("%1 bytes are not covered by signature.").arg(count); } } void PDFSignatureVerificationResult::addCertificateCRLValidityTimeExpiredWarning() { if (!m_flags.testFlag(Warning_Certificate_CRLValidityTimeExpired)) { m_flags.setFlag(Warning_Certificate_CRLValidityTimeExpired); m_warnings << PDFTranslationContext::tr("Certificate revocation list (CRL) not checked, validity time has expired."); } } void PDFSignatureVerificationResult::addCertificateQualifiedStatementNotVerifiedWarning() { if (!m_flags.testFlag(Warning_Certificate_QualifiedStatement)) { m_flags.setFlag(Warning_Certificate_QualifiedStatement); m_warnings << PDFTranslationContext::tr("Qualified certificate statement not verified."); } } void PDFSignatureVerificationResult::setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName) { m_signatureFieldQualifiedName = signatureFieldQualifiedName; } void PDFSignatureVerificationResult::setSignatureFieldReference(PDFObjectReference signatureFieldReference) { m_signatureFieldReference = signatureFieldReference; } void PDFSignatureVerificationResult::addHashAlgorithm(const QString& algorithm) { if (!m_hashAlgorithms.contains(algorithm)) { m_hashAlgorithms << algorithm; } } void PDFSignatureVerificationResult::validate() { if (isCertificateValid() && isSignatureValid()) { m_flags.setFlag(OK); } } QDateTime PDFSignatureVerificationResult::getSignatureDate() const { return m_signatureDate; } void PDFSignatureVerificationResult::setSignatureDate(const QDateTime& signatureDate) { m_signatureDate = signatureDate; } QDateTime PDFSignatureVerificationResult::getTimestampDate() const { return m_timestampDate; } void PDFSignatureVerificationResult::setTimestampDate(const QDateTime& timestampDate) { m_timestampDate = timestampDate; } QByteArray PDFSignatureVerificationResult::getSignatureHandler() const { return m_signatureHandler; } void PDFSignatureVerificationResult::setSignatureHandler(const QByteArray& signatureFilter) { m_signatureHandler = signatureFilter; } PDFSignatureVerificationResult::Status PDFSignatureVerificationResult::getCertificateStatus() const { if (hasCertificateError()) { return Status::Error; } if (hasCertificateWarning()) { return Status::Warning; } return Status::OK; } PDFSignatureVerificationResult::Status PDFSignatureVerificationResult::getSignatureStatus() const { if (hasSignatureError()) { return Status::Error; } if (hasSignatureWarning()) { return Status::Warning; } return Status::OK; } QString PDFSignatureVerificationResult::getStatusText(Status status) { switch (status) { case Status::OK: return PDFTranslationContext::tr("OK"); case Status::Warning: return PDFTranslationContext::tr("Warning"); case Status::Error: return PDFTranslationContext::tr("Error"); default: break; } return QString(); } const PDFClosedIntervalSet& PDFSignatureVerificationResult::getBytesCoveredBySignature() const { return m_bytesCoveredBySignature; } void PDFSignatureVerificationResult::setBytesCoveredBySignature(const PDFClosedIntervalSet& bytesCoveredBySignature) { m_bytesCoveredBySignature = bytesCoveredBySignature; } PDFSignature::Type PDFSignatureVerificationResult::getType() const { return m_type; } void PDFSignatureVerificationResult::setType(const PDFSignature::Type& type) { m_type = type; } void PDFPublicKeySignatureHandler::initializeResult(PDFSignatureVerificationResult& result) const { PDFObjectReference signatureFieldReference = m_signatureField->getSelfReference(); QString signatureFieldQualifiedName = m_signatureField->getName(PDFFormField::NameType::FullyQualified); result.setType(m_signatureField->getSignature().getType()); result.setSignatureFieldReference(signatureFieldReference); result.setSignatureFieldQualifiedName(signatureFieldQualifiedName); result.setSignatureHandler(m_signatureField->getSignature().getSubfilter()); } STACK_OF(X509)* PDFPublicKeySignatureHandler::getCertificates(PKCS7* pkcs7) { if (!pkcs7) { return nullptr; } if (PKCS7_type_is_signed(pkcs7)) { return pkcs7->d.sign->cert; } if (PKCS7_type_is_signedAndEnveloped(pkcs7)) { return pkcs7->d.signed_and_enveloped->cert; } return nullptr; } void PDFPublicKeySignatureHandler::verifyCertificate(PDFSignatureVerificationResult& result) const { PDFOpenSSLGlobalLock lock; OpenSSL_add_all_algorithms(); const PDFSignature& signature = m_signatureField->getSignature(); const QByteArray& content = signature.getContents(); // Jakub Melka: we will try to get pkcs7 from signature, then // verify signer certificates. const unsigned char* data = convertByteArrayToUcharPtr(content); if (PKCS7* pkcs7 = d2i_PKCS7(nullptr, &data, content.size())) { X509_STORE* store = X509_STORE_new(); X509_STORE_CTX* context = X509_STORE_CTX_new(); // Above functions can fail only if not enough memory. But in this // case, this library will crash anyway. Q_ASSERT(store); Q_ASSERT(context); addTrustedCertificates(store); STACK_OF(PKCS7_SIGNER_INFO)* signerInfo = PKCS7_get_signer_info(pkcs7); const int signerInfoCount = sk_PKCS7_SIGNER_INFO_num(signerInfo); STACK_OF(X509)* certificates = getCertificates(pkcs7); if (signerInfo && signerInfoCount > 0 && certificates) { for (int i = 0; i < signerInfoCount; ++i) { PKCS7_SIGNER_INFO* signerInfoValue = sk_PKCS7_SIGNER_INFO_value(signerInfo, i); PKCS7_ISSUER_AND_SERIAL* issuerAndSerial = signerInfoValue->issuer_and_serial; X509* signer = X509_find_by_issuer_and_serial(certificates, issuerAndSerial->issuer, issuerAndSerial->serial); if (!signer) { result.addCertificateMissingError(); break; } if (!X509_STORE_CTX_init(context, store, signer, certificates)) { result.addCertificateGenericError(); break; } if (!X509_STORE_CTX_set_purpose(context, X509_PURPOSE_SMIME_SIGN)) { result.addCertificateGenericError(); break; } 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) { int error = X509_STORE_CTX_get_error(context); switch (error) { case X509_V_OK: // Strange, this should not occur... when X509_verify_cert fails break; case X509_V_ERR_CERT_HAS_EXPIRED: result.addCertificateExpiredError(); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: result.addCertificateSelfSignedError(); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: result.addCertificateSelfSignedInChainError(); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: result.addCertificateTrustedNotFoundError(); break; case X509_V_ERR_CERT_REVOKED: result.addCertificateRevokedError(); break; default: result.addCertificateOtherError(error); break; } // We will add certificate info for all certificates const int count = sk_X509_num(certificates); for (int i = 0; i < count; ++i) { result.addCertificateInfo(getCertificateInfo(sk_X509_value(certificates, i))); } } else { STACK_OF(X509)* validChain = X509_STORE_CTX_get0_chain(context); const int count = sk_X509_num(validChain); for (int i = 0; i < count; ++i) { result.addCertificateInfo(getCertificateInfo(sk_X509_value(validChain, i))); } } X509_STORE_CTX_cleanup(context); } } else { result.addNoSignaturesError(); } X509_STORE_CTX_free(context); X509_STORE_free(store); PKCS7_free(pkcs7); } else { result.addInvalidCertificateError(); } if (!result.hasCertificateError()) { result.setFlag(PDFSignatureVerificationResult::Certificate_OK, true); } } BIO* PDFPublicKeySignatureHandler::getSignedDataBuffer(pdf::PDFSignatureVerificationResult& result, QByteArray& outputBuffer) const { const PDFSignature& signature = m_signatureField->getSignature(); const QByteArray& contents = signature.getContents(); const QByteArray& sourceData = m_sourceData; PDFInteger size = 0; const PDFSignature::ByteRanges& byteRanges = signature.getByteRanges(); for (const PDFSignature::ByteRange& byteRange : byteRanges) { size += byteRange.size; } // Sanity checks if (size > sourceData.size()) { result.addSignatureDataCoveredBySignatureMissingError(); return nullptr; } PDFClosedIntervalSet bytesCoveredBySignature; outputBuffer.reserve(size); for (const PDFSignature::ByteRange& byteRange : byteRanges) { PDFInteger startOffset = byteRange.offset; // Offset to the first data byte PDFInteger endOffset = byteRange.offset + byteRange.size; // Offset to the byte following last data byte if (startOffset == endOffset) { // This means byte range is zero continue; } if (startOffset > endOffset || startOffset < 0 || endOffset < 0 || startOffset >= m_sourceData.size() || endOffset > m_sourceData.size()) { result.addSignatureDataCoveredBySignatureMissingError(); return nullptr; } const int length = endOffset - startOffset; outputBuffer.append(sourceData.constData() + startOffset, length); bytesCoveredBySignature.addInterval(startOffset, endOffset - 1); } // Jakub Melka: We must find byte string, which corresponds to signature. // We find only first occurence, because second one should not exist - because // it will mean that signature must be covered by itself. QByteArray hexContents = contents.toHex(); int index = sourceData.indexOf(hexContents); if (index == -1) { index = sourceData.indexOf(hexContents.toUpper()); } if (index != -1) { int firstByteIndex = index; int lastByteIndex = index + hexContents.size() - 1; if (firstByteIndex > 0 && sourceData[firstByteIndex - 1] == '<') { --firstByteIndex; } if (lastByteIndex + 1 < sourceData.size() && sourceData[lastByteIndex + 1] == '>') { ++lastByteIndex; } bytesCoveredBySignature.addInterval(firstByteIndex, lastByteIndex); } // We add a warning, that this signature doesn't cover whole source byte range if (!bytesCoveredBySignature.isCovered(0, sourceData.size() - 1)) { const PDFInteger notCoveredBytes = sourceData.size() - int(bytesCoveredBySignature.getTotalLength()); result.addSignatureNotCoveredBytesWarning(notCoveredBytes); } result.setBytesCoveredBySignature(qMove(bytesCoveredBySignature)); return BIO_new_mem_buf(outputBuffer.data(), outputBuffer.length()); } void PDFPublicKeySignatureHandler::verifySignature(PDFSignatureVerificationResult& result) const { PDFOpenSSLGlobalLock lock; OpenSSL_add_all_algorithms(); const PDFSignature& signature = m_signatureField->getSignature(); const QByteArray& content = signature.getContents(); // Jakub Melka: we will try to get pkcs7 from signature, then // verify signer certificates. const unsigned char* data = convertByteArrayToUcharPtr(content); if (PKCS7* pkcs7 = d2i_PKCS7(nullptr, &data, content.size())) { QByteArray buffer; if (BIO* inputBuffer = getSignedDataBuffer(result, buffer)) { if (BIO* dataBio = PKCS7_dataInit(pkcs7, inputBuffer)) { // Now, we must read from bio to calculate digests (digest is returned) std::array buffer = { }; int bytesRead = 0; do { bytesRead = BIO_read(dataBio, buffer.data(), int(buffer.size())); } while (bytesRead > 0); STACK_OF(PKCS7_SIGNER_INFO)* signerInfo = PKCS7_get_signer_info(pkcs7); addHashAlgorithmFromSignerInfoStack(signerInfo, result); addSignatureDateFromSignerInfoStack(signerInfo, result); const int signerInfoCount = sk_PKCS7_SIGNER_INFO_num(signerInfo); STACK_OF(X509)* certificates = getCertificates(pkcs7); if (signerInfo && signerInfoCount > 0 && certificates) { for (int i = 0; i < signerInfoCount; ++i) { PKCS7_SIGNER_INFO* signerInfoValue = sk_PKCS7_SIGNER_INFO_value(signerInfo, i); PKCS7_ISSUER_AND_SERIAL* issuerAndSerial = signerInfoValue->issuer_and_serial; X509* signer = X509_find_by_issuer_and_serial(certificates, issuerAndSerial->issuer, issuerAndSerial->serial); if (!signer) { result.addSignatureCertificateMissingError(); break; } const int verification = PKCS7_signatureVerify(dataBio, pkcs7, signerInfoValue, signer); if (verification <= 0) { const int reason = ERR_GET_REASON(ERR_get_error()); switch (reason) { case PKCS7_R_DIGEST_FAILURE: result.addSignatureDigestFailureError(); break; default: result.addSignatureDataOtherError(); break; } } } } else { result.addSignatureNoSignaturesFoundError(); } // According to the documentation, we should not call PKCS7_dataFinal // at the end, when pkcs7 is populated. BIO_free(dataBio); } else { result.addInvalidSignatureError(); } BIO_free(inputBuffer); } else { // There is no need for adding error, error is in this case added by getSignedDataBuffer function } PKCS7_free(pkcs7); } else { result.addInvalidSignatureError(); } if (!result.hasSignatureError()) { result.setFlag(PDFSignatureVerificationResult::Signature_OK, true); } } PDFSignatureVerificationResult PDFSignatureHandler_adbe_pkcs7_detached::verify() const { PDFSignatureVerificationResult result; initializeResult(result); verifyCertificate(result); verifySignature(result); result.validate(); return result; } PDFSignatureVerificationResult PDFSignatureHandler_ETSI_CAdES_detached::verify() const { PDFSignatureVerificationResult result; initializeResult(result); verifyCertificateCAdES(result, X509_PURPOSE_SMIME_SIGN); verifySignature(result); result.validate(); return result; } PDFSignatureVerificationResult PDFSignatureHandler_ETSI_RFC3161::verify() const { PDFSignatureVerificationResult result; initializeResult(result); verifyCertificateCAdES(result, X509_PURPOSE_TIMESTAMP_SIGN); verifySignatureTimestamp(result); result.validate(); return result; } void PDFSignatureHandler_ETSI_RFC3161::verifySignatureTimestamp(PDFSignatureVerificationResult& result) const { PDFOpenSSLGlobalLock lock; OpenSSL_add_all_algorithms(); const PDFSignature& signature = m_signatureField->getSignature(); const QByteArray& content = signature.getContents(); // Jakub Melka: we will try to get pkcs7 from signature, then // verify signer certificates. const unsigned char* data = convertByteArrayToUcharPtr(content); if (PKCS7* pkcs7 = d2i_PKCS7(nullptr, &data, content.size())) { QByteArray buffer; if (BIO* inputBuffer = getSignedDataBuffer(result, buffer)) { X509_STORE* store = X509_STORE_new(); // Above function can fail only if not enough memory. But in this // case, this library will crash anyway. Q_ASSERT(store); addTrustedCertificates(store); // Add certificates from DSS store STACK_OF(X509)* certificatesFromPkcs7 = getCertificates(pkcs7); STACK_OF(X509)* usedCertificates = sk_X509_new_null(); // First, add all certificates from pkcs7 for (int i = 0; i < sk_X509_num(certificatesFromPkcs7); ++i) { X509* certificate = sk_X509_value(certificatesFromPkcs7, i); sk_X509_push(usedCertificates, certificate); X509_up_ref(certificate); } if (m_parameters.dss && !m_parameters.dss->getMasterItem()->Cert.empty()) { // Second, add all certificates from document's security store for (const QByteArray& certificateData : m_parameters.dss->getMasterItem()->Cert) { const unsigned char* certificateDataBuffer = convertByteArrayToUcharPtr(certificateData); if (X509* certificate = d2i_X509(nullptr, &certificateDataBuffer, certificateData.size())) { sk_X509_push(usedCertificates, certificate); } } } // Initialization of verification context TS_VERIFY_CTX* ts_context = TS_VERIFY_CTX_new(); TS_VERIFY_CTX_init(ts_context); TS_VERIFY_CTX_set_data(ts_context, inputBuffer); TS_VERIFY_CTX_set_flags(ts_context, TS_VFY_ALL_DATA & ~TS_VFY_POLICY & ~TS_VFY_NONCE & ~TS_VFY_TSA_NAME); TS_VERIFY_CTX_set_store(ts_context, store); TS_VERIFY_CTS_set_certs(ts_context, usedCertificates); // Get timestamp and hash algorithm if (TS_TST_INFO* info = PKCS7_to_TS_TST_INFO(pkcs7)) { // Date/time of timestamp const ASN1_GENERALIZEDTIME* time = TS_TST_INFO_get_time(info); result.setTimestampDate(getDateTimeFromASN(time)); } STACK_OF(PKCS7_SIGNER_INFO)* signerInfos = PKCS7_get_signer_info(pkcs7); addHashAlgorithmFromSignerInfoStack(signerInfos, result); addSignatureDateFromSignerInfoStack(signerInfos, result); const int verifyValue = TS_RESP_verify_token(ts_context, pkcs7); if (verifyValue <= 0) { const int reason = ERR_GET_REASON(ERR_get_error()); switch (reason) { case TS_R_MESSAGE_IMPRINT_MISMATCH: result.addSignatureDigestFailureError(); break; default: result.addSignatureDataOtherError(); break; } } // Finalization of verification context. Function TS_VERIFY_CTX_cleanup also // frees all data, such as context, store, etc. TS_VERIFY_CTX_cleanup(ts_context); TS_VERIFY_CTX_free(ts_context); } else { // There is no need for adding error, error is in this case added by getSignedDataBuffer function } PKCS7_free(pkcs7); } else { result.addInvalidSignatureError(); } if (!result.hasSignatureError()) { result.setFlag(PDFSignatureVerificationResult::Signature_OK, true); } } // This is protected by global mutex, but it is ugly static PDFSignatureVerificationResult* s_ETSI_currentResult = nullptr; int PDFSignatureHandler_ETSI_base::verifyCallback(int ok, X509_STORE_CTX* context) { const int errorCode = X509_STORE_CTX_get_error(context); switch (errorCode) { case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: { // We will treat this as only warning s_ETSI_currentResult->addCertificateCRLValidityTimeExpiredWarning(); X509_STORE_CTX_set_error(context, X509_V_OK); return 1; } case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: { // We must handle all critical extensions manually X509* certificate = X509_STORE_CTX_get_current_cert(context); const STACK_OF(X509_EXTENSION)* extensions = X509_get0_extensions(certificate); for (int i = 0, extensionsCount = sk_X509_EXTENSION_num(extensions); i < extensionsCount; ++i) { X509_EXTENSION* extension = sk_X509_EXTENSION_value(extensions, i); // Skip non-critical extensions if (!X509_EXTENSION_get_critical(extension)) { continue; } const ASN1_OBJECT* object = X509_EXTENSION_get_object(extension); const int nid = OBJ_obj2nid(object); switch (nid) { case NID_basic_constraints: case NID_key_usage: // These are handled by OpenSSL continue; case NID_qcStatements: { // We will treat this as only warning s_ETSI_currentResult->addCertificateQualifiedStatementNotVerifiedWarning(); X509_STORE_CTX_set_error(context, X509_V_OK); continue; } default: return ok; } } X509_STORE_CTX_set_error(context, X509_V_OK); return 1; } default: break; } return ok; } void PDFSignatureHandler_ETSI_base::verifyCertificateCAdES(PDFSignatureVerificationResult& result, int purpose) const { PDFOpenSSLGlobalLock lock; s_ETSI_currentResult = &result; OpenSSL_add_all_algorithms(); const PDFSignature& signature = m_signatureField->getSignature(); const QByteArray& content = signature.getContents(); // Jakub Melka: we will try to get pkcs7 from signature, then // verify signer certificates. const unsigned char* data = convertByteArrayToUcharPtr(content); if (PKCS7* pkcs7 = d2i_PKCS7(nullptr, &data, content.size())) { X509_STORE* store = X509_STORE_new(); X509_STORE_CTX* context = X509_STORE_CTX_new(); // Above functions can fail only if not enough memory. But in this // case, this library will crash anyway. Q_ASSERT(store); Q_ASSERT(context); addTrustedCertificates(store); STACK_OF(PKCS7_SIGNER_INFO)* signerInfo = PKCS7_get_signer_info(pkcs7); const int signerInfoCount = sk_PKCS7_SIGNER_INFO_num(signerInfo); STACK_OF(X509)* certificates = getCertificates(pkcs7); if (signerInfo && signerInfoCount > 0 && certificates) { STACK_OF(X509)* allCertificates = nullptr; if (m_parameters.dss && !m_parameters.dss->getMasterItem()->Cert.empty()) { allCertificates = sk_X509_new_null(); // First, add all certificates from pkcs7 for (int i = 0; i < sk_X509_num(certificates); ++i) { sk_X509_push(allCertificates, sk_X509_value(certificates, i)); } // Second, add all certificates from document's security store for (const QByteArray& certificateData : m_parameters.dss->getMasterItem()->Cert) { const unsigned char* certificateDataBuffer = convertByteArrayToUcharPtr(certificateData); if (X509* certificate = d2i_X509(nullptr, &certificateDataBuffer, certificateData.size())) { sk_X509_push(allCertificates, certificate); } } } STACK_OF(X509)* usedCertificates = allCertificates ? allCertificates : certificates; // Jakub Melka: add certificate revocation lists if (m_parameters.dss && !m_parameters.dss->getMasterItem()->CRL.empty()) { for (const QByteArray& crlData : m_parameters.dss->getMasterItem()->CRL) { const unsigned char* crlDataBuffer = convertByteArrayToUcharPtr(crlData); if (X509_CRL* crl = d2i_X509_CRL(nullptr, &crlDataBuffer, crlData.size())) { X509_STORE_add_crl(store, crl); X509_CRL_free(crl); } } } for (int i = 0; i < signerInfoCount; ++i) { PKCS7_SIGNER_INFO* signerInfoValue = sk_PKCS7_SIGNER_INFO_value(signerInfo, i); PKCS7_ISSUER_AND_SERIAL* issuerAndSerial = signerInfoValue->issuer_and_serial; X509* signer = X509_find_by_issuer_and_serial(usedCertificates, issuerAndSerial->issuer, issuerAndSerial->serial); if (!signer) { result.addCertificateMissingError(); break; } if (!X509_STORE_CTX_init(context, store, signer, usedCertificates)) { result.addCertificateGenericError(); break; } if (!X509_STORE_CTX_set_purpose(context, purpose)) { result.addCertificateGenericError(); break; } unsigned long flags = X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL | X509_V_FLAG_EXTENDED_CRL_SUPPORT; if (m_parameters.ignoreExpirationDate) { flags |= X509_V_FLAG_NO_CHECK_TIME; } X509_STORE_CTX_set_flags(context, flags); X509_STORE_CTX_set_verify_cb(context, &PDFSignatureHandler_ETSI_CAdES_detached::verifyCallback); int verificationResult = X509_verify_cert(context); if (verificationResult <= 0) { int error = X509_STORE_CTX_get_error(context); switch (error) { case X509_V_OK: // Strange, this should not occur... when X509_verify_cert fails break; case X509_V_ERR_CERT_HAS_EXPIRED: result.addCertificateExpiredError(); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: result.addCertificateSelfSignedError(); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: result.addCertificateSelfSignedInChainError(); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: result.addCertificateTrustedNotFoundError(); break; case X509_V_ERR_CERT_REVOKED: result.addCertificateRevokedError(); break; default: result.addCertificateOtherError(error); break; } // We will add certificate info for all certificates const int count = sk_X509_num(usedCertificates); for (int i = 0; i < count; ++i) { result.addCertificateInfo(getCertificateInfo(sk_X509_value(usedCertificates, i))); } } else { STACK_OF(X509)* validChain = X509_STORE_CTX_get0_chain(context); const int count = sk_X509_num(validChain); for (int i = 0; i < count; ++i) { result.addCertificateInfo(getCertificateInfo(sk_X509_value(validChain, i))); } } X509_STORE_CTX_cleanup(context); } if (allCertificates) { for (int i = sk_X509_num(certificates); i < sk_X509_num(allCertificates); ++i) { X509_free(sk_X509_value(allCertificates, i)); } sk_X509_free(allCertificates); } } else { result.addNoSignaturesError(); } X509_STORE_CTX_free(context); X509_STORE_free(store); PKCS7_free(pkcs7); } else { result.addInvalidCertificateError(); } if (!result.hasCertificateError()) { result.setFlag(PDFSignatureVerificationResult::Certificate_OK, true); } } PDFSignatureVerificationResult PDFSignatureHandler_adbe_pkcs7_rsa_sha1::verify() const { PDFSignatureVerificationResult result; initializeResult(result); verifyRSACertificate(result); verifyRSASignature(result); result.validate(); return result; } X509* PDFSignatureHandler_adbe_pkcs7_rsa_sha1::createCertificate(size_t index) const { const PDFSignature& signature = m_signatureField->getSignature(); const std::vector* certificates = signature.getCertificates(); if (certificates && index < certificates->size()) { const QByteArray& certificateSize = (*certificates)[index]; const unsigned char* data = convertByteArrayToUcharPtr(certificateSize); return d2i_X509(nullptr, &data, certificateSize.size()); } return nullptr; } bool PDFSignatureHandler_adbe_pkcs7_rsa_sha1::getMessageDigest(const QByteArray& message, ASN1_OCTET_STRING* encryptedString, RSA* rsa, int& algorithmNID, QByteArray& digest) const { if (!getMessageDigestAlgorithm(encryptedString, rsa, algorithmNID)) { return false; } if (const EVP_MD* md = EVP_get_digestbynid(algorithmNID)) { unsigned int messageDigestSize = EVP_MD_size(md); digest.resize(messageDigestSize); EVP_MD_CTX* context = EVP_MD_CTX_new(); Q_ASSERT(context); EVP_DigestInit(context, md); EVP_DigestUpdate(context, message.constData(), message.size()); EVP_DigestFinal(context, convertByteArrayToUcharPtr(digest), &messageDigestSize); EVP_MD_CTX_free(context); return true; } return false; } bool PDFSignatureHandler_adbe_pkcs7_rsa_sha1::getMessageDigestAlgorithm(ASN1_OCTET_STRING* encryptedString, RSA* rsa, int& algorithmNID) const { algorithmNID = 0; int size = RSA_size(rsa); std::vector decryptedBuffer(size, 0); const int signatureSize = RSA_public_decrypt(encryptedString->length, encryptedString->data, decryptedBuffer.data(), rsa, RSA_PKCS1_PADDING); if (signatureSize <= 0) { return false; } Q_ASSERT(signatureSize < decryptedBuffer.size()); const unsigned char* decryptedBufferPtr = decryptedBuffer.data(); if (X509_SIG* x509_sig = d2i_X509_SIG(nullptr, &decryptedBufferPtr, signatureSize)) { const X509_ALGOR* algorithm = nullptr; const ASN1_OBJECT* algorithmDescriptor = nullptr; X509_SIG_get0(x509_sig, &algorithm, nullptr); X509_ALGOR_get0(&algorithmDescriptor, nullptr, nullptr, algorithm); algorithmNID = OBJ_obj2nid(algorithmDescriptor); X509_SIG_free(x509_sig); return true; } return false; } void PDFSignatureHandler_adbe_pkcs7_rsa_sha1::verifyRSACertificate(PDFSignatureVerificationResult& result) const { if (X509* certificate = createCertificate(0)) { STACK_OF(X509)* certificates = sk_X509_new_null(); sk_X509_push(certificates, certificate); for (size_t i = 1;; ++i) { if (X509* currentCertificate = createCertificate(i)) { sk_X509_push(certificates, currentCertificate); } else { break; } } X509_STORE* store = X509_STORE_new(); X509_STORE_CTX* context = X509_STORE_CTX_new(); // Above functions can fail only if not enough memory. But in this // case, this library will crash anyway. Q_ASSERT(store); Q_ASSERT(context); addTrustedCertificates(store); X509* signer = certificate; if (!X509_STORE_CTX_init(context, store, signer, certificates)) { result.addCertificateGenericError(); } if (!X509_STORE_CTX_set_purpose(context, X509_PURPOSE_SMIME_SIGN)) { result.addCertificateGenericError(); } if (!result.hasCertificateError()) { 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) { int error = X509_STORE_CTX_get_error(context); switch (error) { case X509_V_OK: // Strange, this should not occur... when X509_verify_cert fails break; case X509_V_ERR_CERT_HAS_EXPIRED: result.addCertificateExpiredError(); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: result.addCertificateSelfSignedError(); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: result.addCertificateSelfSignedInChainError(); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: result.addCertificateTrustedNotFoundError(); break; case X509_V_ERR_CERT_REVOKED: result.addCertificateRevokedError(); break; default: result.addCertificateOtherError(error); break; } // We will add certificate info for all certificates const int count = sk_X509_num(certificates); for (int i = 0; i < count; ++i) { result.addCertificateInfo(getCertificateInfo(sk_X509_value(certificates, i))); } } else { STACK_OF(X509)* validChain = X509_STORE_CTX_get0_chain(context); const int count = sk_X509_num(validChain); for (int i = 0; i < count; ++i) { result.addCertificateInfo(getCertificateInfo(sk_X509_value(validChain, i))); } } X509_STORE_CTX_cleanup(context); } X509_STORE_CTX_free(context); X509_STORE_free(store); sk_X509_pop_free(certificates, X509_free); } else { result.addInvalidCertificateError(); } if (!result.hasCertificateError()) { result.setFlag(PDFSignatureVerificationResult::Certificate_OK, true); } } void PDFSignatureHandler_adbe_pkcs7_rsa_sha1::verifyRSASignature(PDFSignatureVerificationResult& result) const { // Jakub Melka: we will use first certificate to validate signature X509* certificate = createCertificate(0); if (!certificate) { result.addSignatureCertificateMissingError(); return; } EVP_PKEY* evpKey = X509_get0_pubkey(certificate); if (!evpKey) { X509_free(certificate); result.addSignatureCertificateMissingError(); return; } RSA* rsa = EVP_PKEY_get0_RSA(evpKey); if (!rsa) { X509_free(certificate); result.addSignatureCertificateMissingError(); return; } QByteArray outputBuffer; if (BIO* bio = this->getSignedDataBuffer(result, outputBuffer)) { const PDFSignature& signature = m_signatureField->getSignature(); const QByteArray& signKey = signature.getContents(); const unsigned char* encryptedSign = convertByteArrayToUcharPtr(signKey); const unsigned int encryptedSignLength = signKey.length(); if (ASN1_OCTET_STRING* encryptedString = d2i_ASN1_OCTET_STRING(nullptr, &encryptedSign, encryptedSignLength)) { int algorithmNID = NID_undef; QByteArray digestBuffer; if (!getMessageDigest(outputBuffer, encryptedString, rsa, algorithmNID, digestBuffer)) { BIO_free(bio); X509_free(certificate); ASN1_OCTET_STRING_free(encryptedString); result.addSignatureDataOtherError(); return; } const unsigned char* digest = convertByteArrayToUcharPtr(digestBuffer); const unsigned int digestLength = digestBuffer.length(); std::array buffer = { }; OBJ_obj2txt(buffer.data(), int(buffer.size() - 1), OBJ_nid2obj(algorithmNID), 0); result.addHashAlgorithm(QString::fromLatin1(buffer.data())); const int verifyValue = RSA_verify(algorithmNID, digest, digestLength, encryptedString->data, encryptedString->length, rsa); ASN1_OCTET_STRING_free(encryptedString); if (verifyValue == 0) { // We have failed, probably due to invalid signature const unsigned long errorCode = ERR_GET_REASON(ERR_get_error()); switch (errorCode) { case RSA_R_DIGEST_DOES_NOT_MATCH: result.addSignatureDigestFailureError(); break; default: result.addSignatureDataOtherError(); break; } } } else { result.addSignatureDataOtherError(); } BIO_free(bio); } X509_free(certificate); if (!result.hasSignatureError()) { result.setFlag(PDFSignatureVerificationResult::Signature_OK, true); } } PDFSignatureVerificationResult PDFSignatureHandler_adbe_pkcs7_sha1::verify() const { PDFSignatureVerificationResult result; initializeResult(result); verifyCertificate(result); verifySignature(result); result.validate(); return result; } BIO* PDFSignatureHandler_adbe_pkcs7_sha1::getSignedDataBuffer(PDFSignatureVerificationResult& result, QByteArray& outputBuffer) const { QByteArray temporaryBuffer; if (BIO* bio = PDFPublicKeySignatureHandler::getSignedDataBuffer(result, temporaryBuffer)) { // Calculate SHA1 outputBuffer.resize(SHA_DIGEST_LENGTH); SHA1(convertByteArrayToUcharPtr(temporaryBuffer), temporaryBuffer.length(), convertByteArrayToUcharPtr(outputBuffer)); BIO_free(bio); return BIO_new_mem_buf(outputBuffer.data(), outputBuffer.length()); } return nullptr; } 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(keyUsage)); } unsigned char* buffer = nullptr; int length = i2d_X509(certificate, &buffer); if (length >= 0) { Q_ASSERT(buffer); info.setCertificateData(QByteArray(reinterpret_cast(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; } 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::getCertificateInfo(const QByteArray& certificateData) { std::optional 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 persist_version = 0; stream >> persist_version; stream >> type; stream >> info; } QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int nid) { QString result; const int stringLocation = X509_NAME_get_index_by_NID(name, nid, -1); X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, stringLocation); return getStringFromASN1_STRING(X509_NAME_ENTRY_get_data(entry)); } QString pdf::PDFPublicKeySignatureHandler::getStringFromASN1_STRING(ASN1_STRING* string) { QString result; if (string) { // Jakub Melka: we must convert entry to UTF8 encoding using function ASN1_STRING_to_UTF8 unsigned char* utf8Buffer = nullptr; int errorCodeOrLength = ASN1_STRING_to_UTF8(&utf8Buffer, string); if (errorCodeOrLength > 0) { result = QString::fromUtf8(reinterpret_cast(utf8Buffer), errorCodeOrLength); } OPENSSL_free(utf8Buffer); } return result; } QDateTime PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME* time) { QDateTime result; if (time) { tm internalTime = { }; if (ASN1_TIME_to_tm(time, &internalTime) > 0) { time_t localTime = _mkgmtime(&internalTime); result = QDateTime::fromSecsSinceEpoch(localTime, Qt::UTC); } } return result; } void PDFPublicKeySignatureHandler::addHashAlgorithmFromSignerInfoStack(STACK_OF(PKCS7_SIGNER_INFO)* signerInfoStack, PDFSignatureVerificationResult& result) { if (!signerInfoStack) { // No signature info provided return; } const int count = sk_PKCS7_SIGNER_INFO_num(signerInfoStack); for (int i = 0; i < count; ++i) { PKCS7_SIGNER_INFO* signerInfoValue = sk_PKCS7_SIGNER_INFO_value(signerInfoStack, i); if (signerInfoValue && signerInfoValue->digest_alg && signerInfoValue->digest_alg->algorithm) { std::array buffer = { }; OBJ_obj2txt(buffer.data(), int(buffer.size() - 1), signerInfoValue->digest_alg->algorithm, 0); result.addHashAlgorithm(QString::fromLatin1(buffer.data())); } } } void PDFPublicKeySignatureHandler::addSignatureDateFromSignerInfoStack(STACK_OF(PKCS7_SIGNER_INFO)* signerInfoStack, PDFSignatureVerificationResult& result) { if (!signerInfoStack) { // No signature info provided return; } if (sk_PKCS7_SIGNER_INFO_num(signerInfoStack) != 1) { // Multiple signature infos, or no signature info return; } // Jakub Melka: We will get signed attribute from signer info. PKCS7_SIGNER_INFO* signerInfo = sk_PKCS7_SIGNER_INFO_value(signerInfoStack, 0); ASN1_TYPE* attribute = PKCS7_get_signed_attribute(signerInfo, NID_pkcs9_signingTime); if (!attribute) { return; } switch (attribute->type) { case V_ASN1_UTCTIME: result.setSignatureDate(getDateTimeFromASN(attribute->value.utctime)); break; case V_ASN1_GENERALIZEDTIME: result.setSignatureDate(getDateTimeFromASN(attribute->value.generalizedtime)); break; default: break; } } void PDFCertificateStore::serialize(QDataStream& stream) const { stream << persist_version; stream << m_certificates; } void 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 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 #ifdef Q_OS_WIN #include #include #pragma comment(lib, "crypt32.lib") #endif void pdf::PDFPublicKeySignatureHandler::addTrustedCertificates(X509_STORE* store) const { if (m_parameters.store) { const PDFCertificateStore::CertificateEntries& certificates = m_parameters.store->getCertificates(); for (const auto& entry : certificates) { QByteArray certificateData = entry.info.getCertificateData(); const unsigned char* pointer = convertByteArrayToUcharPtr(certificateData); X509* certificate = d2i_X509(nullptr, &pointer, certificateData.length()); if (certificate) { X509_STORE_add_cert(store, certificate); X509_free(certificate); } } } #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 } pdf::PDFCertificateStore::CertificateEntries pdf::PDFCertificateStore::getSystemCertificates() { CertificateEntries result; #ifdef Q_OS_WIN HCERTSTORE certStore = CertOpenSystemStore(NULL, L"ROOT"); PCCERT_CONTEXT context = nullptr; if (certStore) { while (context = CertEnumCertificatesInStore(certStore, context)) { const unsigned char* pointer = context->pbCertEncoded; QByteArray data(reinterpret_cast(pointer), context->cbCertEncoded); std::optional 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; }