|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "secutil.h" |
|
6 #include "secmod.h" |
|
7 #include "cert.h" |
|
8 #include "secoid.h" |
|
9 #include "nss.h" |
|
10 |
|
11 /* NSPR 2.0 header files */ |
|
12 #include "prinit.h" |
|
13 #include "prprf.h" |
|
14 #include "prsystem.h" |
|
15 #include "prmem.h" |
|
16 /* Portable layer header files */ |
|
17 #include "plstr.h" |
|
18 #include "sechash.h" /* for HASH_GetHashObject() */ |
|
19 |
|
20 static PRBool debugInfo; |
|
21 static PRBool verbose; |
|
22 static PRBool doVerify; |
|
23 static PRBool displayAll; |
|
24 |
|
25 static const char * const usageInfo[] = { |
|
26 "signver - verify a detached PKCS7 signature - Version " NSS_VERSION, |
|
27 "Commands:", |
|
28 " -A display all information from pkcs #7", |
|
29 " -V verify the signed object and display result", |
|
30 "Options:", |
|
31 " -a signature file is ASCII", |
|
32 " -d certdir directory containing cert database", |
|
33 " -i dataFileName input file containing signed data (default stdin)", |
|
34 " -o outputFileName output file name, default stdout", |
|
35 " -s signatureFileName input file for signature (default stdin)", |
|
36 " -v display verbose reason for failure" |
|
37 }; |
|
38 static int nUsageInfo = sizeof(usageInfo)/sizeof(char *); |
|
39 |
|
40 extern int SV_PrintPKCS7ContentInfo(FILE *, SECItem *); |
|
41 |
|
42 static void Usage(char *progName, FILE *outFile) |
|
43 { |
|
44 int i; |
|
45 fprintf(outFile, "Usage: %s [ commands ] options\n", progName); |
|
46 for (i = 0; i < nUsageInfo; i++) |
|
47 fprintf(outFile, "%s\n", usageInfo[i]); |
|
48 exit(-1); |
|
49 } |
|
50 |
|
51 static HASH_HashType |
|
52 AlgorithmToHashType(SECAlgorithmID *digestAlgorithms) |
|
53 { |
|
54 SECOidTag tag = SECOID_GetAlgorithmTag(digestAlgorithms); |
|
55 HASH_HashType hash = HASH_GetHashTypeByOidTag(tag); |
|
56 return hash; |
|
57 } |
|
58 |
|
59 |
|
60 static SECStatus |
|
61 DigestContent (SECItem * digest, SECItem * content, HASH_HashType hashType) |
|
62 { |
|
63 unsigned int maxLen = digest->len; |
|
64 unsigned int len = HASH_ResultLen(hashType); |
|
65 SECStatus rv; |
|
66 |
|
67 if (len > maxLen) { |
|
68 PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
|
69 return SECFailure; |
|
70 } |
|
71 |
|
72 rv = HASH_HashBuf(hashType, digest->data, content->data, content->len); |
|
73 if (rv == SECSuccess) |
|
74 digest->len = len; |
|
75 return rv; |
|
76 } |
|
77 |
|
78 enum { |
|
79 cmd_DisplayAllPCKS7Info = 0, |
|
80 cmd_VerifySignedObj |
|
81 }; |
|
82 |
|
83 enum { |
|
84 opt_ASCII, |
|
85 opt_CertDir, |
|
86 opt_InputDataFile, |
|
87 opt_ItemNumber, |
|
88 opt_OutputFile, |
|
89 opt_InputSigFile, |
|
90 opt_PrintWhyFailure, |
|
91 opt_DebugInfo |
|
92 }; |
|
93 |
|
94 static secuCommandFlag signver_commands[] = |
|
95 { |
|
96 { /* cmd_DisplayAllPCKS7Info*/ 'A', PR_FALSE, 0, PR_FALSE }, |
|
97 { /* cmd_VerifySignedObj */ 'V', PR_FALSE, 0, PR_FALSE } |
|
98 }; |
|
99 |
|
100 static secuCommandFlag signver_options[] = |
|
101 { |
|
102 { /* opt_ASCII */ 'a', PR_FALSE, 0, PR_FALSE }, |
|
103 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, |
|
104 { /* opt_InputDataFile */ 'i', PR_TRUE, 0, PR_FALSE }, |
|
105 { /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE }, |
|
106 { /* opt_InputSigFile */ 's', PR_TRUE, 0, PR_FALSE }, |
|
107 { /* opt_PrintWhyFailure */ 'v', PR_FALSE, 0, PR_FALSE }, |
|
108 { /* opt_DebugInfo */ 0, PR_FALSE, 0, PR_FALSE, "debug" } |
|
109 }; |
|
110 |
|
111 int main(int argc, char **argv) |
|
112 { |
|
113 PRFileDesc *contentFile = NULL; |
|
114 PRFileDesc *signFile = PR_STDIN; |
|
115 FILE * outFile = stdout; |
|
116 char * progName; |
|
117 SECStatus rv; |
|
118 int result = 1; |
|
119 SECItem pkcs7der, content; |
|
120 secuCommand signver; |
|
121 |
|
122 pkcs7der.data = NULL; |
|
123 content.data = NULL; |
|
124 |
|
125 signver.numCommands = sizeof(signver_commands) /sizeof(secuCommandFlag); |
|
126 signver.numOptions = sizeof(signver_options) / sizeof(secuCommandFlag); |
|
127 signver.commands = signver_commands; |
|
128 signver.options = signver_options; |
|
129 |
|
130 #ifdef XP_PC |
|
131 progName = strrchr(argv[0], '\\'); |
|
132 #else |
|
133 progName = strrchr(argv[0], '/'); |
|
134 #endif |
|
135 progName = progName ? progName+1 : argv[0]; |
|
136 |
|
137 rv = SECU_ParseCommandLine(argc, argv, progName, &signver); |
|
138 if (SECSuccess != rv) { |
|
139 Usage(progName, outFile); |
|
140 } |
|
141 debugInfo = signver.options[opt_DebugInfo ].activated; |
|
142 verbose = signver.options[opt_PrintWhyFailure ].activated; |
|
143 doVerify = signver.commands[cmd_VerifySignedObj].activated; |
|
144 displayAll= signver.commands[cmd_DisplayAllPCKS7Info].activated; |
|
145 if (!doVerify && !displayAll) |
|
146 doVerify = PR_TRUE; |
|
147 |
|
148 /* Set the certdb directory (default is ~/.netscape) */ |
|
149 rv = NSS_Init(SECU_ConfigDirectory(signver.options[opt_CertDir].arg)); |
|
150 if (rv != SECSuccess) { |
|
151 SECU_PrintPRandOSError(progName); |
|
152 return result; |
|
153 } |
|
154 /* below here, goto cleanup */ |
|
155 SECU_RegisterDynamicOids(); |
|
156 |
|
157 /* Open the input content file. */ |
|
158 if (signver.options[opt_InputDataFile].activated && |
|
159 signver.options[opt_InputDataFile].arg) { |
|
160 if (PL_strcmp("-", signver.options[opt_InputDataFile].arg)) { |
|
161 contentFile = PR_Open(signver.options[opt_InputDataFile].arg, |
|
162 PR_RDONLY, 0); |
|
163 if (!contentFile) { |
|
164 PR_fprintf(PR_STDERR, |
|
165 "%s: unable to open \"%s\" for reading.\n", |
|
166 progName, signver.options[opt_InputDataFile].arg); |
|
167 goto cleanup; |
|
168 } |
|
169 } else |
|
170 contentFile = PR_STDIN; |
|
171 } |
|
172 |
|
173 /* Open the input signature file. */ |
|
174 if (signver.options[opt_InputSigFile].activated && |
|
175 signver.options[opt_InputSigFile].arg) { |
|
176 if (PL_strcmp("-", signver.options[opt_InputSigFile].arg)) { |
|
177 signFile = PR_Open(signver.options[opt_InputSigFile].arg, |
|
178 PR_RDONLY, 0); |
|
179 if (!signFile) { |
|
180 PR_fprintf(PR_STDERR, |
|
181 "%s: unable to open \"%s\" for reading.\n", |
|
182 progName, signver.options[opt_InputSigFile].arg); |
|
183 goto cleanup; |
|
184 } |
|
185 } |
|
186 } |
|
187 |
|
188 if (contentFile == PR_STDIN && signFile == PR_STDIN && doVerify) { |
|
189 PR_fprintf(PR_STDERR, |
|
190 "%s: cannot read both content and signature from standard input\n", |
|
191 progName); |
|
192 goto cleanup; |
|
193 } |
|
194 |
|
195 /* Open|Create the output file. */ |
|
196 if (signver.options[opt_OutputFile].activated) { |
|
197 outFile = fopen(signver.options[opt_OutputFile].arg, "w"); |
|
198 if (!outFile) { |
|
199 PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for writing.\n", |
|
200 progName, signver.options[opt_OutputFile].arg); |
|
201 goto cleanup; |
|
202 } |
|
203 } |
|
204 |
|
205 /* read in the input files' contents */ |
|
206 rv = SECU_ReadDERFromFile(&pkcs7der, signFile, |
|
207 signver.options[opt_ASCII].activated, PR_FALSE); |
|
208 if (signFile != PR_STDIN) |
|
209 PR_Close(signFile); |
|
210 if (rv != SECSuccess) { |
|
211 SECU_PrintError(progName, "problem reading PKCS7 input"); |
|
212 goto cleanup; |
|
213 } |
|
214 if (contentFile) { |
|
215 rv = SECU_FileToItem(&content, contentFile); |
|
216 if (contentFile != PR_STDIN) |
|
217 PR_Close(contentFile); |
|
218 if (rv != SECSuccess) |
|
219 content.data = NULL; |
|
220 } |
|
221 |
|
222 /* Signature Verification */ |
|
223 if (doVerify) { |
|
224 SEC_PKCS7ContentInfo *cinfo; |
|
225 SEC_PKCS7SignedData *signedData; |
|
226 HASH_HashType digestType; |
|
227 PRBool contentIsSigned; |
|
228 |
|
229 cinfo = SEC_PKCS7DecodeItem(&pkcs7der, NULL, NULL, NULL, NULL, |
|
230 NULL, NULL, NULL); |
|
231 if (cinfo == NULL) { |
|
232 PR_fprintf(PR_STDERR, "Unable to decode PKCS7 data\n"); |
|
233 goto cleanup; |
|
234 } |
|
235 /* below here, goto done */ |
|
236 |
|
237 contentIsSigned = SEC_PKCS7ContentIsSigned(cinfo); |
|
238 if (debugInfo) { |
|
239 PR_fprintf(PR_STDERR, "Content is%s encrypted.\n", |
|
240 SEC_PKCS7ContentIsEncrypted(cinfo) ? "" : " not"); |
|
241 } |
|
242 if (debugInfo || !contentIsSigned) { |
|
243 PR_fprintf(PR_STDERR, "Content is%s signed.\n", |
|
244 contentIsSigned ? "" : " not"); |
|
245 } |
|
246 |
|
247 if (!contentIsSigned) |
|
248 goto done; |
|
249 |
|
250 signedData = cinfo->content.signedData; |
|
251 |
|
252 /* assume that there is only one digest algorithm for now */ |
|
253 digestType = AlgorithmToHashType(signedData->digestAlgorithms[0]); |
|
254 if (digestType == HASH_AlgNULL) { |
|
255 PR_fprintf(PR_STDERR, "Invalid hash algorithmID\n"); |
|
256 goto done; |
|
257 } |
|
258 if (content.data) { |
|
259 SECCertUsage usage = certUsageEmailSigner; |
|
260 SECItem digest; |
|
261 unsigned char digestBuffer[HASH_LENGTH_MAX]; |
|
262 |
|
263 if (debugInfo) |
|
264 PR_fprintf(PR_STDERR, "contentToVerify=%s\n", content.data); |
|
265 |
|
266 digest.data = digestBuffer; |
|
267 digest.len = sizeof digestBuffer; |
|
268 |
|
269 if (DigestContent(&digest, &content, digestType)) { |
|
270 SECU_PrintError(progName, "Message digest computation failure"); |
|
271 goto done; |
|
272 } |
|
273 |
|
274 if (debugInfo) { |
|
275 unsigned int i; |
|
276 PR_fprintf(PR_STDERR, "Data Digest=:"); |
|
277 for (i = 0; i < digest.len; i++) |
|
278 PR_fprintf(PR_STDERR, "%02x:", digest.data[i]); |
|
279 PR_fprintf(PR_STDERR, "\n"); |
|
280 } |
|
281 |
|
282 fprintf(outFile, "signatureValid="); |
|
283 PORT_SetError(0); |
|
284 if (SEC_PKCS7VerifyDetachedSignature (cinfo, usage, |
|
285 &digest, digestType, PR_FALSE)) { |
|
286 fprintf(outFile, "yes"); |
|
287 } else { |
|
288 fprintf(outFile, "no"); |
|
289 if (verbose) { |
|
290 fprintf(outFile, ":%s", |
|
291 SECU_Strerror(PORT_GetError())); |
|
292 } |
|
293 } |
|
294 fprintf(outFile, "\n"); |
|
295 result = 0; |
|
296 } |
|
297 done: |
|
298 SEC_PKCS7DestroyContentInfo(cinfo); |
|
299 } |
|
300 |
|
301 if (displayAll) { |
|
302 if (SV_PrintPKCS7ContentInfo(outFile, &pkcs7der)) |
|
303 result = 1; |
|
304 } |
|
305 |
|
306 cleanup: |
|
307 SECITEM_FreeItem(&pkcs7der, PR_FALSE); |
|
308 SECITEM_FreeItem(&content, PR_FALSE); |
|
309 |
|
310 if (NSS_Shutdown() != SECSuccess) { |
|
311 result = 1; |
|
312 } |
|
313 |
|
314 return result; |
|
315 } |