|
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 /* |
|
6 * p7verify -- A command to do a verification of a *detached* pkcs7 signature. |
|
7 */ |
|
8 |
|
9 #include "nspr.h" |
|
10 #include "secutil.h" |
|
11 #include "plgetopt.h" |
|
12 #include "secpkcs7.h" |
|
13 #include "cert.h" |
|
14 #include "certdb.h" |
|
15 #include "secoid.h" |
|
16 #include "sechash.h" /* for HASH_GetHashObject() */ |
|
17 #include "nss.h" |
|
18 |
|
19 #if defined(XP_UNIX) |
|
20 #include <unistd.h> |
|
21 #endif |
|
22 |
|
23 #include <stdio.h> |
|
24 #include <string.h> |
|
25 |
|
26 #if (defined(XP_WIN) && !defined(WIN32)) || (defined(__sun) && !defined(SVR4)) |
|
27 extern int fread(char *, size_t, size_t, FILE*); |
|
28 extern int fprintf(FILE *, char *, ...); |
|
29 #endif |
|
30 |
|
31 |
|
32 static HASH_HashType |
|
33 AlgorithmToHashType(SECAlgorithmID *digestAlgorithms) |
|
34 { |
|
35 |
|
36 SECOidTag tag; |
|
37 |
|
38 tag = SECOID_GetAlgorithmTag(digestAlgorithms); |
|
39 |
|
40 switch (tag) { |
|
41 case SEC_OID_MD2: |
|
42 return HASH_AlgMD2; |
|
43 case SEC_OID_MD5: |
|
44 return HASH_AlgMD5; |
|
45 case SEC_OID_SHA1: |
|
46 return HASH_AlgSHA1; |
|
47 default: |
|
48 fprintf(stderr, "should never get here\n"); |
|
49 return HASH_AlgNULL; |
|
50 } |
|
51 } |
|
52 |
|
53 static int |
|
54 DigestFile(unsigned char *digest, unsigned int *len, unsigned int maxLen, |
|
55 FILE *inFile, HASH_HashType hashType) |
|
56 { |
|
57 int nb; |
|
58 unsigned char ibuf[4096]; |
|
59 const SECHashObject *hashObj; |
|
60 void *hashcx; |
|
61 |
|
62 hashObj = HASH_GetHashObject(hashType); |
|
63 |
|
64 hashcx = (* hashObj->create)(); |
|
65 if (hashcx == NULL) |
|
66 return -1; |
|
67 |
|
68 (* hashObj->begin)(hashcx); |
|
69 |
|
70 for (;;) { |
|
71 if (feof(inFile)) break; |
|
72 nb = fread(ibuf, 1, sizeof(ibuf), inFile); |
|
73 if (nb != sizeof(ibuf)) { |
|
74 if (nb == 0) { |
|
75 if (ferror(inFile)) { |
|
76 PORT_SetError(SEC_ERROR_IO); |
|
77 (* hashObj->destroy)(hashcx, PR_TRUE); |
|
78 return -1; |
|
79 } |
|
80 /* eof */ |
|
81 break; |
|
82 } |
|
83 } |
|
84 (* hashObj->update)(hashcx, ibuf, nb); |
|
85 } |
|
86 |
|
87 (* hashObj->end)(hashcx, digest, len, maxLen); |
|
88 (* hashObj->destroy)(hashcx, PR_TRUE); |
|
89 |
|
90 return 0; |
|
91 } |
|
92 |
|
93 |
|
94 static void |
|
95 Usage(char *progName) |
|
96 { |
|
97 fprintf(stderr, |
|
98 "Usage: %s -c content -s signature [-d dbdir] [-u certusage]\n", |
|
99 progName); |
|
100 fprintf(stderr, "%-20s content file that was signed\n", |
|
101 "-c content"); |
|
102 fprintf(stderr, "%-20s file containing signature for that content\n", |
|
103 "-s signature"); |
|
104 fprintf(stderr, |
|
105 "%-20s Key/Cert database directory (default is ~/.netscape)\n", |
|
106 "-d dbdir"); |
|
107 fprintf(stderr, "%-20s Define the type of certificate usage (default is certUsageEmailSigner)\n", |
|
108 "-u certusage"); |
|
109 fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " "); |
|
110 fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " "); |
|
111 fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " "); |
|
112 fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " "); |
|
113 fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " "); |
|
114 fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " "); |
|
115 fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " "); |
|
116 fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " "); |
|
117 fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " "); |
|
118 fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " "); |
|
119 fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " "); |
|
120 fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " "); |
|
121 |
|
122 exit(-1); |
|
123 } |
|
124 |
|
125 static int |
|
126 HashDecodeAndVerify(FILE *out, FILE *content, PRFileDesc *signature, |
|
127 SECCertUsage usage, char *progName) |
|
128 { |
|
129 SECItem derdata; |
|
130 SEC_PKCS7ContentInfo *cinfo; |
|
131 SEC_PKCS7SignedData *signedData; |
|
132 HASH_HashType digestType; |
|
133 SECItem digest; |
|
134 unsigned char buffer[32]; |
|
135 |
|
136 if (SECU_ReadDERFromFile(&derdata, signature, PR_FALSE, |
|
137 PR_FALSE) != SECSuccess) { |
|
138 SECU_PrintError(progName, "error reading signature file"); |
|
139 return -1; |
|
140 } |
|
141 |
|
142 cinfo = SEC_PKCS7DecodeItem(&derdata, NULL, NULL, NULL, NULL, |
|
143 NULL, NULL, NULL); |
|
144 if (cinfo == NULL) |
|
145 return -1; |
|
146 |
|
147 if (! SEC_PKCS7ContentIsSigned(cinfo)) { |
|
148 fprintf (out, "Signature file is pkcs7 data, but not signed.\n"); |
|
149 return -1; |
|
150 } |
|
151 |
|
152 signedData = cinfo->content.signedData; |
|
153 |
|
154 /* assume that there is only one digest algorithm for now */ |
|
155 digestType = AlgorithmToHashType(signedData->digestAlgorithms[0]); |
|
156 if (digestType == HASH_AlgNULL) { |
|
157 fprintf (out, "Invalid hash algorithmID\n"); |
|
158 return -1; |
|
159 } |
|
160 |
|
161 digest.data = buffer; |
|
162 if (DigestFile (digest.data, &digest.len, 32, content, digestType)) { |
|
163 SECU_PrintError (progName, "problem computing message digest"); |
|
164 return -1; |
|
165 } |
|
166 |
|
167 fprintf(out, "Signature is "); |
|
168 if (SEC_PKCS7VerifyDetachedSignature (cinfo, usage, &digest, digestType, |
|
169 PR_FALSE)) |
|
170 fprintf(out, "valid.\n"); |
|
171 else |
|
172 fprintf(out, "invalid (Reason: %s).\n", |
|
173 SECU_Strerror(PORT_GetError())); |
|
174 |
|
175 SEC_PKCS7DestroyContentInfo(cinfo); |
|
176 return 0; |
|
177 } |
|
178 |
|
179 |
|
180 int |
|
181 main(int argc, char **argv) |
|
182 { |
|
183 char *progName; |
|
184 FILE *contentFile, *outFile; |
|
185 PRFileDesc *signatureFile; |
|
186 SECCertUsage certUsage = certUsageEmailSigner; |
|
187 PLOptState *optstate; |
|
188 PLOptStatus status; |
|
189 SECStatus rv; |
|
190 |
|
191 progName = strrchr(argv[0], '/'); |
|
192 progName = progName ? progName+1 : argv[0]; |
|
193 |
|
194 contentFile = NULL; |
|
195 signatureFile = NULL; |
|
196 outFile = NULL; |
|
197 |
|
198 /* |
|
199 * Parse command line arguments |
|
200 */ |
|
201 optstate = PL_CreateOptState(argc, argv, "c:d:o:s:u:"); |
|
202 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { |
|
203 switch (optstate->option) { |
|
204 case '?': |
|
205 Usage(progName); |
|
206 break; |
|
207 |
|
208 case 'c': |
|
209 contentFile = fopen(optstate->value, "r"); |
|
210 if (!contentFile) { |
|
211 fprintf(stderr, "%s: unable to open \"%s\" for reading\n", |
|
212 progName, optstate->value); |
|
213 return -1; |
|
214 } |
|
215 break; |
|
216 |
|
217 case 'd': |
|
218 SECU_ConfigDirectory(optstate->value); |
|
219 break; |
|
220 |
|
221 case 'o': |
|
222 outFile = fopen(optstate->value, "w"); |
|
223 if (!outFile) { |
|
224 fprintf(stderr, "%s: unable to open \"%s\" for writing\n", |
|
225 progName, optstate->value); |
|
226 return -1; |
|
227 } |
|
228 break; |
|
229 |
|
230 case 's': |
|
231 signatureFile = PR_Open(optstate->value, PR_RDONLY, 0); |
|
232 if (!signatureFile) { |
|
233 fprintf(stderr, "%s: unable to open \"%s\" for reading\n", |
|
234 progName, optstate->value); |
|
235 return -1; |
|
236 } |
|
237 break; |
|
238 |
|
239 case 'u': { |
|
240 int usageType; |
|
241 |
|
242 usageType = atoi (strdup(optstate->value)); |
|
243 if (usageType < certUsageSSLClient || usageType > certUsageAnyCA) |
|
244 return -1; |
|
245 certUsage = (SECCertUsage)usageType; |
|
246 break; |
|
247 } |
|
248 |
|
249 } |
|
250 } |
|
251 |
|
252 if (!contentFile) Usage (progName); |
|
253 if (!signatureFile) Usage (progName); |
|
254 if (!outFile) outFile = stdout; |
|
255 |
|
256 /* Call the NSS initialization routines */ |
|
257 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); |
|
258 rv = NSS_Init(SECU_ConfigDirectory(NULL)); |
|
259 if (rv != SECSuccess) { |
|
260 SECU_PrintPRandOSError(progName); |
|
261 return -1; |
|
262 } |
|
263 |
|
264 if (HashDecodeAndVerify(outFile, contentFile, signatureFile, |
|
265 certUsage, progName)) { |
|
266 SECU_PrintError(progName, "problem decoding/verifying signature"); |
|
267 return -1; |
|
268 } |
|
269 |
|
270 if (NSS_Shutdown() != SECSuccess) { |
|
271 exit(1); |
|
272 } |
|
273 |
|
274 return 0; |
|
275 } |