From 7c78027a67e2f26d942c5a3db18304557ad4c21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-Ulrich=20J=C3=BCttner?= Date: Wed, 28 Jun 2017 13:37:22 +0200 Subject: [PATCH] Export signature via Qt5 interface. Add support for signatures of SubFilter "ETSI.CAdES.detached". Add an optional validation time to method validateSignature(). Print full Subject Distinguished Name, signing time, hash algorithm and a statement wether the total document is signed in pdfsig. Fixes bug #99271 --- poppler/Form.cc | 189 ++++++++++++++++++++++++++++++++++++++++++-- poppler/Form.h | 34 +++++++- poppler/SignatureHandler.cc | 34 +++++++- poppler/SignatureHandler.h | 5 +- poppler/SignatureInfo.cc | 33 +++++++- poppler/SignatureInfo.h | 9 ++- qt5/src/poppler-form.cc | 112 ++++++++++++++++++++++++++ qt5/src/poppler-form.h | 58 ++++++++++++++ utils/pdfsig.1 | 6 +- utils/pdfsig.cc | 86 ++++++++++++++++++++ 10 files changed, 548 insertions(+), 18 deletions(-) diff --git a/poppler/Form.cc b/poppler/Form.cc index 4627a43..4fe9df4 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -28,10 +28,12 @@ #endif #include +#include #include #include #include "goo/gmem.h" #include "goo/GooString.h" +#include "goo/GooList.h" #include "Error.h" #include "Object.h" #include "Array.h" @@ -442,11 +444,164 @@ FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *aobj, unsigned nu FormWidget(docA, aobj, num, ref, p) { type = formSignature; + file_size = 0; } -SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation) +SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime) { - return static_cast(field)->validateSignature(doVerifyCert, forceRevalidation); + return static_cast(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime); +} + +std::list FormWidgetSignature::getSignedRangeBounds() +{ + Object* obj = static_cast(field)->getByteRange(); + std::list list; + if (obj && obj->isArray()) + { + if (obj->arrayGetLength() == 4) + { + for (int i = 0; i < 2; ++i) + { + Object offsetObj, lenObj; + obj->arrayGet(2*i, &offsetObj); + obj->arrayGet(2*i+1, &lenObj); + if (offsetObj.isIntOrInt64() && lenObj.isIntOrInt64()) + { + Goffset offset = offsetObj.getIntOrInt64(); + Goffset len = lenObj.getIntOrInt64(); + list.push_back(offset); + list.push_back(offset+len); + } + offsetObj.free(); + lenObj.free(); + } + } + } + return list; +} + +GooString* FormWidgetSignature::getCheckedSignature() +{ + Goffset start = 0; + Goffset end = 0; + Object* obj = static_cast(field)->getByteRange(); + if (obj->isArray() && obj->arrayGetLength() == 4) + { + Object offsetObj1, lenObj, offsetObj2; + obj->arrayGet(0, &offsetObj1); + obj->arrayGet(1, &lenObj); + obj->arrayGet(2, &offsetObj2); + if (offsetObj1.isIntOrInt64() && offsetObj2.isIntOrInt64() && lenObj.isIntOrInt64()) + { + start = offsetObj1.getIntOrInt64() + lenObj.getIntOrInt64(); + end = offsetObj2.getIntOrInt64(); + } + offsetObj1.free(); + lenObj.free(); + offsetObj2.free(); + } + if (end >= start+6) + { + BaseStream* stream = doc->getBaseStream(); + file_size = stream->getLength(); + Goffset len = end-start; + stream->setPos(end-1); + int c2 = stream->lookChar(); + stream->setPos(start); + int c1 = stream->getChar(); + // PDF signatures are first ASN1 DER, then hex encoded PKCS#7 structures, + // possibly padded with 0 characters and enclosed in '<' and '>'. + // The ASN1 DER encoding of a PKCS#7 structure must start with the tag 0x30 + // for SEQUENCE. The next byte must be 0x80 for ASN1 DER indefinite length + // encoding or (0x80 + n) for ASN1 DER definite length encoding + // where n is the number of subsequent "length bytes" which big-endian + // encode the length of the content of the SEQUENCE following them. + if (len <= std::numeric_limits::max() && file_size > end && c1 == '<' && c2 == '>') + { + GooString gstr; + ++start; + --end; + len = end-start; + Goffset pos = 0; + do + { + c1 = stream->getChar(); + if (c1 == EOF) + return nullptr; + gstr.append(static_cast(c1)); + } while (++pos < len); + if (gstr.getChar(0) == '3' && gstr.getChar(1) == '0') + { + if (gstr.getChar(2) == '8' && gstr.getChar(3) == '0') + { + // ASN1 DER indefinite length encoding: + // We only check that all characters up to the enclosing '>' + // are hex characters and that there are two hex encoded 0 bytes + // just before the enclosing '>' marking the end of the indefinite + // length encoding. + int paddingCount = 0; + while (gstr.getChar(len-1) == '0' && gstr.getChar(len-2) == '0') + { + ++paddingCount; + len -= 2; + } + if (paddingCount < 2 || len%2 == 1) + len = 0; + } + else if (gstr.getChar(2) == '8') + { + // ASN1 DER definite length encoding: + // We calculate the length of the following bytes from the length bytes and + // check that after the length bytes and the following calculated number of + // bytes all bytes up to the enclosing '>' character are hex encoded 0 bytes. + int lenBytes = gstr.getChar(3) - '0'; + if (lenBytes > 0 && lenBytes <= 4) + { + int sigLen = 0; + for (int i = 0; i < 2*lenBytes; ++i) + { + sigLen <<= 4; + char c = gstr.getChar(i+4); + if (isdigit(c)) + sigLen += c - '0'; + else if (isxdigit(c) && c >= 'a') + sigLen += c - 'a' + 10; + else if (isxdigit(c) && c >= 'A') + sigLen += c - 'A' + 10; + else + { + len = 0; + break; + } + } + if (sigLen > 0 && 2*(sigLen+lenBytes) <= len-4) + { + for (int i = 2*(sigLen+lenBytes)+4; i < len; ++i) + { + if (gstr.getChar(i) != '0') + { + len = 0; + break; + } + } + } + else + len = 0; + } + } + for (int i = 0; i < len; ++i) + { + if (!isxdigit(gstr.getChar(i))) + len = 0; + } + if (len > 0) + { + return new GooString(&gstr, 0, len); + } + } + } + } + return nullptr; } void FormWidgetSignature::updateWidgetAppearance() @@ -1386,7 +1541,9 @@ GooString *FormFieldChoice::getSelectedChoice() { // FormFieldSignature //------------------------------------------------------------------------ FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& ref, FormField *parent, std::set *usedParents) - : FormField(docA, dict, ref, parent, usedParents, formSignature) + : FormField(docA, dict, ref, parent, usedParents, formSignature), + signature_type(adbe_pkcs7_detached), byte_range(), + signature(nullptr), signature_info(nullptr) { signature = NULL; @@ -1433,7 +1590,16 @@ void FormFieldSignature::parseInfo() // check if subfilter is supported for signature validation, only detached signatures work for now sig_dict.dictLookup("SubFilter", &subfilterName); - if (subfilterName.isName("adbe.pkcs7.detached") || subfilterName.isName("adbe.pkcs7.sha1")) { + if (subfilterName.isName("adbe.pkcs7.sha1")) { + signature_type = adbe_pkcs7_sha1; + signature_info->setSubFilterSupport(true); + } + else if (subfilterName.isName("adbe.pkcs7.detached")) { + signature_type = adbe_pkcs7_detached; + signature_info->setSubFilterSupport(true); + } + else if (subfilterName.isName("ETSI.CAdES.detached")) { + signature_type = ETSI_CAdES_detached; signature_info->setSubFilterSupport(true); } @@ -1467,8 +1633,17 @@ void FormFieldSignature::hashSignedDataBlock(SignatureHandler *handler, Goffset #endif } +FormSignatureType FormWidgetSignature::signatureType() +{ + return static_cast(field)->signature_type; +} + +void FormWidgetSignature::setFormSignatureType(FormSignatureType type) +{ + static_cast(field)->signature_type = type; +} -SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation) +SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime) { #ifdef ENABLE_NSS3 if (!signature_info->isSubfilterSupported()) { @@ -1529,6 +1704,8 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for sig_val_state = signature_handler.validateSignature(); signature_info->setSignatureValStatus(SignatureHandler::NSS_SigTranslate(sig_val_state)); signature_info->setSignerName(signature_handler.getSignerName()); + signature_info->setSubjectDN(signature_handler.getSignerSubjectDN()); + signature_info->setHashAlgorithm(signature_handler.getHashAlgorithm()); // verify if signature contains a 'signing time' attribute if (signature_handler.getSigningTime() != 0) { @@ -1539,7 +1716,7 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for return signature_info; } - cert_val_state = signature_handler.validateCertificate(); + cert_val_state = signature_handler.validateCertificate(validationTime); signature_info->setCertificateValStatus(SignatureHandler::NSS_CertTranslate(cert_val_state)); #endif diff --git a/poppler/Form.h b/poppler/Form.h index 8ddb6fe..9be4d8c 100644 --- a/poppler/Form.h +++ b/poppler/Form.h @@ -13,6 +13,7 @@ // Copyright 2013 Adrian Johnson // Copyright 2015 André Guerreiro // Copyright 2015 André Esser +// Copyright 2017 Hans-Ulrich Jüttner // //======================================================================== @@ -27,8 +28,10 @@ #include "Annot.h" #include +#include class GooString; +class GooList; class Array; class Dict; class Annot; @@ -60,6 +63,12 @@ enum VariableTextQuadding { quaddingRightJustified }; +enum FormSignatureType { + adbe_pkcs7_sha1, + adbe_pkcs7_detached, + ETSI_CAdES_detached +}; + class Form; class FormField; class FormFieldButton; @@ -251,7 +260,23 @@ public: FormWidgetSignature(PDFDoc *docA, Object *dict, unsigned num, Ref ref, FormField *p); void updateWidgetAppearance() override; - SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation); + FormSignatureType signatureType(); + void setFormSignatureType(FormSignatureType type); + SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime = -1); + + // returns a list with the boundaries of the signed ranges + // the elements of the list are of type Goffset + std::list getSignedRangeBounds(); + + // checks the length encoding of the signature and returns the hex encoded signature + // if the check passed otherwise a nullptr is returned + GooString* getCheckedSignature(); + + // this method only gives the correct file size if getCheckedSignature() + // has been called before + Goffset getCheckedFileSize() const { return file_size; } +protected: + Goffset file_size; }; //------------------------------------------------------------------------ @@ -490,16 +515,21 @@ protected: //------------------------------------------------------------------------ class FormFieldSignature: public FormField { + friend class FormWidgetSignature; public: FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& ref, FormField *parent, std::set *usedParents); - SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation); + SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime = -1); ~FormFieldSignature(); + Object* getByteRange() { return &byte_range; } + GooString* getSignature() { return signature; } private: void parseInfo(); void hashSignedDataBlock(SignatureHandler *handler, Goffset block_len); + + FormSignatureType signature_type; Object byte_range; GooString *signature; SignatureInfo *signature_info; diff --git a/poppler/SignatureHandler.cc b/poppler/SignatureHandler.cc index 71644e5..bddc45f 100644 --- a/poppler/SignatureHandler.cc +++ b/poppler/SignatureHandler.cc @@ -9,6 +9,7 @@ // Copyright 2015, 2016 Albert Astals Cid // Copyright 2015 Markus Kilås // Copyright 2017 Sebastian Rasmussen +// Copyright 2017 Hans-Ulrich Jüttner // //======================================================================== @@ -47,6 +48,26 @@ char *SignatureHandler::getSignerName() return CERT_GetCommonName(&cert->subject); } +const char * SignatureHandler::getSignerSubjectDN() +{ + if (!CMSSignerInfo) + return nullptr; + + CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); + if (!cert) + return nullptr; + return cert->subjectName; +} + +HASH_HashType SignatureHandler::getHashAlgorithm() +{ + if (hash_context && hash_context->hashobj) + { + return hash_context->hashobj->type; + } + return HASH_AlgNULL; +} + time_t SignatureHandler::getSigningTime() { PRTime sTime; // time in microseconds since the epoch @@ -54,7 +75,7 @@ time_t SignatureHandler::getSigningTime() if (NSS_CMSSignerInfo_GetSigningTime (CMSSignerInfo, &sTime) != SECSuccess) return 0; - return (time_t) sTime/1000000; + return static_cast(sTime/1000000); } @@ -271,7 +292,7 @@ NSSCMSVerificationStatus SignatureHandler::validateSignature() } } -SECErrorCodes SignatureHandler::validateCertificate() +SECErrorCodes SignatureHandler::validateCertificate(time_t validation_time) { SECErrorCodes retVal; CERTCertificate *cert; @@ -282,10 +303,15 @@ SECErrorCodes SignatureHandler::validateCertificate() if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == NULL) CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound; - CERTValInParam inParams[2]; + PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now + if (validation_time > 0) + vTime = 1000000*(PRTime)validation_time; + CERTValInParam inParams[3]; inParams[0].type = cert_pi_revocationFlags; inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy(); - inParams[1].type = cert_pi_end; + inParams[1].type = cert_pi_date; + inParams[1].value.scalar.time = vTime; + inParams[2].type = cert_pi_end; CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, NULL, CMSSignerInfo->cmsg->pwfn_arg); diff --git a/poppler/SignatureHandler.h b/poppler/SignatureHandler.h index 8e2a4da..715ab9f 100644 --- a/poppler/SignatureHandler.h +++ b/poppler/SignatureHandler.h @@ -7,6 +7,7 @@ // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2015 Albert Astals Cid +// Copyright 2017 Hans-Ulrich Jüttner // //======================================================================== @@ -36,10 +37,12 @@ public: ~SignatureHandler(); time_t getSigningTime(); char * getSignerName(); + const char * getSignerSubjectDN(); + HASH_HashType getHashAlgorithm(); void setSignature(unsigned char *, int); void updateHash(unsigned char * data_block, int data_len); NSSCMSVerificationStatus validateSignature(); - SECErrorCodes validateCertificate(); + SECErrorCodes validateCertificate(time_t validation_time = -1); //Translate NSS error codes static SignatureValidationStatus NSS_SigTranslate(NSSCMSVerificationStatus nss_code); diff --git a/poppler/SignatureInfo.cc b/poppler/SignatureInfo.cc index 8f7ec45..86b1cfa 100644 --- a/poppler/SignatureInfo.cc +++ b/poppler/SignatureInfo.cc @@ -6,6 +6,7 @@ // // Copyright 2015 André Guerreiro // Copyright 2015 André Esser +// Copyright 2017 Hans-Ulrich Jüttner // //======================================================================== @@ -16,13 +17,17 @@ #include #include +#include + /* Constructor & Destructor */ SignatureInfo::SignatureInfo() { sig_status = SIGNATURE_NOT_VERIFIED; cert_status = CERTIFICATE_NOT_VERIFIED; - signer_name = NULL; + signer_name = nullptr; + subject_dn = nullptr; + hash_type = HASH_AlgNULL; signing_time = 0; sig_subfilter_supported = false; } @@ -31,7 +36,9 @@ SignatureInfo::SignatureInfo(SignatureValidationStatus sig_val_status, Certifica { sig_status = sig_val_status; cert_status = cert_val_status; - signer_name = NULL; + signer_name = nullptr; + subject_dn = nullptr; + hash_type = HASH_AlgNULL; signing_time = 0; sig_subfilter_supported = false; } @@ -53,11 +60,21 @@ CertificateValidationStatus SignatureInfo::getCertificateValStatus() return cert_status; } -char *SignatureInfo::getSignerName() +const char *SignatureInfo::getSignerName() { return signer_name; } +const char *SignatureInfo::getSubjectDN() +{ + return subject_dn; +} + +int SignatureInfo::getHashAlgorithm() +{ + return hash_type; +} + time_t SignatureInfo::getSigningTime() { return signing_time; @@ -81,6 +98,16 @@ void SignatureInfo::setSignerName(char *signerName) signer_name = signerName; } +void SignatureInfo::setSubjectDN(const char *subjectDN) +{ + subject_dn = subjectDN; +} + +void SignatureInfo::setHashAlgorithm(int type) +{ + hash_type = type; +} + void SignatureInfo::setSigningTime(time_t signingTime) { signing_time = signingTime; diff --git a/poppler/SignatureInfo.h b/poppler/SignatureInfo.h index 82b4ec4..7ed4428 100644 --- a/poppler/SignatureInfo.h +++ b/poppler/SignatureInfo.h @@ -7,6 +7,7 @@ // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2015 Albert Astals Cid +// Copyright 2017 Hans-Ulrich Jüttner // //======================================================================== @@ -46,7 +47,9 @@ public: /* GETTERS */ SignatureValidationStatus getSignatureValStatus(); CertificateValidationStatus getCertificateValStatus(); - char *getSignerName(); + const char *getSignerName(); + const char *getSubjectDN(); + int getHashAlgorithm(); time_t getSigningTime(); bool isSubfilterSupported() { return sig_subfilter_supported; } @@ -54,6 +57,8 @@ public: void setSignatureValStatus(enum SignatureValidationStatus ); void setCertificateValStatus(enum CertificateValidationStatus ); void setSignerName(char *); + void setSubjectDN(const char *); + void setHashAlgorithm(int); void setSigningTime(time_t); void setSubFilterSupport(bool isSupported) { sig_subfilter_supported = isSupported; } @@ -64,6 +69,8 @@ private: SignatureValidationStatus sig_status; CertificateValidationStatus cert_status; char *signer_name; + const char *subject_dn; + int hash_type; time_t signing_time; bool sig_subfilter_supported; }; diff --git a/qt5/src/poppler-form.cc b/qt5/src/poppler-form.cc index 1ccb26c..0149018 100644 --- a/qt5/src/poppler-form.cc +++ b/qt5/src/poppler-form.cc @@ -4,6 +4,7 @@ * Copyright (C) 2011 Carlos Garcia Campos * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow + * Copyright (C) 2017, Hans-Ulrich Jüttner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +36,20 @@ #include "poppler-annotation-helper.h" #include +#include + +enum HASH_HashType +{ + HASH_AlgNULL = 0, + HASH_AlgMD2 = 1, + HASH_AlgMD5 = 2, + HASH_AlgSHA1 = 3, + HASH_AlgSHA256 = 4, + HASH_AlgSHA384 = 5, + HASH_AlgSHA512 = 6, + HASH_AlgSHA224 = 7, + HASH_AlgTOTAL +}; namespace { @@ -448,8 +463,13 @@ struct SignatureValidationInfoPrivate { SignatureValidationInfo::SignatureStatus signature_status; SignatureValidationInfo::CertificateStatus certificate_status; + QByteArray signature; QString signer_name; + QString signer_subject_dn; + int hash_algorithm; time_t signing_time; + QList range_bounds; + qint64 docLength; }; @@ -485,12 +505,62 @@ QString SignatureValidationInfo::signerName() const return d->signer_name; } +QString SignatureValidationInfo::signerSubjectDN() const +{ + Q_D(const SignatureValidationInfo); + return d->signer_subject_dn; +} + +SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const +{ + Q_D(const SignatureValidationInfo); + return static_cast(d->hash_algorithm); +} + time_t SignatureValidationInfo::signingTime() const { Q_D(const SignatureValidationInfo); return d->signing_time; } +QDateTime SignatureValidationInfo::signingDateTime() const +{ + return QDateTime::fromTime_t(signingTime()); +} + +QByteArray SignatureValidationInfo::signature() const +{ + Q_D(const SignatureValidationInfo); + return d->signature; +} + +QList SignatureValidationInfo::signedRangeBounds() const +{ + Q_D(const SignatureValidationInfo); + return d->range_bounds; +} + +bool SignatureValidationInfo::signsTotalDocument() const +{ + Q_D(const SignatureValidationInfo); + if (d->range_bounds.size() == 4 && d->range_bounds.value(0) == 0 && + d->range_bounds.value(1) >= 0 && + d->range_bounds.value(2) > d->range_bounds.value(1) && + d->range_bounds.value(3) >= d->range_bounds.value(2)) + { + // The range from d->range_bounds.value(1) to d->range_bounds.value(2) is + // not authenticated by the signature and should only contain the signature + // itself padded with 0 bytes. This has been checked in readSignature(). + // If it failed, d->signature is empty. + // A potential range after d->range_bounds.value(3) would be also not + // authenticated. Therefore d->range_bounds.value(3) should coincide with + // the end of the document. + if (d->docLength == d->range_bounds.value(3) && !d->signature.isEmpty()) + return true; + } + return false; +} + SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValidationInfo &other) { if ( this != &other ) @@ -512,9 +582,33 @@ FormField::FormType FormFieldSignature::type() const { return FormField::FormSignature; } + +FormFieldSignature::SignatureType FormFieldSignature::signatureType() const +{ + SignatureType sigType = AdbePkcs7detached; + FormWidgetSignature* fws = static_cast(m_formData->fm); + switch (fws->signatureType()) + { + case adbe_pkcs7_sha1: + sigType = AdbePkcs7sha1; + break; + case adbe_pkcs7_detached: + sigType = AdbePkcs7detached; + break; + case ETSI_CAdES_detached: + sigType = EtsiCAdESdetached; + break; + } + return sigType; +} SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const { + return validate(opt, QDateTime()); +} + +SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime& validationTime) const +{ FormWidgetSignature* fws = static_cast(m_formData->fm); SignatureInfo* si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation); SignatureValidationInfoPrivate* priv = new SignatureValidationInfoPrivate; @@ -567,7 +661,25 @@ SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const break; } priv->signer_name = si->getSignerName(); + priv->signer_subject_dn = si->getSubjectDN(); + priv->hash_algorithm = si->getHashAlgorithm(); + priv->signing_time = si->getSigningTime(); + std::list list = fws->getSignedRangeBounds(); + if (!list.empty()) + { + for (Goffset bound : list) + { + priv->range_bounds.append(bound); + } + } + GooString* checkedSignature = fws->getCheckedSignature(); + if (priv->range_bounds.size() == 4 && checkedSignature) + { + priv->signature = QByteArray::fromHex(checkedSignature->getCString()); + priv->docLength = fws->getCheckedFileSize(); + } + delete checkedSignature; return SignatureValidationInfo(priv); } diff --git a/qt5/src/poppler-form.h b/qt5/src/poppler-form.h index 44928b3..7a8d575 100644 --- a/qt5/src/poppler-form.h +++ b/qt5/src/poppler-form.h @@ -3,6 +3,7 @@ * Copyright (C) 2008, 2011, 2016, 2017, Albert Astals Cid * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow + * Copyright (C) 2017, Hans-Ulrich Jüttner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +23,8 @@ #ifndef _POPPLER_QT5_FORM_H_ #define _POPPLER_QT5_FORM_H_ +#include +#include #include #include #include @@ -401,6 +404,22 @@ namespace Poppler { CertificateNotVerified ///< The certificate is not yet verified. }; + /** + * The hash algorithme of the signature + */ + enum HashAlgorithm + { + HashAlgNULL = 0, + HashAlgMD2 = 1, + HashAlgMD5 = 2, + HashAlgSHA1 = 3, + HashAlgSHA256 = 4, + HashAlgSHA384 = 5, + HashAlgSHA512 = 6, + HashAlgSHA224 = 7, + HashAlgTOTAL + }; + /// \cond PRIVATE SignatureValidationInfo(SignatureValidationInfoPrivate *priv); /// \endcond @@ -422,9 +441,36 @@ namespace Poppler { QString signerName() const; /** + The signer subject distinguished name associated with the signature. + */ + QString signerSubjectDN() const; + + /** + The the hash algorithm used for the signature. + */ + HashAlgorithm hashAlgorithm() const; + + /** The signing time associated with the signature. */ time_t signingTime() const; + QDateTime signingDateTime() const; + + /** + Get the signature binary data. + */ + QByteArray signature() const; + + /** + Get the bounds of the ranges of the document which are signed. + */ + QList signedRangeBounds() const; + + /** + Checks whether the signature authenticates the total document + except for the signature itself. + */ + bool signsTotalDocument() const; SignatureValidationInfo(const SignatureValidationInfo &other); SignatureValidationInfo &operator=(const SignatureValidationInfo &other); @@ -443,6 +489,15 @@ namespace Poppler { class POPPLER_QT5_EXPORT FormFieldSignature : public FormField { public: + /** + The types of signature fields. + */ + enum SignatureType { + AdbePkcs7sha1, + AdbePkcs7detached, + EtsiCAdESdetached + }; + /** The validation options of this signature. */ @@ -458,12 +513,15 @@ namespace Poppler { FormType type() const override; + SignatureType signatureType() const; + /** Validate the signature. Reset signature validatation info of scoped instance. */ SignatureValidationInfo validate(ValidateOptions opt) const; + SignatureValidationInfo validate(int opt, const QDateTime& validationTime) const; private: Q_DISABLE_COPY(FormFieldSignature) diff --git a/utils/pdfsig.1 b/utils/pdfsig.1 index 8029ff0..99ca056 100644 --- a/utils/pdfsig.1 +++ b/utils/pdfsig.1 @@ -9,7 +9,11 @@ pdfsig \- Portable Document Format (PDF) digital signatures tool .SH DESCRIPTION .B pdfsig verifies the digital signatures in a PDF document. -It also displays the identity of each signer (commonName field of the signer certificate) and the time and date of the signature. +It also displays the identity of each signer +(commonName field and full distinguished name of the signer certificate), +the time and date of the signature, the hash algorithm used for signing, +the type of the signature as stated in the PDF and +the signed ranges with a statement wether the total document is signed. .PP The signer certificate validation uses the trusted certificates stored in the following locations: .IP \(bu diff --git a/utils/pdfsig.cc b/utils/pdfsig.cc index 2190fea..cc6b04e 100644 --- a/utils/pdfsig.cc +++ b/utils/pdfsig.cc @@ -8,6 +8,7 @@ // Copyright 2015 André Esser // Copyright 2015 Albert Astals Cid // Copyright 2016 Markus Kilås +// Copyright 2017 Hans-Ulrich Jüttner // //======================================================================== @@ -18,6 +19,7 @@ #include #include #include +#include "goo/GooList.h" #include "parseargs.h" #include "Object.h" #include "Array.h" @@ -29,6 +31,19 @@ #include "SignatureInfo.h" +enum HASH_HashType +{ + HASH_AlgNULL = 0, + HASH_AlgMD2 = 1, + HASH_AlgMD5 = 2, + HASH_AlgSHA1 = 3, + HASH_AlgSHA256 = 4, + HASH_AlgSHA384 = 5, + HASH_AlgSHA512 = 6, + HASH_AlgSHA224 = 7, + HASH_AlgTOTAL +}; + const char * getReadableSigState(SignatureValidationStatus sig_vs) { switch(sig_vs) { @@ -157,7 +172,78 @@ int main(int argc, char *argv[]) sig_info = sig_widgets.at(i)->validateSignature(!dontVerifyCert, false); printf("Signature #%u:\n", i+1); printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName()); + printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN()); printf(" - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime())); + printf(" - Signing Hash Algorithm: "); + switch (sig_info->getHashAlgorithm()) + { + case HASH_AlgMD2: + printf("MD2\n"); + break; + case HASH_AlgMD5: + printf("MD5\n"); + break; + case HASH_AlgSHA1: + printf("SHA1\n"); + break; + case HASH_AlgSHA256: + printf("SHA-256\n"); + break; + case HASH_AlgSHA384: + printf("SHA-384\n"); + break; + case HASH_AlgSHA512: + printf("SHA-512\n"); + break; + case HASH_AlgSHA224: + printf("SHA-224\n"); + break; + default: + printf("unknown\n"); + } + printf(" - Signature Type: "); + switch (sig_widgets.at(i)->signatureType()) + { + case adbe_pkcs7_sha1: + printf("adbe.pkcs7.sha1\n"); + break; + case adbe_pkcs7_detached: + printf("adbe.pkcs7.detached\n"); + break; + case ETSI_CAdES_detached: + printf("ETSI.CAdES.detached\n"); + break; + default: + printf("unknown\n"); + } + std::list range_list = sig_widgets.at(i)->getSignedRangeBounds(); + if (!range_list.empty()) + { + int i = 0; + long long bound = 0; + printf(" - Signed Ranges: "); + for (Goffset b : range_list) + { + bound = b; + if (i%2 == 0) + { + if (i > 0) + printf(", "); + printf("[%lld - ", bound); + } + else + printf("%lld]", bound); + ++i; + } + GooString* signature = sig_widgets.at(i)->getCheckedSignature(); + if (signature && sig_widgets.at(i)->getCheckedFileSize() == bound) + { + printf("\n - Total document signed\n"); + delete signature; + } + else + printf("\n - Not total document signed\n"); + } printf(" - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus())); gfree(time_str); if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) { -- 1.9.1