comparison mupdf-source/source/tools/pdfsign.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 /*
24 * PDF signature tool: verify and sign digital signatures in PDF files.
25 */
26
27 #include "mupdf/fitz.h"
28 #include "mupdf/pdf.h"
29 #include "mupdf/helpers/pkcs7-openssl.h"
30
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34
35 static char *infile = NULL;
36 static char *outfile = NULL;
37 static char *certificatefile = NULL;
38 static char *certificatepassword = "";
39 static int verify = 0;
40 static int clear = 0;
41 static int sign = 0;
42 static int list = 1;
43
44 static int usage(void)
45 {
46 fprintf(stderr,
47 "usage: mutool sign [options] input.pdf [signature object numbers]\n"
48 "\t-p -\tpassword\n"
49 "\t-v \tverify signature\n"
50 "\t-c \tclear signatures\n"
51 "\t-s -\tsign signatures using certificate file\n"
52 "\t-P -\tcertificate password\n"
53 "\t-o -\toutput file name\n"
54 );
55 return 1;
56 }
57
58 static void verify_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
59 {
60 char *name;
61 pdf_signature_error err;
62 pdf_pkcs7_verifier *verifier;
63 int edits;
64 pdf_pkcs7_distinguished_name *dn = NULL;
65
66 printf("Verifying signature %d:\n", pdf_to_num(ctx, signature));
67
68 if (!pdf_signature_is_signed(ctx, doc, signature))
69 {
70 printf("\tSignature is not signed.\n");
71 return;
72 }
73
74 verifier = pkcs7_openssl_new_verifier(ctx);
75 fz_var(dn);
76 fz_try(ctx)
77 {
78 dn = pdf_signature_get_signatory(ctx, verifier, doc, signature);
79 if (dn)
80 {
81 name = pdf_signature_format_distinguished_name(ctx, dn);
82 printf("\tDistinguished name: %s\n", name);
83 fz_free(ctx, name);
84 }
85 else
86 {
87 printf("\tSignature information missing.\n");
88 }
89
90 err = pdf_check_certificate(ctx, verifier, doc, signature);
91 if (err)
92 printf("\tCertificate error: %s\n", pdf_signature_error_description(err));
93 else
94 printf("\tCertificate is trusted.\n");
95
96 err = pdf_check_digest(ctx, verifier, doc, signature);
97 edits = pdf_signature_incremental_change_since_signing(ctx, doc, signature);
98 if (err)
99 printf("\tDigest error: %s\n", pdf_signature_error_description(err));
100 else if (edits)
101 printf("\tThe signature is valid but there have been edits since signing.\n");
102 else
103 printf("\tThe document is unchanged since signing.\n");
104 }
105 fz_always(ctx)
106 {
107 pdf_signature_drop_distinguished_name(ctx, dn);
108 pdf_drop_verifier(ctx, verifier);
109 }
110 fz_catch(ctx)
111 {
112 fz_rethrow(ctx);
113 }
114 }
115
116 static void clear_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
117 {
118 pdf_page *page = NULL;
119 pdf_annot *widget;
120 pdf_obj *parent;
121 int pageno, pagenoend;
122
123 fz_var(page);
124
125 printf("Clearing signature %d.\n", pdf_to_num(ctx, signature));
126
127 fz_try(ctx)
128 {
129 parent = pdf_dict_get(ctx, signature, PDF_NAME(P));
130 if (pdf_is_dict(ctx, parent))
131 {
132 pageno = pdf_lookup_page_number(ctx, doc, parent);
133 pagenoend = pageno+1;
134 }
135 else
136 {
137 pageno = 0;
138 pagenoend = pdf_count_pages(ctx, doc);
139 }
140 for (; pageno < pagenoend; pageno++)
141 {
142 page = pdf_load_page(ctx, doc, pageno);
143 for (widget = pdf_first_widget(ctx, page); widget; widget = pdf_next_widget(ctx, widget))
144 if (pdf_widget_type(ctx, widget) == PDF_WIDGET_TYPE_SIGNATURE && !pdf_objcmp_resolve(ctx, pdf_annot_obj(ctx, widget), signature))
145 pdf_clear_signature(ctx, widget);
146 fz_drop_page(ctx, (fz_page *) page);
147 page = NULL;
148 }
149 }
150 fz_always(ctx)
151 fz_drop_page(ctx, (fz_page*)page);
152 fz_catch(ctx)
153 fz_rethrow(ctx);
154 }
155
156 static void sign_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
157 {
158 pdf_pkcs7_signer *signer = NULL;
159 pdf_page *page = NULL;
160 pdf_annot *widget;
161 pdf_obj *parent;
162 int pageno, pagenoend;
163
164 fz_var(page);
165 fz_var(signer);
166
167 printf("Signing signature %d.\n", pdf_to_num(ctx, signature));
168
169 fz_try(ctx)
170 {
171 signer = pkcs7_openssl_read_pfx(ctx, certificatefile, certificatepassword);
172
173 parent = pdf_dict_get(ctx, signature, PDF_NAME(P));
174 if (pdf_is_dict(ctx, parent))
175 {
176 pageno = pdf_lookup_page_number(ctx, doc, parent);
177 pagenoend = pageno+1;
178 }
179 else
180 {
181 pageno = 0;
182 pagenoend = pdf_count_pages(ctx, doc);
183 }
184 for (; pageno < pagenoend; pageno++)
185 {
186 page = pdf_load_page(ctx, doc, pageno);
187 for (widget = pdf_first_widget(ctx, page); widget; widget = pdf_next_widget(ctx, widget))
188 if (pdf_widget_type(ctx, widget) == PDF_WIDGET_TYPE_SIGNATURE && !pdf_objcmp_resolve(ctx, pdf_annot_obj(ctx, widget), signature))
189 pdf_sign_signature(ctx, widget, signer,
190 PDF_SIGNATURE_DEFAULT_APPEARANCE,
191 NULL,
192 NULL,
193 NULL);
194 fz_drop_page(ctx, (fz_page *) page);
195 page = NULL;
196 }
197 }
198 fz_always(ctx)
199 {
200 fz_drop_page(ctx, (fz_page*)page);
201 pdf_drop_signer(ctx, signer);
202 }
203 fz_catch(ctx)
204 fz_rethrow(ctx);
205
206 }
207
208 static void list_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
209 {
210 pdf_pkcs7_distinguished_name *dn;
211 pdf_pkcs7_verifier *verifier;
212
213 if (!pdf_signature_is_signed(ctx, doc, signature))
214 {
215 printf("%5d: Signature is not signed.\n", pdf_to_num(ctx, signature));
216 return;
217 }
218
219 verifier = pkcs7_openssl_new_verifier(ctx);
220
221 dn = pdf_signature_get_signatory(ctx, verifier, doc, signature);
222 if (dn)
223 {
224 char *s = pdf_signature_format_distinguished_name(ctx, dn);
225 printf("%5d: Distinguished name: %s\n", pdf_to_num(ctx, signature), s);
226 fz_free(ctx, s);
227 pdf_signature_drop_distinguished_name(ctx, dn);
228 }
229 else
230 {
231 printf("%5d: Signature information missing.\n", pdf_to_num(ctx, signature));
232 }
233
234 pdf_drop_verifier(ctx, verifier);
235
236 }
237
238 static void process_field(fz_context *ctx, pdf_document *doc, pdf_obj *field)
239 {
240 if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) != PDF_NAME(Sig))
241 fz_warn(ctx, "%d is not a signature, skipping", pdf_to_num(ctx, field));
242 else
243 {
244 if (list)
245 list_signature(ctx, doc, field);
246 if (verify)
247 verify_signature(ctx, doc, field);
248 if (clear)
249 clear_signature(ctx, doc, field);
250 if (sign)
251 sign_signature(ctx, doc, field);
252 }
253 }
254
255 static int process_field_hierarchy(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_cycle_list *cycle_up)
256 {
257 pdf_cycle_list cycle;
258 pdf_obj *kids;
259 int x = 0;
260
261 if (field == NULL || pdf_cycle(ctx, &cycle, cycle_up, field))
262 fz_throw(ctx, FZ_ERROR_SYNTAX, "recursive field hierarchy");
263
264 kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
265 if (kids)
266 {
267 int i, n;
268 n = pdf_array_len(ctx, kids);
269 for (i = 0; i < n; ++i)
270 {
271 pdf_obj *kid = pdf_array_get(ctx, kids, i);
272 x += process_field_hierarchy(ctx, doc, kid, &cycle);
273 }
274 }
275 else if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) == PDF_NAME(Sig))
276 {
277 process_field(ctx, doc, field);
278 ++x;
279 }
280
281 return x;
282 }
283
284 static int process_acro_form(fz_context *ctx, pdf_document *doc)
285 {
286 pdf_obj *trailer = pdf_trailer(ctx, doc);
287 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
288 pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm));
289 pdf_obj *fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields));
290 int i, x, n = pdf_array_len(ctx, fields);
291 for (i = x = 0; i < n; ++i)
292 x += process_field_hierarchy(ctx, doc, pdf_array_get(ctx, fields, i), NULL);
293 return x;
294 }
295
296 int pdfsign_main(int argc, char **argv)
297 {
298 fz_context *ctx;
299 pdf_document *doc = NULL;
300 char *password = "";
301 int c;
302
303 while ((c = fz_getopt(argc, argv, "co:p:s:vP:")) != -1)
304 {
305 switch (c)
306 {
307 case 'c': list = 0; clear = 1; break;
308 case 'o': outfile = fz_optpath(fz_optarg); break;
309 case 'p': password = fz_optarg; break;
310 case 'P': certificatepassword = fz_optarg; break;
311 case 's': list = 0; sign = 1; certificatefile = fz_optarg; break;
312 case 'v': list = 0; verify = 1; break;
313 default: return usage();
314 }
315 }
316
317 if (argc - fz_optind < 1)
318 return usage();
319
320 infile = argv[fz_optind++];
321
322 if (!clear && !sign && !verify && argc - fz_optind > 0)
323 {
324 list = 0;
325 verify = 1;
326 }
327
328 ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
329 if (!ctx)
330 {
331 fprintf(stderr, "cannot initialize context\n");
332 exit(1);
333 }
334
335 fz_var(doc);
336
337 fz_try(ctx)
338 {
339 doc = pdf_open_document(ctx, infile);
340 if (pdf_needs_password(ctx, doc))
341 if (!pdf_authenticate_password(ctx, doc, password))
342 fz_warn(ctx, "cannot authenticate password: %s", infile);
343
344 if (argc - fz_optind <= 0 || list)
345 {
346 if (!process_acro_form(ctx, doc))
347 {
348 fprintf(stderr, "No signatures found!\n");
349 exit(1);
350 }
351 }
352 else
353 {
354 while (argc - fz_optind)
355 {
356 pdf_obj *field = pdf_new_indirect(ctx, doc, fz_atoi(argv[fz_optind]), 0);
357 process_field(ctx, doc, field);
358 pdf_drop_obj(ctx, field);
359 fz_optind++;
360 }
361 }
362
363 if (clear || sign)
364 {
365 pdf_write_options opts = pdf_default_write_options;
366 opts.do_incremental = 1;
367 if (!outfile)
368 outfile = "out.pdf";
369 pdf_save_document(ctx, doc, outfile, &opts);
370 }
371 }
372 fz_always(ctx)
373 pdf_drop_document(ctx, doc);
374 fz_catch(ctx)
375 {
376 fz_report_error(ctx);
377 fz_log_error(ctx, "error processing signatures");
378 }
379
380 fz_flush_warnings(ctx);
381 fz_drop_context(ctx);
382 return 0;
383 }