1/*
2 * PDF signature tool: verify and sign digital signatures in PDF files.
3 */
4
5#include "mupdf/fitz.h"
6#include "mupdf/pdf.h"
7#include "mupdf/helpers/pkcs7-check.h"
8#include "mupdf/helpers/pkcs7-openssl.h"
9
10#include <string.h>
11#include <stdlib.h>
12#include <stdio.h>
13
14static char *infile = NULL;
15static char *outfile = NULL;
16static char *certificatefile = NULL;
17static char *certificatepassword = "";
18static int verify = 0;
19static int clear = 0;
20static int sign = 0;
21static int list = 1;
22
23static void usage(void)
24{
25 fprintf(stderr,
26 "usage: mutool sign [options] input.pdf [signature object numbers]\n"
27 "\t-p -\tpassword\n"
28 "\t-v \tverify signature\n"
29 "\t-c \tclear signatures\n"
30 "\t-s -\tsign signatures using certificate file\n"
31 "\t-P -\tcertificate password\n"
32 "\t-o -\toutput file name\n"
33 );
34 exit(1);
35}
36
37static void verify_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
38{
39 char name[500];
40 enum pdf_signature_error err;
41 int edits;
42
43 printf("verifying signature %d\n", pdf_to_num(ctx, signature));
44
45 pdf_signature_designated_name(ctx, doc, signature, name, sizeof name);
46 printf(" Designated name: %s\n", name);
47
48 err = pdf_check_certificate(ctx, doc, signature);
49 if (err)
50 printf(" Certificate error: %s\n", pdf_signature_error_description(err));
51 else
52 printf(" Certificate is trusted.\n");
53
54 fz_try(ctx)
55 {
56 err = pdf_check_digest(ctx, doc, signature);
57 edits = pdf_signature_incremental_change_since_signing(ctx, doc, signature);
58 if (err)
59 printf(" Digest error: %s\n", pdf_signature_error_description(err));
60 else if (edits)
61 printf(" The signature is valid but there have been edits since signing.\n");
62 else
63 printf(" The document is unchanged since signing.\n");
64 }
65 fz_catch(ctx)
66 printf(" Digest error: %s\n", fz_caught_message(ctx));
67}
68
69static void clear_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
70{
71 pdf_page *page = NULL;
72 pdf_widget *widget;
73 pdf_obj *parent;
74 int pageno;
75
76 fz_var(page);
77
78 printf("clearing signature %d\n", pdf_to_num(ctx, signature));
79
80 fz_try(ctx)
81 {
82 parent = pdf_dict_get(ctx, signature, PDF_NAME(P));
83 pageno = pdf_lookup_page_number(ctx, doc, parent);
84 page = pdf_load_page(ctx, doc, pageno);
85 for (widget = pdf_first_widget(ctx, page); widget; widget = pdf_next_widget(ctx, widget))
86 if (pdf_widget_type(ctx, widget) == PDF_WIDGET_TYPE_SIGNATURE && !pdf_objcmp_resolve(ctx, widget->obj, signature))
87 pdf_clear_signature(ctx, doc, widget);
88 }
89 fz_always(ctx)
90 fz_drop_page(ctx, (fz_page*)page);
91 fz_catch(ctx)
92 fz_rethrow(ctx);
93}
94
95static void sign_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
96{
97 pdf_pkcs7_signer *signer = NULL;
98 pdf_page *page = NULL;
99 pdf_widget *widget;
100 pdf_obj *parent;
101 int pageno;
102
103 fz_var(page);
104 fz_var(signer);
105
106 printf("signing signature %d\n", pdf_to_num(ctx, signature));
107
108 fz_try(ctx)
109 {
110 signer = pkcs7_openssl_read_pfx(ctx, certificatefile, certificatepassword);
111
112 parent = pdf_dict_get(ctx, signature, PDF_NAME(P));
113 pageno = pdf_lookup_page_number(ctx, doc, parent);
114 page = pdf_load_page(ctx, doc, pageno);
115 for (widget = pdf_first_widget(ctx, page); widget; widget = pdf_next_widget(ctx, widget))
116 if (pdf_widget_type(ctx, widget) == PDF_WIDGET_TYPE_SIGNATURE && !pdf_objcmp_resolve(ctx, widget->obj, signature))
117 pdf_sign_signature(ctx, doc, widget, signer);
118 }
119 fz_always(ctx)
120 {
121 fz_drop_page(ctx, (fz_page*)page);
122 if (signer)
123 signer->drop(signer);
124 }
125 fz_catch(ctx)
126 fz_rethrow(ctx);
127
128}
129
130static void list_signature(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
131{
132 char name[500];
133 pdf_signature_designated_name(ctx, doc, signature, name, sizeof name);
134 printf("%5d: signature name: %s\n", pdf_to_num(ctx, signature), name);
135}
136
137static void process_field(fz_context *ctx, pdf_document *doc, pdf_obj *field)
138{
139 if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) != PDF_NAME(Sig))
140 fz_warn(ctx, "%d is not a signature, skipping", pdf_to_num(ctx, field));
141 else
142 {
143 if (list)
144 list_signature(ctx, doc, field);
145 if (verify)
146 verify_signature(ctx, doc, field);
147 if (clear)
148 clear_signature(ctx, doc, field);
149 if (sign)
150 sign_signature(ctx, doc, field);
151 }
152}
153
154static void process_field_hierarchy(fz_context *ctx, pdf_document *doc, pdf_obj *field)
155{
156 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
157 if (kids)
158 {
159 int i, n;
160 n = pdf_array_len(ctx, kids);
161 for (i = 0; i < n; ++i)
162 {
163 pdf_obj *kid = pdf_array_get(ctx, kids, i);
164 if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) == PDF_NAME(Sig))
165 process_field_hierarchy(ctx, doc, kid);
166 }
167 }
168 else if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) == PDF_NAME(Sig))
169 process_field(ctx, doc, field);
170}
171
172static void process_acro_form(fz_context *ctx, pdf_document *doc)
173{
174 pdf_obj *trailer = pdf_trailer(ctx, doc);
175 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
176 pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm));
177 pdf_obj *fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields));
178 int i, n = pdf_array_len(ctx, fields);
179 for (i = 0; i < n; ++i)
180 process_field_hierarchy(ctx, doc, pdf_array_get(ctx, fields, i));
181}
182
183int pdfsign_main(int argc, char **argv)
184{
185 fz_context *ctx;
186 pdf_document *doc;
187 char *password = "";
188 int c;
189 pdf_page *page = NULL;
190
191 while ((c = fz_getopt(argc, argv, "co:p:s:vP:")) != -1)
192 {
193 switch (c)
194 {
195 case 'c': list = 0; clear = 1; break;
196 case 'o': outfile = fz_optarg; break;
197 case 'p': password = fz_optarg; break;
198 case 'P': certificatepassword = fz_optarg; break;
199 case 's': list = 0; sign = 1; certificatefile = fz_optarg; break;
200 case 'v': list = 0; verify = 1; break;
201 default: usage(); break;
202 }
203 }
204
205 if (argc - fz_optind < 1)
206 usage();
207
208 infile = argv[fz_optind++];
209
210 if (!clear && !sign && !verify && argc - fz_optind > 0)
211 {
212 list = 0;
213 verify = 1;
214 }
215
216 ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
217 if (!ctx)
218 {
219 fprintf(stderr, "cannot initialize context\n");
220 exit(1);
221 }
222
223 fz_var(page);
224
225 doc = pdf_open_document(ctx, infile);
226 fz_try(ctx)
227 {
228 if (pdf_needs_password(ctx, doc))
229 if (!pdf_authenticate_password(ctx, doc, password))
230 fz_warn(ctx, "cannot authenticate password: %s", infile);
231
232 if (argc - fz_optind <= 0 || list)
233 process_acro_form(ctx, doc);
234 else
235 {
236 while (argc - fz_optind)
237 {
238 pdf_obj *field = pdf_new_indirect(ctx, doc, fz_atoi(argv[fz_optind]), 0);
239 process_field(ctx, doc, field);
240 pdf_drop_obj(ctx, field);
241 fz_optind++;
242 }
243 }
244
245 if (clear || sign)
246 {
247 if (!outfile)
248 outfile = "out.pdf";
249 pdf_save_document(ctx, doc, outfile, NULL);
250 }
251 }
252 fz_always(ctx)
253 pdf_drop_document(ctx, doc);
254 fz_catch(ctx)
255 {
256 fz_drop_page(ctx, (fz_page*)page);
257 fprintf(stderr, "error processing signatures: %s\n", fz_caught_message(ctx));
258 }
259
260 fz_flush_warnings(ctx);
261 fz_drop_context(ctx);
262 return 0;
263}
264