mirror of https://github.com/JakubMelka/PDF4QT.git
Finishing of CAdES signature verification
This commit is contained in:
parent
417c4912eb
commit
8954f6410c
|
@ -311,10 +311,31 @@ void PDFSignatureVerificationResult::addSignatureDataCoveredBySignatureMissingEr
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -694,10 +715,79 @@ PDFSignatureVerificationResult PDFSignatureHandler_ETSI_CAdES_detached::verify()
|
|||
return result;
|
||||
}
|
||||
|
||||
// This is protected by global mutex, but it is ugly
|
||||
static PDFSignatureVerificationResult* s_ETSI_CAdES_detached_currentResult = nullptr;
|
||||
|
||||
int PDFSignatureHandler_ETSI_CAdES_detached::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_CAdES_detached_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_CAdES_detached_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_CAdES_detached::verifyCertificateCAdES(PDFSignatureVerificationResult& result) const
|
||||
{
|
||||
PDFOpenSSLGlobalLock lock;
|
||||
|
||||
s_ETSI_CAdES_detached_currentResult = &result;
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
const PDFSignature& signature = m_signatureField->getSignature();
|
||||
|
@ -746,6 +836,20 @@ void PDFSignatureHandler_ETSI_CAdES_detached::verifyCertificateCAdES(PDFSignatur
|
|||
}
|
||||
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);
|
||||
|
@ -770,12 +874,13 @@ void PDFSignatureHandler_ETSI_CAdES_detached::verifyCertificateCAdES(PDFSignatur
|
|||
break;
|
||||
}
|
||||
|
||||
unsigned long flags = X509_V_FLAG_TRUSTED_FIRST;
|
||||
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)
|
||||
|
@ -1435,7 +1540,14 @@ QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int
|
|||
|
||||
const int stringLocation = X509_NAME_get_index_by_NID(name, nid, -1);
|
||||
X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, stringLocation);
|
||||
if (ASN1_STRING* string = X509_NAME_ENTRY_get_data(entry))
|
||||
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;
|
||||
|
|
|
@ -297,6 +297,8 @@ public:
|
|||
Error_Signature_DataCoveredBySignatureMissing = 0x00100000, ///< Data covered by signature are not present
|
||||
|
||||
Warning_Signature_NotCoveredBytes = 0x00200000, ///< Some bytes in source data are not covered by signature
|
||||
Warning_Certificate_CRLValidityTimeExpired = 0x00400000, ///< Certificate revocation list was not checked, because it's validity expired
|
||||
Warning_Certificate_QualifiedStatement = 0x00800000, ///< Qualified certificate statement not verified
|
||||
|
||||
Error_Certificates_Mask = Error_Certificate_Invalid | Error_Certificate_NoSignatures | Error_Certificate_Missing | Error_Certificate_Generic |
|
||||
Error_Certificate_Expired | Error_Certificate_SelfSigned | Error_Certificate_SelfSignedChain | Error_Certificate_TrustedNotFound |
|
||||
|
@ -305,7 +307,7 @@ public:
|
|||
Error_Signatures_Mask = Error_Signature_Invalid | Error_Signature_SourceCertificateMissing | Error_Signature_NoSignaturesFound |
|
||||
Error_Signature_DigestFailure | Error_Signature_DataOther | Error_Signature_DataCoveredBySignatureMissing,
|
||||
|
||||
Warnings_Mask = Warning_Signature_NotCoveredBytes
|
||||
Warnings_Mask = Warning_Signature_NotCoveredBytes | Warning_Certificate_CRLValidityTimeExpired | Warning_Certificate_QualifiedStatement
|
||||
};
|
||||
Q_DECLARE_FLAGS(VerificationFlags, VerificationFlag)
|
||||
|
||||
|
@ -329,7 +331,10 @@ public:
|
|||
void addSignatureDigestFailureError();
|
||||
void addSignatureDataOtherError();
|
||||
void addSignatureDataCoveredBySignatureMissingError();
|
||||
|
||||
void addSignatureNotCoveredBytesWarning(PDFInteger count);
|
||||
void addCertificateCRLValidityTimeExpiredWarning();
|
||||
void addCertificateQualifiedStatementNotVerifiedWarning();
|
||||
|
||||
bool isValid() const { return hasFlag(OK); }
|
||||
bool isCertificateValid() const { return hasFlag(Certificate_OK); }
|
||||
|
|
|
@ -56,6 +56,9 @@ public:
|
|||
/// Returns name converted to QString
|
||||
static QString getStringFromX509Name(X509_NAME* name, int nid);
|
||||
|
||||
/// Returns ASN1 string converted to QString
|
||||
static QString getStringFromASN1_STRING(ASN1_STRING* string);
|
||||
|
||||
/// Converts ASN time to QDateTime. If conversion fails, then invalid
|
||||
/// datetime is returned.
|
||||
static QDateTime getDateTimeFromASN(const ASN1_TIME* time);
|
||||
|
@ -126,6 +129,7 @@ public:
|
|||
|
||||
private:
|
||||
void verifyCertificateCAdES(PDFSignatureVerificationResult& result) const;
|
||||
static int verifyCallback(int ok, X509_STORE_CTX* context);
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
|
Loading…
Reference in New Issue