diff --git a/goo/GooString.cc b/goo/GooString.cc index 12592e4..b6dbda4 100644 --- a/goo/GooString.cc +++ b/goo/GooString.cc @@ -917,6 +917,11 @@ GBool GooString::hasUnicodeMarker(void) const return length > 1 && (s[0] & 0xff) == 0xfe && (s[1] & 0xff) == 0xff; } +GBool GooString::hasASN1Marker(void) const +{ + return length > 1 && (s[0] & 0xff) == 0x30 && (s[1] & 0xf0) == 0x80; +} + GooString *GooString::sanitizedName(GBool psmode) { GooString *name; diff --git a/goo/GooString.h b/goo/GooString.h index de70497..02c9a7e 100644 --- a/goo/GooString.h +++ b/goo/GooString.h @@ -162,6 +162,7 @@ public: GBool hasUnicodeMarker(void) const; GBool hasJustUnicodeMarker(void) const { return length == 2 && hasUnicodeMarker(); } + GBool hasASN1Marker(void) const; // Sanitizes the string so that it does // not contain any ( ) < > [ ] { } / % diff --git a/poppler/Form.cc b/poppler/Form.cc index 530b93e..e72b90b 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -443,6 +443,18 @@ FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *aobj, unsigned nu { type = formSignature; file_size = 0; + sig_start = 0; + sig_end = 0; +} + +FormSignatureType FormWidgetSignature::signatureType() +{ + return static_cast(field)->signature_type; +} + +void FormWidgetSignature::setFormSignatureType(FormSignatureType type) +{ + static_cast(field)->signature_type = type; } SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime) @@ -455,6 +467,50 @@ Object* FormWidgetSignature::getByteRange() return static_cast(field)->getByteRange(); } +GBool FormWidgetSignature::signDocument(const char* certNickname, const char* digestName, + const char* password, const char* reason) +{ + GBool ok = gFalse; + if (certNickname != nullptr) + { + time_t now = time(nullptr); + unsigned char tmp_buffer[4]; + memcpy(tmp_buffer, "PDF", 4); + SignatureHandler sigHandler(certNickname, SignatureHandler::getHashOidTag(digestName)); + sigHandler.updateHash(tmp_buffer, 4); + // calculate a signature over tmp_buffer with the certificate to get it's size + GooString* tmpSignature = sigHandler.signDetached(password, now); + if (tmpSignature != nullptr) + { + FormFieldSignature* signatureField = static_cast(field); + GooString gReason(reason != nullptr ? reason : "I approve this document"); + GooString gName(sigHandler.getSignerName()); + Object vObj; + ok = createSignature(vObj, gName, gReason, tmpSignature, &now); + if (ok) + { + // calculate hash with preliminary values for ranges + sigHandler.restartHash(); + prepareSignature(&sigHandler); + ok = updateSignature(vObj, tmpSignature); + } + if (ok) + { + // recalculate hash with the correct ranges + sigHandler.restartHash(); + prepareSignature(&sigHandler); + GooString* signature = sigHandler.signDetached(password, now); + if (signature != nullptr) + { + ok = updateSignature(vObj, signature); + signatureField->setSignature(signature); + } + } + } + } + return ok; +} + GooString* FormWidgetSignature::getCheckedSignature() { Goffset start = 0; @@ -535,7 +591,7 @@ GooString* FormWidgetSignature::getCheckedSignature() break; } } - if (sigLen > 0 && 2*(sigLen+lenBytes) < len-4) + if (sigLen > 0 && 2*(sigLen+lenBytes) <= len-4) { for (int i = 2*(sigLen+lenBytes)+4; i < len; ++i) { @@ -565,6 +621,158 @@ GooString* FormWidgetSignature::getCheckedSignature() return nullptr; } +GBool FormWidgetSignature::prepareSignature(SignatureHandler *handler) +{ + GBool ok = gFalse; + size_t size; + char* membuf; + FILE* mfp = open_memstream(&membuf, &size); + if (mfp != nullptr) + { + FileOutStream* outStr = new FileOutStream(mfp, 0); + doc->saveAs(outStr, writeForceIncremental); + fclose(mfp); + if (membuf != nullptr) + { + for (sig_start = 0; sig_start < size; ++sig_start) + { + if (strncmp(&membuf[sig_start], "/Contents <308", 14) == 0) + { + char* p = index(&membuf[sig_start+14], '>'); + if (p != nullptr) + { + sig_end = ++p - membuf; + p = strstr(&membuf[sig_end], "%%%%EOF\r\n"); + for (file_size = sig_end; file_size < size; ++file_size) + { + if (membuf[file_size] == '%' && membuf[file_size+1] == '%' && + membuf[file_size+2] == 'E' && membuf[file_size+3] == 'O' && + membuf[file_size+4] == 'F' && membuf[file_size+5] == '\r' && + membuf[file_size+6] == '\n') + { + file_size += 7; + ok = gTrue; + break; + } + } + if (ok) + { + sig_start += 10; + break; + } + } + } + } + Goffset range_offset = sig_end; + Goffset range_size = 0; + if (ok) + { + ok = gFalse; + for (range_offset = sig_end; range_offset < file_size; ++range_offset) + { + if (strncmp(&membuf[range_offset], "/ByteRange [0 ", 14) == 0) + { + char* p = index(&membuf[range_offset+14], ']'); + if (p != nullptr) + { + range_size = (++p - membuf) - range_offset; + ok = gTrue; + break; + } + } + } + } + if (ok) + { + char* range_buf = new char[128]; + Goffset end_size = file_size - sig_end; + Goffset s = sprintf(range_buf, "/ByteRange [0 %lld %lld %lld ]", + sig_start, sig_end, end_size); + char* buffer = new char[file_size-sig_end+64]; + memcpy(buffer, &membuf[sig_end], range_offset-sig_end); + memcpy(&buffer[range_offset-sig_end], range_buf, s); + memcpy(&buffer[range_offset-sig_end+s], &membuf[range_offset+range_size], + file_size-range_offset-range_size); + file_size += s - range_size; + handler->updateHash(reinterpret_cast(membuf), sig_start); + handler->updateHash(reinterpret_cast(buffer), file_size-sig_end); + delete range_buf; + delete buffer; + } + free(membuf); + } + } + return ok; +} + +GBool FormWidgetSignature::createSignature(Object &vObj, const GooString& name, + const GooString& reason, + const GooString* signature, + const time_t* sTime) +{ + GBool ok = obj.isDict(); + if (ok) + { + file_size = doc->getBaseStream()->getLength(); + Object bObj, obj1; + obj.dictSet("V", vObj.initDict(xref)); + vObj.dictAdd(copyString("Type"), obj1.initName("Sig")); + vObj.dictAdd(copyString("Filter"), obj1.initName("Adobe.PPKLite")); + switch (signatureType()) { + case adbe_pkcs7_sha1: + // we don't support signing with SubFilter "adbe.pkcs7.sha1" + case adbe_pkcs7_detached: + vObj.dictAdd(copyString("SubFilter"), obj1.initName("adbe.pkcs7.detached")); + break; + case ETSI_CAdES_detached: + vObj.dictAdd(copyString("SubFilter"), obj1.initName("ETSI.CAdES.detached")); + break; + } + vObj.dictAdd(copyString("Name"), obj1.initString(name.copy())); + char buf[24]; + size_t size = strftime(buf, 24, "D:%Y%m%d%H%M%S%z", localtime(sTime)); + if (size >= 2 && size < 22) + { + buf[size] = buf[size-1]; + buf[size-1] = buf[size-2]; + buf[size-2] = '\''; + buf[++size] = '\''; + buf[++size] = '\0'; + GooString gTime(buf, size); + vObj.dictAdd(copyString("M"), obj1.initString(gTime.copy())); + } + vObj.dictAdd(copyString("Reason"), obj1.initString(reason.copy())); + vObj.dictAdd(copyString("Contents"), obj1.initString(signature->copy())); + bObj.initArray(xref); + bObj.arrayAdd(obj1.initInt64(0)); + bObj.arrayAdd(obj1.initInt64(file_size)); + bObj.arrayAdd(obj1.initInt64(file_size+2*signature->getLength()+2)); + bObj.arrayAdd(obj1.initInt64(100)); + vObj.dictAdd(copyString("ByteRange"), &bObj); + xref->setModifiedObject(&obj, ref); + obj1.free(); + } + return ok; +} + +GBool FormWidgetSignature::updateSignature(Object &vObj, const GooString* signature) +{ + GBool ok = vObj.isDict(); + if (ok && signature != nullptr) + { + Object bObj, obj1; + vObj.dictSet("Contents", obj1.initString(signature->copy())); + bObj.initArray(xref); + bObj.arrayAdd(obj1.initInt64(0)); + bObj.arrayAdd(obj1.initInt64(sig_start)); + bObj.arrayAdd(obj1.initInt64(sig_end)); + bObj.arrayAdd(obj1.initInt64(file_size-sig_end)); + vObj.dictSet("ByteRange", &bObj); + obj1.free(); + } + return ok; +} + void FormWidgetSignature::updateWidgetAppearance() { // Unimplemented @@ -1507,10 +1715,10 @@ 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; - signature_info = new SignatureInfo(); parseInfo(); } @@ -1554,7 +1762,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); + } + 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); } diff --git a/poppler/Form.h b/poppler/Form.h index c595aa0..e13e889 100644 --- a/poppler/Form.h +++ b/poppler/Form.h @@ -61,6 +61,12 @@ enum VariableTextQuadding { quaddingRightJustified }; +enum FormSignatureType { + adbe_pkcs7_sha1, + adbe_pkcs7_detached, + ETSI_CAdES_detached +}; + class Form; class FormField; class FormFieldButton; @@ -252,18 +258,37 @@ public: FormWidgetSignature(PDFDoc *docA, Object *dict, unsigned num, Ref ref, FormField *p); void updateWidgetAppearance() override; + FormSignatureType signatureType(); + void setFormSignatureType(FormSignatureType type); SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime = -1); - Object* getByteRange(); + Object *getByteRange(); + + // creates or replaces the dictionary name "V" in the signature dictionary and + // fills it with the fields of the signature; the field "Contents" is the signature + // in PKCS#7 format, which is calculated over the byte range encompassing the whole + // document except for the signature itself; this byte range is specified in the + // field "ByteRange" in the dictionary "V" + // return success + GBool signDocument(const char *certNickname, const char *digestName, + const char *password, const char *reason = nullptr); // checks the length encoding of the signature and returns the hex encoded signature // if the check passed otherwise a nullptr is returned - GooString* getCheckedSignature(); + GooString *getCheckedSignature(); // this method only gives the correct file size if getCheckedSignature() // has been called before Goffset getCheckedFileSize() const { return file_size; } + protected: + GBool prepareSignature(SignatureHandler *handler); + GBool createSignature(Object &vObj, const GooString &name, const GooString &reason, + const GooString *signature, const time_t *sTime); + GBool updateSignature(Object &vObj, const GooString *signature); + Goffset file_size; + Goffset sig_start; + Goffset sig_end; }; //------------------------------------------------------------------------ @@ -502,6 +527,7 @@ protected: //------------------------------------------------------------------------ class FormFieldSignature: public FormField { + friend class FormWidgetSignature; public: FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& ref, FormField *parent, std::set *usedParents); @@ -509,10 +535,14 @@ public: ~FormFieldSignature(); Object* getByteRange() { return &byte_range; } + GooString* getSignature() { return signature; } + void setSignature(const GooString* sig) { if (sig != nullptr) signature = sig->copy(); } 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/PDFDoc.cc b/poppler/PDFDoc.cc index f02f192..bd132bb 100644 --- a/poppler/PDFDoc.cc +++ b/poppler/PDFDoc.cc @@ -1215,6 +1215,14 @@ void PDFDoc::writeString (GooString* s, OutStream* outStr, Guchar *fileKey, outStr->printf("%c", unescaped); } outStr->printf(") "); + } else if (s->hasASN1Marker()) { + const char* c = s->getCString(); + outStr->printf("<"); + for(int i=0; igetLength(); i++) { + unsigned char value = *(c+i)&0x000000ff; + outStr->printf("%2.2x", value); + } + outStr->printf("> "); } else { const char* c = s->getCString(); outStr->printf("("); diff --git a/poppler/SignatureHandler.cc b/poppler/SignatureHandler.cc index 456d688..22c7ac8 100644 --- a/poppler/SignatureHandler.cc +++ b/poppler/SignatureHandler.cc @@ -22,6 +22,18 @@ #include #include +/* NSS headers */ +#include + +void SignatureHandler::outputCallback(void* arg, const char* buf, unsigned long len) +{ + if (arg != nullptr && buf != nullptr) + { + GooString* gSignature = reinterpret_cast(arg); + gSignature->append(buf, len); + } +} + unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId) { switch(digestAlgId){ @@ -39,24 +51,50 @@ unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId) } } +SECOidTag SignatureHandler::getHashOidTag(const char* digestName) +{ + SECOidTag tag = SEC_OID_UNKNOWN; + if (strcmp(digestName, "SHA1") == 0) + { + tag = SEC_OID_SHA1; + } + else if (strcmp(digestName, "SHA256") == 0) + { + tag = SEC_OID_SHA256; + } + else if (strcmp(digestName, "SHA384") == 0) + { + tag = SEC_OID_SHA384; + } + else if (strcmp(digestName, "SHA512") == 0) + { + tag = SEC_OID_SHA512; + } + return tag; +} + char *SignatureHandler::getSignerName() { - if (CMSSignerInfo == nullptr) + if (signing_cert == nullptr && CMSSignerInfo == nullptr) return nullptr; - CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); - return CERT_GetCommonName(&cert->subject); + if (signing_cert == nullptr) + signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); + return CERT_GetCommonName(&signing_cert->subject); } const char * SignatureHandler::getSignerSubjectDN() { - if (CMSSignerInfo == nullptr) + if (signing_cert == nullptr && CMSSignerInfo == nullptr) return nullptr; - CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); - if (cert == nullptr) + if (signing_cert == nullptr) + signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); + + if (signing_cert == nullptr) return nullptr; - return cert->subjectName; + + return signing_cert->subjectName; } const char *SignatureHandler::getHashAlgorithmName() @@ -129,7 +167,7 @@ GooString *SignatureHandler::getDefaultFirefoxCertDB_Linux() /** * Initialise NSS */ -void SignatureHandler::init_nss() +void SignatureHandler::init_nss() { GooString *certDBPath = getDefaultFirefoxCertDB_Linux(); if (certDBPath == NULL) { @@ -145,11 +183,12 @@ void SignatureHandler::init_nss() SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length) - : hash_context(NULL), - CMSMessage(NULL), - CMSSignedData(NULL), - CMSSignerInfo(NULL), - temp_certs(NULL) + : hash_context(nullptr), + CMSMessage(nullptr), + CMSSignedData(nullptr), + CMSSignerInfo(nullptr), + signing_cert(nullptr), + temp_certs(nullptr) { init_nss(); CMSitem.data = p7; @@ -162,6 +201,23 @@ SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length) } } +SignatureHandler::SignatureHandler(const char *certNickname, SECOidTag digestAlgTag) + : hash_length(digestLength(digestAlgTag)), + digest_alg_tag(digestAlgTag), + CMSitem(), + hash_context(nullptr), + CMSMessage(nullptr), + CMSSignedData(nullptr), + CMSSignerInfo(nullptr), + signing_cert(nullptr), + temp_certs(nullptr) +{ + init_nss(); + CMSMessage = NSS_CMSMessage_Create(nullptr); + signing_cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), certNickname); + hash_context = HASH_Create(HASH_GetHashTypeByOidTag(digestAlgTag)); +} + HASHContext * SignatureHandler::initHashContext() { @@ -179,6 +235,13 @@ void SignatureHandler::updateHash(unsigned char * data_block, int data_len) } } +void SignatureHandler::restartHash() +{ + if (hash_context) + HASH_Destroy(hash_context); + hash_context = HASH_Create(HASH_GetHashTypeByOidTag(digest_alg_tag)); +} + SignatureHandler::~SignatureHandler() { SECITEM_FreeItem(&CMSitem, PR_FALSE); @@ -189,6 +252,8 @@ SignatureHandler::~SignatureHandler() if (CMSMessage) NSS_CMSMessage_Destroy(CMSMessage); + if (signing_cert) + CERT_DestroyCertificate(signing_cert); if (hash_context) HASH_Destroy(hash_context); @@ -251,7 +316,6 @@ NSSCMSSignerInfo *SignatureHandler::CMS_SignerInfoCreate(NSSCMSSignedData * cms_ { NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0); if (!signerInfo) { - printf("Error in NSS_CMSSignedData_GetSignerInfo()\n"); return NULL; } else { return signerInfo; @@ -342,6 +406,54 @@ SECErrorCodes SignatureHandler::validateCertificate(time_t validation_time) return retVal; } +GooString* SignatureHandler::signDetached(const char* password, time_t signing_time) +{ + if (hash_context == nullptr) + return nullptr; + unsigned char* digest_buffer = reinterpret_cast(PORT_Alloc(hash_length)); + unsigned int result_len = 0; + HASH_End(hash_context, digest_buffer, &result_len, hash_length); + SECItem digest; + digest.data = digest_buffer; + digest.len = result_len; + SEC_PKCS7ContentInfo* p7_info = + SEC_PKCS7CreateSignedData(signing_cert, certUsageEmailSigner, + CERT_GetDefaultCertDB(), digest_alg_tag, &digest, + nullptr, nullptr); + if (p7_info == nullptr) + { + return nullptr; + } + SEC_PKCS7SignerInfo** signerinfos = p7_info->content.signedData->signerInfos; + if (signerinfos == nullptr) + { + SEC_PKCS7DestroyContentInfo(p7_info); + return nullptr; + } + for (SEC_PKCS7SignerInfo* si = *signerinfos; si != nullptr; si = *++signerinfos) + { + if (si->cert != nullptr) + { + si->certList = CERT_CertChainFromCert(si->cert, certUsageEmailSigner, PR_TRUE); + } + } + SEC_PKCS7AddSigningTime(p7_info); + GooString* signature = new GooString; + PWData pwdata = + { + password != nullptr ? PWData::PW_PLAINTEXT : PWData::PW_NONE, + password + }; + + if (SEC_PKCS7Encode(p7_info, outputCallback, reinterpret_cast(signature), + nullptr, nullptr, &pwdata) != SECSuccess) + { + delete signature; + signature = nullptr; + } + SEC_PKCS7DestroyContentInfo(p7_info); + return signature; +} SignatureValidationStatus SignatureHandler::NSS_SigTranslate(NSSCMSVerificationStatus nss_code) { diff --git a/poppler/SignatureHandler.h b/poppler/SignatureHandler.h index 0b58da3..7ab3e6d 100644 --- a/poppler/SignatureHandler.h +++ b/poppler/SignatureHandler.h @@ -34,6 +34,7 @@ class SignatureHandler { public: SignatureHandler(unsigned char *p7, int p7_length); + SignatureHandler(const char *certNickname, SECOidTag digestAlgTag); ~SignatureHandler(); time_t getSigningTime(); char * getSignerName(); @@ -41,14 +42,27 @@ public: const char * getHashAlgorithmName(); void setSignature(unsigned char *, int); void updateHash(unsigned char * data_block, int data_len); + void restartHash(); NSSCMSVerificationStatus validateSignature(); SECErrorCodes validateCertificate(time_t validation_time = -1); + GooString * signDetached(const char * password, time_t signing_time = -1); //Translate NSS error codes static SignatureValidationStatus NSS_SigTranslate(NSSCMSVerificationStatus nss_code); static CertificateValidationStatus NSS_CertTranslate(SECErrorCodes nss_code); + static SECOidTag getHashOidTag(const char* digestName); private: + typedef struct { + enum { + PW_NONE = 0, + PW_FROMFILE = 1, + PW_PLAINTEXT = 2, + PW_EXTERNAL = 3 + } source; + const char * data; + } PWData; + SignatureHandler(const SignatureHandler &); SignatureHandler& operator=(const SignatureHandler &); @@ -60,13 +74,16 @@ private: NSSCMSSignedData *CMS_SignedDataCreate(NSSCMSMessage * cms_msg); NSSCMSSignerInfo *CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data); HASHContext * initHashContext(); + static void outputCallback(void* arg, const char* buf, unsigned long len); unsigned int hash_length; + SECOidTag digest_alg_tag; SECItem CMSitem; HASHContext *hash_context; NSSCMSMessage *CMSMessage; NSSCMSSignedData *CMSSignedData; NSSCMSSignerInfo *CMSSignerInfo; + CERTCertificate *signing_cert; CERTCertificate **temp_certs; }; diff --git a/qt5/src/poppler-form.cc b/qt5/src/poppler-form.cc index d829b40..fdc2edb 100644 --- a/qt5/src/poppler-form.cc +++ b/qt5/src/poppler-form.cc @@ -182,7 +182,6 @@ Link *FormField::additionalAction(AdditionalActionType type) const return action; } - FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(*new FormFieldData(doc, p, w)) { @@ -564,6 +563,73 @@ FormField::FormType FormFieldSignature::type() const return FormField::FormSignature; } +FormFieldSignature::SignatureType FormFieldSignature::signatureType() +{ + 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; +} + +void FormFieldSignature::setSignatureType(SignatureType type) +{ + FormWidgetSignature* fws = static_cast(m_formData->fm); + switch (type) + { + case AdbePkcs7sha1: + fws->setFormSignatureType(adbe_pkcs7_sha1); + break; + case AdbePkcs7detached: + fws->setFormSignatureType(adbe_pkcs7_detached); + break; + case EtsiCAdESdetached: + fws->setFormSignatureType(ETSI_CAdES_detached); + break; + } +} + +bool FormFieldSignature::sign(const QString& certNickname, const QString& password, + DigestAlgorithm digestAlg) +{ + FormWidgetSignature* fws = static_cast(m_formData->fm); + const char* digest = nullptr; + char* pw = password.isEmpty() ? nullptr : strdup(password.toUtf8().constData()); + char* name = strdup(certNickname.toUtf8().constData()); + switch (digestAlg) + { + case SHA1: + digest = "SHA1"; + break; + case SHA256: + digest = "SHA256"; + break; + case SHA384: + digest = "SHA384"; + break; + case SHA512: + digest = "SHA512"; + break; + default: + digest = "SHA256"; + break; + } + GBool ok = fws->signDocument(name, digest, pw, nullptr); + free(name); + free(pw); + return ok; +} + SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime& validationTime) const { FormWidgetSignature* fws = static_cast(m_formData->fm); @@ -642,9 +708,10 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime& v } } } - if (priv->range_bounds.size() == 4) + GooString* checkedSignature = fws->getCheckedSignature(); + if (priv->range_bounds.size() == 4 && checkedSignature != nullptr) { - priv->signature = QByteArray::fromHex(fws->getCheckedSignature()->getCString()); + priv->signature = QByteArray::fromHex(checkedSignature->getCString()); priv->docLength = fws->getCheckedFileSize(); } diff --git a/qt5/src/poppler-form.h b/qt5/src/poppler-form.h index d568083..c09d389 100644 --- a/qt5/src/poppler-form.h +++ b/qt5/src/poppler-form.h @@ -30,6 +30,7 @@ #include #include "poppler-export.h" +class Object; class Page; class FormWidget; class FormWidgetButton; @@ -472,6 +473,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. */ @@ -480,6 +490,13 @@ namespace Poppler { ValidateForceRevalidation = 2, ///< Force revalidation of the certificate. }; + enum DigestAlgorithm { + SHA1 = 1, + SHA256 = 2, + SHA384 = 3, + SHA512 = 4, + }; + /// \cond PRIVATE FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w); /// \endcond @@ -487,7 +504,21 @@ namespace Poppler { FormType type() const override; - /** + SignatureType signatureType(); + void setSignatureType(SignatureType type); + + /** + Sign the whole document in this signature field. + + @param certNickname nickname of the signing certificate in the mozilla database + @param password password to unlock the private key + (may be empty if no password is set) + @param digestAlg digest algorithm to be used in the signature + */ + bool sign(const QString& certNickname, const QString& password, + DigestAlgorithm digestAlg = SHA256); + + /** Validate the signature. Reset signature validatation info of scoped instance.