1//========================================================================
2//
3// pdfsig.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
8// Copyright 2015 André Esser <bepandre@hotmail.com>
9// Copyright 2015, 2017-2023 Albert Astals Cid <aacid@kde.org>
10// Copyright 2016 Markus Kilås <digital@markuspage.com>
11// Copyright 2017, 2019 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
12// Copyright 2017, 2019 Adrian Johnson <ajohnson@redneon.com>
13// Copyright 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
14// Copyright 2019 Alexey Pavlov <alexpux@gmail.com>
15// Copyright 2019. 2023 Oliver Sander <oliver.sander@tu-dresden.de>
16// Copyright 2019 Nelson Efrain A. Cruz <neac03@gmail.com>
17// Copyright 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
18// Copyright 2021 Theofilos Intzoglou <int.teo@gmail.com>
19// Copyright 2022 Felix Jung <fxjung@posteo.de>
20// Copyright 2022, 2024 Erich E. Hoover <erich.e.hoover@gmail.com>
21// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
22//
23//========================================================================
24
25#include "config.h"
26#include <poppler-config.h>
27#include <cstdio>
28#include <cstdlib>
29#include <cstddef>
30#include <cstring>
31#include <ctime>
32#include <fstream>
33#include <random>
34#include "parseargs.h"
35#include "Object.h"
36#include "Array.h"
37#include "goo/gbasename.h"
38#include "Page.h"
39#include "PDFDoc.h"
40#include "PDFDocFactory.h"
41#include "DateInfo.h"
42#include "Error.h"
43#include "GlobalParams.h"
44#ifdef ENABLE_NSS3
45# include "NSSCryptoSignBackend.h"
46#endif
47#include "CryptoSignBackend.h"
48#include "SignatureInfo.h"
49#include "Win32Console.h"
50#include "numberofcharacters.h"
51#include "UTF.h"
52#if __has_include(<libgen.h>)
53# include <libgen.h>
54#endif
55
56#ifdef HAVE_GETTEXT
57# include <libintl.h>
58# include <clocale>
59# define _(STRING) gettext(STRING)
60#else
61# define _(STRING) STRING
62#endif
63
64static const char *getReadableSigState(SignatureValidationStatus sig_vs)
65{
66 switch (sig_vs) {
67 case SIGNATURE_VALID:
68 return "Signature is Valid.";
69
70 case SIGNATURE_INVALID:
71 return "Signature is Invalid.";
72
73 case SIGNATURE_DIGEST_MISMATCH:
74 return "Digest Mismatch.";
75
76 case SIGNATURE_DECODING_ERROR:
77 return "Document isn't signed or corrupted data.";
78
79 case SIGNATURE_NOT_VERIFIED:
80 return "Signature has not yet been verified.";
81
82 default:
83 return "Unknown Validation Failure.";
84 }
85}
86
87static const char *getReadableCertState(CertificateValidationStatus cert_vs)
88{
89 switch (cert_vs) {
90 case CERTIFICATE_TRUSTED:
91 return "Certificate is Trusted.";
92
93 case CERTIFICATE_UNTRUSTED_ISSUER:
94 return "Certificate issuer isn't Trusted.";
95
96 case CERTIFICATE_UNKNOWN_ISSUER:
97 return "Certificate issuer is unknown.";
98
99 case CERTIFICATE_REVOKED:
100 return "Certificate has been Revoked.";
101
102 case CERTIFICATE_EXPIRED:
103 return "Certificate has Expired";
104
105 case CERTIFICATE_NOT_VERIFIED:
106 return "Certificate has not yet been verified.";
107
108 default:
109 return "Unknown issue with Certificate or corrupted data.";
110 }
111}
112
113static char *getReadableTime(time_t unix_time)
114{
115 char *time_str = (char *)gmalloc(size: 64);
116 strftime(s: time_str, maxsize: 64, format: "%b %d %Y %H:%M:%S", tp: localtime(timer: &unix_time));
117 return time_str;
118}
119
120static bool dumpSignature(int sig_num, int sigCount, FormFieldSignature *s, const char *filename)
121{
122 const GooString *signature = s->getSignature();
123 if (!signature) {
124 printf(format: "Cannot dump signature #%d\n", sig_num);
125 return false;
126 }
127
128 const int sigCountLength = numberOfCharacters(n: sigCount);
129 // We want format to be {0:s}.sig{1:Xd} where X is sigCountLength
130 // since { is the magic character to replace things we need to put it twice where
131 // we don't want it to be replaced
132 const std::unique_ptr<GooString> format = GooString::format(fmt: "{{0:s}}.sig{{1:{0:d}d}}", sigCountLength);
133 const std::unique_ptr<GooString> path = GooString::format(fmt: format->c_str(), gbasename(filename).c_str(), sig_num);
134 printf(format: "Signature #%d (%u bytes) => %s\n", sig_num, signature->getLength(), path->c_str());
135 std::ofstream outfile(path->c_str(), std::ofstream::binary);
136 outfile.write(s: signature->c_str(), n: signature->getLength());
137 outfile.close();
138
139 return true;
140}
141
142static GooString nssDir;
143static GooString nssPassword;
144static char ownerPassword[33] = "\001";
145static char userPassword[33] = "\001";
146static bool printVersion = false;
147static bool printHelp = false;
148static bool printCryptoSignBackends = false;
149static bool dontVerifyCert = false;
150static bool noOCSPRevocationCheck = false;
151static bool noAppearance = false;
152static bool dumpSignatures = false;
153static bool etsiCAdESdetached = false;
154static char backendString[256] = "";
155static char signatureName[256] = "";
156static char certNickname[256] = "";
157static char password[256] = "";
158static char digestName[256] = "SHA256";
159static GooString reason;
160static bool listNicknames = false;
161static bool addNewSignature = false;
162static bool useAIACertFetch = false;
163static GooString newSignatureFieldName;
164
165static const ArgDesc argDesc[] = { { .arg: "-nssdir", .kind: argGooString, .val: &nssDir, .size: 0, .usage: "path to directory of libnss3 database" },
166 { .arg: "-nss-pwd", .kind: argGooString, .val: &nssPassword, .size: 0, .usage: "password to access the NSS database (if any)" },
167 { .arg: "-nocert", .kind: argFlag, .val: &dontVerifyCert, .size: 0, .usage: "don't perform certificate validation" },
168 { .arg: "-no-ocsp", .kind: argFlag, .val: &noOCSPRevocationCheck, .size: 0, .usage: "don't perform online OCSP certificate revocation check" },
169 { .arg: "-no-appearance", .kind: argFlag, .val: &noAppearance, .size: 0, .usage: "don't add appearance information when signing existing fields" },
170 { .arg: "-aia", .kind: argFlag, .val: &useAIACertFetch, .size: 0, .usage: "use Authority Information Access (AIA) extension for certificate fetching" },
171 { .arg: "-dump", .kind: argFlag, .val: &dumpSignatures, .size: 0, .usage: "dump all signatures into current directory" },
172 { .arg: "-add-signature", .kind: argFlag, .val: &addNewSignature, .size: 0, .usage: "adds a new signature to the document" },
173 { .arg: "-new-signature-field-name", .kind: argGooString, .val: &newSignatureFieldName, .size: 0, .usage: "field name used for the newly added signature. A random ID will be used if empty" },
174 { .arg: "-sign", .kind: argString, .val: &signatureName, .size: 256, .usage: "sign the document in the given signature field (by name or number)" },
175 { .arg: "-etsi", .kind: argFlag, .val: &etsiCAdESdetached, .size: 0, .usage: "create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached" },
176 { .arg: "-backend", .kind: argString, .val: &backendString, .size: 256, .usage: "use given backend for signing/verification" },
177 { .arg: "-nick", .kind: argString, .val: &certNickname, .size: 256, .usage: "use the certificate with the given nickname/fingerprint for signing" },
178 { .arg: "-kpw", .kind: argString, .val: &password, .size: 256, .usage: "password for the signing key (might be missing if the key isn't password protected)" },
179 { .arg: "-digest", .kind: argString, .val: &digestName, .size: 256, .usage: "name of the digest algorithm (default: SHA256)" },
180 { .arg: "-reason", .kind: argGooString, .val: &reason, .size: 0, .usage: "reason for signing (default: no reason given)" },
181 { .arg: "-list-nicks", .kind: argFlag, .val: &listNicknames, .size: 0, .usage: "list available nicknames in the NSS database" },
182 { .arg: "-list-backends", .kind: argFlag, .val: &printCryptoSignBackends, .size: 0, .usage: "print cryptographic signature backends" },
183 { .arg: "-opw", .kind: argString, .val: ownerPassword, .size: sizeof(ownerPassword), .usage: "owner password (for encrypted files)" },
184 { .arg: "-upw", .kind: argString, .val: userPassword, .size: sizeof(userPassword), .usage: "user password (for encrypted files)" },
185 { .arg: "-v", .kind: argFlag, .val: &printVersion, .size: 0, .usage: "print copyright and version info" },
186 { .arg: "-h", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
187 { .arg: "-help", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
188 { .arg: "--help", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
189 { .arg: "-?", .kind: argFlag, .val: &printHelp, .size: 0, .usage: "print usage information" },
190 {} };
191
192static void print_version_usage(bool usage)
193{
194 fprintf(stderr, format: "pdfsig version %s\n", PACKAGE_VERSION);
195 fprintf(stderr, format: "%s\n", popplerCopyright);
196 fprintf(stderr, format: "%s\n", xpdfCopyright);
197 if (usage) {
198 printUsage(program: "pdfsig", otherArgs: "<PDF-file> [<output-file>]", args: argDesc);
199 }
200}
201
202static void print_backends()
203{
204 fprintf(stderr, format: "pdfsig backends:\n");
205 for (const auto &backend : CryptoSign::Factory::getAvailable()) {
206 switch (backend) {
207 case CryptoSign::Backend::Type::NSS3:
208 fprintf(stderr, format: "NSS");
209 break;
210 case CryptoSign::Backend::Type::GPGME:
211 fprintf(stderr, format: "GPG");
212 break;
213 }
214 if (backend == CryptoSign::Factory::getActive()) {
215 fprintf(stderr, format: " (active)\n");
216 } else {
217 fprintf(stderr, format: "\n");
218 }
219 }
220}
221
222static std::vector<std::unique_ptr<X509CertificateInfo>> getAvailableSigningCertificates(bool *error)
223{
224#ifdef ENABLE_NSS3
225 bool wrongPassword = false;
226 bool passwordNeeded = false;
227 auto passwordCallback = [&passwordNeeded, &wrongPassword](const char *) -> char * {
228 static bool firstTime = true;
229 if (!firstTime) {
230 wrongPassword = true;
231 return nullptr;
232 }
233 firstTime = false;
234 if (nssPassword.getLength() > 0) {
235 return strdup(s: nssPassword.c_str());
236 } else {
237 passwordNeeded = true;
238 return nullptr;
239 }
240 };
241 NSSSignatureConfiguration::setNSSPasswordCallback(passwordCallback);
242#endif
243 auto backend = CryptoSign::Factory::createActive();
244 if (!backend) {
245 *error = true;
246 printf(format: "No backends for cryptographic signatures available");
247 return {};
248 }
249 std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = backend->getAvailableSigningCertificates();
250#ifdef ENABLE_NSS3
251 NSSSignatureConfiguration::setNSSPasswordCallback({});
252 if (passwordNeeded) {
253 *error = true;
254 printf(format: "Password is needed to access the NSS database.\n");
255 printf(format: "\tPlease provide one with -nss-pwd.\n");
256 return {};
257 }
258 if (wrongPassword) {
259 *error = true;
260 printf(format: "Password was not accepted to open the NSS database.\n");
261 printf(format: "\tPlease provide the correct one with -nss-pwd.\n");
262 return {};
263 }
264
265#endif
266 *error = false;
267 return vCerts;
268}
269
270static std::string locationToString(KeyLocation location)
271{
272 switch (location) {
273 case KeyLocation::Unknown:
274 return {};
275 case KeyLocation::Other:
276 return "(Other)";
277 case KeyLocation::Computer:
278 return "(Computer)";
279 case KeyLocation::HardwareToken:
280 return "(Hardware Token)";
281 }
282 return {};
283}
284
285static std::string TextStringToUTF8(const std::string &str)
286{
287 const UnicodeMap *utf8Map = globalParams->getUtf8Map();
288
289 std::vector<Unicode> u = TextStringToUCS4(textStr: str);
290
291 std::string convertedStr;
292 for (auto &c : u) {
293 char buf[8];
294 const int n = utf8Map->mapUnicode(u: c, buf, bufSize: sizeof(buf));
295 convertedStr.append(s: buf, n: n);
296 }
297
298 return convertedStr;
299}
300
301int main(int argc, char *argv[])
302{
303 char *time_str = nullptr;
304 globalParams = std::make_unique<GlobalParams>();
305
306 Win32Console win32Console(&argc, &argv);
307
308 const bool ok = parseArgs(args: argDesc, argc: &argc, argv);
309
310 if (!ok) {
311 print_version_usage(usage: true);
312 return 99;
313 }
314
315 if (printVersion) {
316 print_version_usage(usage: false);
317 return 0;
318 }
319
320 if (printHelp) {
321 print_version_usage(usage: true);
322 return 0;
323 }
324
325 if (strlen(s: backendString) > 0) {
326 auto backend = CryptoSign::Factory::typeFromString(string: backendString);
327 if (backend) {
328 CryptoSign::Factory::setPreferredBackend(backend.value());
329 } else {
330 fprintf(stderr, format: "Unsupported backend\n");
331 return 98;
332 }
333 }
334
335 if (printCryptoSignBackends) {
336 print_backends();
337 return 0;
338 }
339
340#ifdef ENABLE_NSS3
341 NSSSignatureConfiguration::setNSSDir(nssDir);
342#endif
343
344 if (listNicknames) {
345 bool getCertsError;
346 const std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = getAvailableSigningCertificates(error: &getCertsError);
347 if (getCertsError) {
348 return 2;
349 } else {
350 if (vCerts.empty()) {
351 printf(format: "There are no certificates available.\n");
352 } else {
353 printf(format: "Certificate nicknames available:\n");
354 for (auto &cert : vCerts) {
355 const GooString &nick = cert->getNickName();
356 const auto location = locationToString(location: cert->getKeyLocation());
357 printf(format: "%s %s\n", nick.c_str(), location.c_str());
358 }
359 }
360 }
361 return 0;
362 }
363
364 if (argc < 2) {
365 // no filename was given
366 print_version_usage(usage: true);
367 return 99;
368 }
369
370 std::unique_ptr<GooString> fileName = std::make_unique<GooString>(args&: argv[1]);
371
372 std::optional<GooString> ownerPW, userPW;
373 if (ownerPassword[0] != '\001') {
374 ownerPW = GooString(ownerPassword);
375 }
376 if (userPassword[0] != '\001') {
377 userPW = GooString(userPassword);
378 }
379 // open PDF file
380 std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(uri: *fileName, ownerPassword: ownerPW, userPassword: userPW));
381
382 if (!doc->isOk()) {
383 return 1;
384 }
385
386 int signatureNumber;
387 if (strlen(s: signatureName) > 0) {
388 signatureNumber = atoi(nptr: signatureName);
389 if (signatureNumber == 0) {
390 signatureNumber = -1;
391 }
392 } else {
393 signatureNumber = 0;
394 }
395
396 if (addNewSignature && signatureNumber > 0) {
397 // incompatible options
398 print_version_usage(usage: true);
399 return 99;
400 }
401
402 if (addNewSignature) {
403 if (argc == 2) {
404 fprintf(stderr, format: "An output filename for the signed document must be given\n");
405 return 2;
406 }
407
408 if (strlen(s: certNickname) == 0) {
409 printf(format: "A nickname of the signing certificate must be given\n");
410 return 2;
411 }
412
413 if (etsiCAdESdetached) {
414 printf(format: "-etsi is not supported yet with -add-signature\n");
415 printf(format: "Please file a bug report if this is important for you\n");
416 return 2;
417 }
418
419 if (digestName != std::string("SHA256")) {
420 printf(format: "Only digest SHA256 is supported at the moment with -add-signature\n");
421 printf(format: "Please file a bug report if this is important for you\n");
422 return 2;
423 }
424
425 if (doc->getPage(page: 1) == nullptr) {
426 printf(format: "Error getting first page of the document.\n");
427 return 2;
428 }
429
430 bool getCertsError;
431 // We need to call this otherwise NSS spins forever
432 getAvailableSigningCertificates(error: &getCertsError);
433 if (getCertsError) {
434 return 2;
435 }
436
437 const auto rs = std::unique_ptr<GooString>(reason.toStr().empty() ? nullptr : std::make_unique<GooString>(args: utf8ToUtf16WithBom(utf8: reason.toStr())));
438
439 if (newSignatureFieldName.getLength() == 0) {
440 // Create a random field name, it could be anything but 32 hex numbers should
441 // hopefully give us something that is not already in the document
442 std::random_device rd;
443 std::mt19937 gen(rd());
444 std::uniform_int_distribution<> distrib(1, 15);
445 for (int i = 0; i < 32; ++i) {
446 const int value = distrib(gen);
447 newSignatureFieldName.append(c: value < 10 ? 48 + value : 65 + (value - 10));
448 }
449 }
450
451 // We don't provide a way to customize the UI from pdfsig for now
452 const bool success = doc->sign(saveFilename: std::string { argv[2] }, certNickname: std::string { certNickname }, password: std::string { password }, partialFieldName: newSignatureFieldName.copy(), /*page*/ 1,
453 /*rect */ { 0, 0, 0, 0 }, /*signatureText*/ {}, /*signatureTextLeft*/ {}, /*fontSize */ 0, /*leftFontSize*/ 0,
454 /*fontColor*/ {}, /*borderWidth*/ 0, /*borderColor*/ {}, /*backgroundColor*/ {}, reason: rs.get(), /* location */ nullptr, /* image path */ imagePath: "", ownerPassword: ownerPW, userPassword: userPW);
455 return success ? 0 : 3;
456 }
457
458 const std::vector<FormFieldSignature *> signatures = doc->getSignatureFields();
459 const unsigned int sigCount = signatures.size();
460
461 if (signatureNumber == -1) {
462 for (unsigned int i = 0; i < sigCount; i++) {
463 const GooString *goo = signatures.at(n: i)->getCreateWidget()->getField()->getFullyQualifiedName();
464 if (!goo) {
465 continue;
466 }
467
468 const std::string name = TextStringToUTF8(str: goo->toStr());
469 if (name == signatureName) {
470 signatureNumber = i + 1;
471 break;
472 }
473 }
474
475 if (signatureNumber == -1) {
476 fprintf(stderr, format: "Signature field not found by name\n");
477 return 2;
478 }
479 }
480
481 if (signatureNumber > 0) {
482 // We are signing an existing signature field
483 if (argc == 2) {
484 fprintf(stderr, format: "An output filename for the signed document must be given\n");
485 return 2;
486 }
487
488 if (signatureNumber > static_cast<int>(sigCount)) {
489 printf(format: "File '%s' does not contain a signature with number %d\n", fileName->c_str(), signatureNumber);
490 return 2;
491 }
492
493 if (strlen(s: certNickname) == 0) {
494 printf(format: "A nickname of the signing certificate must be given\n");
495 return 2;
496 }
497
498 if (digestName != std::string("SHA256")) {
499 printf(format: "Only digest SHA256 is supported at the moment\n");
500 printf(format: "Please file a bug report if this is important for you\n");
501 return 2;
502 }
503
504 bool getCertsError;
505 // We need to call this otherwise NSS spins forever
506 getAvailableSigningCertificates(error: &getCertsError);
507 if (getCertsError) {
508 return 2;
509 }
510
511 FormFieldSignature *ffs = signatures.at(n: signatureNumber - 1);
512 Goffset file_size = 0;
513 const std::optional<GooString> sig = ffs->getCheckedSignature(checkedFileSize: &file_size);
514 if (sig) {
515 printf(format: "Signature number %d is already signed\n", signatureNumber);
516 return 2;
517 }
518 if (etsiCAdESdetached) {
519 ffs->setSignatureType(ETSI_CAdES_detached);
520 }
521 const auto rs = std::unique_ptr<GooString>(reason.toStr().empty() ? nullptr : std::make_unique<GooString>(args: utf8ToUtf16WithBom(utf8: reason.toStr())));
522 if (ffs->getNumWidgets() != 1) {
523 printf(format: "Unexpected number of widgets for the signature: %d\n", ffs->getNumWidgets());
524 return 2;
525 }
526#ifdef HAVE_GETTEXT
527 if (!noAppearance) {
528 setlocale(LC_ALL, locale: "");
529 bindtextdomain(domainname: "pdfsig", CMAKE_INSTALL_LOCALEDIR);
530 textdomain(domainname: "pdfsig");
531 }
532#endif
533 FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(ffs->getWidget(i: 0));
534 auto backend = CryptoSign::Factory::createActive();
535 auto sigHandler = backend->createSigningHandler(certID: certNickname, digestAlgTag: HashAlgorithm::Sha256);
536 std::unique_ptr<X509CertificateInfo> certInfo = sigHandler->getCertificateInfo();
537 if (!certInfo) {
538 fprintf(stderr, format: "signDocument: error getting signature info\n");
539 return 2;
540 }
541 const std::string signerName = certInfo->getSubjectInfo().commonName;
542 const std::string timestamp = timeToStringWithFormat(timeA: nullptr, format: "%Y.%m.%d %H:%M:%S %z");
543 const AnnotColor blackColor(0, 0, 0);
544 const std::string signatureText(GooString::format(_("Digitally signed by {0:s}"), signerName.c_str())->toStr() + "\n" + GooString::format(_("Date: {0:s}"), timestamp.c_str())->toStr());
545 const auto gSignatureText = std::make_unique<GooString>(args: (signatureText.empty() || noAppearance) ? "" : utf8ToUtf16WithBom(utf8: signatureText));
546 const auto gSignatureLeftText = std::make_unique<GooString>(args: (signerName.empty() || noAppearance) ? "" : utf8ToUtf16WithBom(utf8: signerName));
547 const bool success = fws->signDocumentWithAppearance(filename: argv[2], certNickname: std::string { certNickname }, password: std::string { password }, reason: rs.get(), location: nullptr, ownerPassword: {}, userPassword: {}, signatureText: *gSignatureText, signatureTextLeft: *gSignatureLeftText, fontSize: 0, leftFontSize: 0, fontColor: std::make_unique<AnnotColor>(args: blackColor));
548 return success ? 0 : 3;
549 }
550
551 if (argc > 2) {
552 // We are not signing and more than 1 filename was given
553 print_version_usage(usage: true);
554 return 99;
555 }
556
557 if (sigCount >= 1) {
558 if (dumpSignatures) {
559 printf(format: "Dumping Signatures: %u\n", sigCount);
560 for (unsigned int i = 0; i < sigCount; i++) {
561 const bool dumpingOk = dumpSignature(sig_num: i, sigCount, s: signatures.at(n: i), filename: fileName->c_str());
562 if (!dumpingOk) {
563 // for now, do nothing. We have logged a message
564 // to the user before returning false in dumpSignature
565 // and it is possible to have "holes" in the signatures
566 continue;
567 }
568 }
569 return 0;
570 } else {
571 printf(format: "Digital Signature Info of: %s\n", fileName->c_str());
572 }
573 } else {
574 printf(format: "File '%s' does not contain any signatures\n", fileName->c_str());
575 return 2;
576 }
577 std::unordered_map<int, SignatureInfo *> signatureInfos;
578 for (unsigned int i = 0; i < sigCount; i++) {
579 // Let's start the signature check first for signatures.
580 // we can always wait for completion later
581 FormFieldSignature *ffs = signatures.at(n: i);
582 if (ffs->getSignatureType() == unsigned_signature_field) {
583 continue;
584 }
585 signatureInfos[i] = ffs->validateSignatureAsync(doVerifyCert: !dontVerifyCert, forceRevalidation: false, validationTime: -1 /* now */, ocspRevocationCheck: !noOCSPRevocationCheck, enableAIA: useAIACertFetch, doneCallback: {});
586 }
587
588 for (unsigned int i = 0; i < sigCount; i++) {
589 FormFieldSignature *ffs = signatures.at(n: i);
590 printf(format: "Signature #%u:\n", i + 1);
591 const GooString *goo = ffs->getCreateWidget()->getField()->getFullyQualifiedName();
592 if (goo) {
593 const std::string name = TextStringToUTF8(str: goo->toStr());
594 printf(format: " - Signature Field Name: %s\n", name.c_str());
595 }
596
597 if (ffs->getSignatureType() == unsigned_signature_field) {
598 printf(format: " The signature form field is not signed.\n");
599 continue;
600 }
601
602 const SignatureInfo *sig_info = signatureInfos[i];
603 CertificateValidationStatus certificateStatus = ffs->validateSignatureResult();
604 printf(format: " - Signer Certificate Common Name: %s\n", sig_info->getSignerName().c_str());
605 printf(format: " - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN().c_str());
606 printf(format: " - Signing Time: %s\n", time_str = getReadableTime(unix_time: sig_info->getSigningTime()));
607 printf(format: " - Signing Hash Algorithm: ");
608 switch (sig_info->getHashAlgorithm()) {
609 case HashAlgorithm::Md2:
610 printf(format: "MD2\n");
611 break;
612 case HashAlgorithm::Md5:
613 printf(format: "MD5\n");
614 break;
615 case HashAlgorithm::Sha1:
616 printf(format: "SHA1\n");
617 break;
618 case HashAlgorithm::Sha256:
619 printf(format: "SHA-256\n");
620 break;
621 case HashAlgorithm::Sha384:
622 printf(format: "SHA-384\n");
623 break;
624 case HashAlgorithm::Sha512:
625 printf(format: "SHA-512\n");
626 break;
627 case HashAlgorithm::Sha224:
628 printf(format: "SHA-224\n");
629 break;
630 default:
631 printf(format: "unknown\n");
632 }
633 printf(format: " - Signature Type: ");
634 switch (ffs->getSignatureType()) {
635 case adbe_pkcs7_sha1:
636 printf(format: "adbe.pkcs7.sha1\n");
637 break;
638 case adbe_pkcs7_detached:
639 printf(format: "adbe.pkcs7.detached\n");
640 break;
641 case ETSI_CAdES_detached:
642 printf(format: "ETSI.CAdES.detached\n");
643 break;
644 default:
645 printf(format: "unknown\n");
646 }
647 const std::vector<Goffset> ranges = ffs->getSignedRangeBounds();
648 if (ranges.size() == 4) {
649 printf(format: " - Signed Ranges: [%lld - %lld], [%lld - %lld]\n", ranges[0], ranges[1], ranges[2], ranges[3]);
650 Goffset checked_file_size;
651 const std::optional<GooString> signature = signatures.at(n: i)->getCheckedSignature(checkedFileSize: &checked_file_size);
652 if (signature && checked_file_size == ranges[3]) {
653 printf(format: " - Total document signed\n");
654 } else {
655 printf(format: " - Not total document signed\n");
656 }
657 }
658 printf(format: " - Signature Validation: %s\n", getReadableSigState(sig_vs: sig_info->getSignatureValStatus()));
659 gfree(p: time_str);
660 if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) {
661 continue;
662 }
663 printf(format: " - Certificate Validation: %s\n", getReadableCertState(cert_vs: certificateStatus));
664 }
665
666 return 0;
667}
668

source code of poppler/utils/pdfsig.cc