OLD | NEW |
1 /* This Source Code Form is subject to the terms of the Mozilla Public | 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 | 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/. */ | 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | 4 |
5 /* | 5 /* |
6 * p7sign -- A command to create a *detached* pkcs7 signature (over a given | 6 * p7sign -- A command to create a *detached* pkcs7 signature (over a given |
7 * input file). | 7 * input file). |
8 */ | 8 */ |
9 | 9 |
10 #include "nspr.h" | 10 #include "nspr.h" |
11 #include "plgetopt.h" | 11 #include "plgetopt.h" |
12 #include "secutil.h" | 12 #include "secutil.h" |
13 #include "secpkcs7.h" | 13 #include "secpkcs7.h" |
14 #include "cert.h" | 14 #include "cert.h" |
15 #include "certdb.h" | 15 #include "certdb.h" |
16 #include "sechash.h"» /* for HASH_GetHashObject() */ | 16 #include "sechash.h" /* for HASH_GetHashObject() */ |
17 #include "nss.h" | 17 #include "nss.h" |
18 #include "pk11func.h" | 18 #include "pk11func.h" |
19 | 19 |
20 #if defined(XP_UNIX) | 20 #if defined(XP_UNIX) |
21 #include <unistd.h> | 21 #include <unistd.h> |
22 #endif | 22 #endif |
23 | 23 |
24 #include <stdio.h> | 24 #include <stdio.h> |
25 #include <string.h> | 25 #include <string.h> |
26 | 26 |
27 #if (defined(XP_WIN) && !defined(WIN32)) || (defined(__sun) && !defined(SVR4)) | 27 #if (defined(XP_WIN) && !defined(WIN32)) || (defined(__sun) && !defined(SVR4)) |
28 extern int fread(char *, size_t, size_t, FILE*); | 28 extern int fread(char *, size_t, size_t, FILE *); |
29 extern int fwrite(char *, size_t, size_t, FILE*); | 29 extern int fwrite(char *, size_t, size_t, FILE *); |
30 extern int fprintf(FILE *, char *, ...); | 30 extern int fprintf(FILE *, char *, ...); |
31 #endif | 31 #endif |
32 | 32 |
33 static secuPWData pwdata = { PW_NONE, 0 }; | 33 static secuPWData pwdata = {PW_NONE, 0}; |
34 | 34 |
35 static void | 35 static void Usage(char *progName) { |
36 Usage(char *progName) | 36 fprintf(stderr, "Usage: %s -k keyname [-d keydir] [-i input] [-o output]\n", |
37 { | 37 progName); |
38 fprintf(stderr, | 38 fprintf(stderr, "%-20s Nickname of key to use for signature\n", "-k keyname"); |
39 » "Usage: %s -k keyname [-d keydir] [-i input] [-o output]\n", | 39 fprintf(stderr, "%-20s Key database directory (default is ~/.netscape)\n", |
40 » progName); | 40 "-d keydir"); |
41 fprintf(stderr, "%-20s Nickname of key to use for signature\n", | 41 fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", |
42 » "-k keyname"); | 42 "-i input"); |
43 fprintf(stderr, "%-20s Key database directory (default is ~/.netscape)\n", | 43 fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", |
44 » "-d keydir"); | 44 "-o output"); |
45 fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", | 45 fprintf(stderr, "%-20s Encapsulate content in signature message\n", "-e"); |
46 » "-i input"); | 46 fprintf(stderr, "%-20s Password to the key databse\n", "-p"); |
47 fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", | 47 fprintf(stderr, "%-20s password file\n", "-f"); |
48 » "-o output"); | 48 exit(-1); |
49 fprintf(stderr, "%-20s Encapsulate content in signature message\n", | 49 } |
50 » "-e"); | 50 |
51 fprintf(stderr, "%-20s Password to the key databse\n", "-p"); | 51 static void SignOut(void *arg, const char *buf, unsigned long len) { |
52 fprintf(stderr, "%-20s password file\n", "-f"); | 52 FILE *out; |
53 exit(-1); | 53 |
54 } | 54 out = (FILE *)arg; |
55 | 55 fwrite(buf, len, 1, out); |
56 static void | 56 } |
57 SignOut(void *arg, const char *buf, unsigned long len) | 57 |
58 { | 58 static int CreateDigest(SECItem *data, char *digestdata, unsigned int *len, |
59 FILE *out; | 59 unsigned int maxlen) { |
60 | 60 const SECHashObject *hashObj; |
61 out = (FILE*) arg;· | 61 void *hashcx; |
62 fwrite (buf, len, 1, out); | 62 |
63 } | 63 /* XXX probably want to extend interface to allow other hash algorithms */ |
64 | 64 hashObj = HASH_GetHashObject(HASH_AlgSHA1); |
65 static int | 65 |
66 CreateDigest(SECItem *data, char *digestdata, unsigned int *len, unsigned int ma
xlen) | 66 hashcx = (*hashObj->create)(); |
67 { | 67 if (hashcx == NULL) return -1; |
68 const SECHashObject *hashObj; | 68 |
69 void *hashcx; | 69 (*hashObj->begin)(hashcx); |
70 | 70 (*hashObj->update)(hashcx, data->data, data->len); |
71 /* XXX probably want to extend interface to allow other hash algorithms */ | 71 (*hashObj->end)(hashcx, (unsigned char *)digestdata, len, maxlen); |
72 hashObj = HASH_GetHashObject(HASH_AlgSHA1); | 72 (*hashObj->destroy)(hashcx, PR_TRUE); |
73 | 73 return 0; |
74 hashcx = (* hashObj->create)(); | 74 } |
75 if (hashcx == NULL) | 75 |
76 » return -1; | 76 static int SignFile(FILE *outFile, PRFileDesc *inFile, CERTCertificate *cert, |
77 | 77 PRBool encapsulated) { |
78 (* hashObj->begin)(hashcx); | 78 char digestdata[32]; |
79 (* hashObj->update)(hashcx, data->data, data->len); | 79 unsigned int len; |
80 (* hashObj->end)(hashcx, (unsigned char *)digestdata, len, maxlen); | 80 SECItem digest, data2sign; |
81 (* hashObj->destroy)(hashcx, PR_TRUE); | 81 SEC_PKCS7ContentInfo *cinfo; |
82 return 0; | 82 SECStatus rv; |
83 } | 83 |
84 | 84 if (outFile == NULL || inFile == NULL || cert == NULL) return -1; |
85 static int | 85 |
86 SignFile(FILE *outFile, PRFileDesc *inFile, CERTCertificate *cert,· | 86 /* suck the file in */ |
87 PRBool encapsulated) | 87 if (SECU_ReadDERFromFile(&data2sign, inFile, PR_FALSE, PR_FALSE) != |
88 { | 88 SECSuccess) |
89 char digestdata[32]; | 89 return -1; |
90 unsigned int len; | 90 |
91 SECItem digest, data2sign; | 91 if (!encapsulated) { |
92 SEC_PKCS7ContentInfo *cinfo; | 92 /* unfortunately, we must create the digest ourselves */ |
93 SECStatus rv; | 93 /* SEC_PKCS7CreateSignedData should have a flag to not include */ |
94 | 94 /* the content for non-encapsulated content at encode time, but */ |
95 if (outFile == NULL || inFile == NULL || cert == NULL) | 95 /* should always compute the hash itself */ |
96 » return -1; | 96 if (CreateDigest(&data2sign, digestdata, &len, 32) < 0) return -1; |
97 | 97 digest.data = (unsigned char *)digestdata; |
98 /* suck the file in */ | 98 digest.len = len; |
99 » if (SECU_ReadDERFromFile(&data2sign, inFile, PR_FALSE, | 99 } |
100 » PR_FALSE) != SECSuccess) | 100 |
101 » return -1; | 101 /* XXX Need a better way to handle that usage stuff! */ |
102 | 102 cinfo = |
103 if (!encapsulated) { | 103 SEC_PKCS7CreateSignedData(cert, certUsageEmailSigner, NULL, SEC_OID_SHA1, |
104 » /* unfortunately, we must create the digest ourselves */ | 104 encapsulated ? NULL : &digest, NULL, NULL); |
105 » /* SEC_PKCS7CreateSignedData should have a flag to not include */ | 105 if (cinfo == NULL) return -1; |
106 » /* the content for non-encapsulated content at encode time, but */ | 106 |
107 » /* should always compute the hash itself */ | 107 if (encapsulated) { |
108 » if (CreateDigest(&data2sign, digestdata, &len, 32) < 0) | 108 SEC_PKCS7SetContent(cinfo, (char *)data2sign.data, data2sign.len); |
109 » return -1; | 109 } |
110 » digest.data = (unsigned char *)digestdata; | 110 |
111 » digest.len = len; | 111 rv = SEC_PKCS7IncludeCertChain(cinfo, NULL); |
| 112 if (rv != SECSuccess) { |
| 113 SEC_PKCS7DestroyContentInfo(cinfo); |
| 114 return -1; |
| 115 } |
| 116 |
| 117 rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL, &pwdata); |
| 118 |
| 119 SEC_PKCS7DestroyContentInfo(cinfo); |
| 120 |
| 121 if (rv != SECSuccess) return -1; |
| 122 |
| 123 return 0; |
| 124 } |
| 125 |
| 126 int main(int argc, char **argv) { |
| 127 char *progName; |
| 128 FILE *outFile; |
| 129 PRFileDesc *inFile; |
| 130 char *keyName = NULL; |
| 131 CERTCertDBHandle *certHandle; |
| 132 CERTCertificate *cert = NULL; |
| 133 PRBool encapsulated = PR_FALSE; |
| 134 PLOptState *optstate; |
| 135 PLOptStatus status; |
| 136 SECStatus rv; |
| 137 |
| 138 progName = strrchr(argv[0], '/'); |
| 139 progName = progName ? progName + 1 : argv[0]; |
| 140 |
| 141 inFile = NULL; |
| 142 outFile = NULL; |
| 143 keyName = NULL; |
| 144 |
| 145 /* |
| 146 * Parse command line arguments |
| 147 */ |
| 148 optstate = PL_CreateOptState(argc, argv, "ed:k:i:o:p:f:"); |
| 149 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { |
| 150 switch (optstate->option) { |
| 151 case '?': |
| 152 Usage(progName); |
| 153 break; |
| 154 |
| 155 case 'e': |
| 156 /* create a message with the signed content encapsulated */ |
| 157 encapsulated = PR_TRUE; |
| 158 break; |
| 159 |
| 160 case 'd': |
| 161 SECU_ConfigDirectory(optstate->value); |
| 162 break; |
| 163 |
| 164 case 'i': |
| 165 inFile = PR_Open(optstate->value, PR_RDONLY, 0); |
| 166 if (!inFile) { |
| 167 fprintf(stderr, "%s: unable to open \"%s\" for reading\n", progName, |
| 168 optstate->value); |
| 169 return -1; |
| 170 } |
| 171 break; |
| 172 |
| 173 case 'k': |
| 174 keyName = strdup(optstate->value); |
| 175 break; |
| 176 |
| 177 case 'o': |
| 178 outFile = fopen(optstate->value, "wb"); |
| 179 if (!outFile) { |
| 180 fprintf(stderr, "%s: unable to open \"%s\" for writing\n", progName, |
| 181 optstate->value); |
| 182 return -1; |
| 183 } |
| 184 break; |
| 185 case 'p': |
| 186 pwdata.source = PW_PLAINTEXT; |
| 187 pwdata.data = strdup(optstate->value); |
| 188 break; |
| 189 |
| 190 case 'f': |
| 191 pwdata.source = PW_FROMFILE; |
| 192 pwdata.data = PORT_Strdup(optstate->value); |
| 193 break; |
112 } | 194 } |
113 | 195 } |
114 /* XXX Need a better way to handle that usage stuff! */ | 196 |
115 cinfo = SEC_PKCS7CreateSignedData (cert, certUsageEmailSigner, NULL, | 197 if (!keyName) Usage(progName); |
116 » » » » SEC_OID_SHA1, | 198 |
117 » » » » encapsulated ? NULL : &digest, | 199 if (!inFile) inFile = PR_STDIN; |
118 » » » » NULL, NULL); | 200 if (!outFile) outFile = stdout; |
119 if (cinfo == NULL) | 201 |
120 » return -1; | 202 /* Call the initialization routines */ |
121 | 203 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); |
122 if (encapsulated) { | 204 rv = NSS_Init(SECU_ConfigDirectory(NULL)); |
123 » SEC_PKCS7SetContent(cinfo, (char *)data2sign.data, data2sign.len); | 205 if (rv != SECSuccess) { |
124 } | 206 SECU_PrintPRandOSError(progName); |
125 | 207 goto loser; |
126 rv = SEC_PKCS7IncludeCertChain (cinfo, NULL); | 208 } |
127 if (rv != SECSuccess) { | 209 |
128 » SEC_PKCS7DestroyContentInfo (cinfo); | 210 PK11_SetPasswordFunc(SECU_GetModulePassword); |
129 » return -1; | 211 |
130 } | 212 /* open cert database */ |
131 | 213 certHandle = CERT_GetDefaultCertDB(); |
132 rv = SEC_PKCS7Encode (cinfo, SignOut, outFile, NULL, | 214 if (certHandle == NULL) { |
133 » » » NULL, &pwdata); | 215 rv = SECFailure; |
134 | 216 goto loser; |
135 SEC_PKCS7DestroyContentInfo (cinfo); | 217 } |
136 | 218 |
137 if (rv != SECSuccess) | 219 /* find cert */ |
138 » return -1; | 220 cert = CERT_FindCertByNickname(certHandle, keyName); |
139 | 221 if (cert == NULL) { |
140 return 0; | 222 SECU_PrintError(progName, |
141 } | 223 "the corresponding cert for key \"%s\" does not exist", |
142 | 224 keyName); |
143 int | 225 rv = SECFailure; |
144 main(int argc, char **argv) | 226 goto loser; |
145 { | 227 } |
146 char *progName; | 228 |
147 FILE *outFile; | 229 if (SignFile(outFile, inFile, cert, encapsulated)) { |
148 PRFileDesc *inFile; | 230 SECU_PrintError(progName, "problem signing data"); |
149 char *keyName = NULL; | 231 rv = SECFailure; |
150 CERTCertDBHandle *certHandle; | 232 goto loser; |
151 CERTCertificate *cert = NULL; | 233 } |
152 PRBool encapsulated = PR_FALSE; | |
153 PLOptState *optstate; | |
154 PLOptStatus status; | |
155 SECStatus rv; | |
156 | |
157 progName = strrchr(argv[0], '/'); | |
158 progName = progName ? progName+1 : argv[0]; | |
159 | |
160 inFile = NULL; | |
161 outFile = NULL; | |
162 keyName = NULL; | |
163 | |
164 /* | |
165 * Parse command line arguments | |
166 */ | |
167 optstate = PL_CreateOptState(argc, argv, "ed:k:i:o:p:f:"); | |
168 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { | |
169 » switch (optstate->option) { | |
170 » case '?': | |
171 » Usage(progName); | |
172 » break; | |
173 | |
174 » case 'e': | |
175 » /* create a message with the signed content encapsulated */ | |
176 » encapsulated = PR_TRUE; | |
177 » break; | |
178 | |
179 » case 'd': | |
180 » SECU_ConfigDirectory(optstate->value); | |
181 » break; | |
182 | |
183 » case 'i': | |
184 » inFile = PR_Open(optstate->value, PR_RDONLY, 0); | |
185 » if (!inFile) { | |
186 » » fprintf(stderr, "%s: unable to open \"%s\" for reading\n", | |
187 » » » progName, optstate->value); | |
188 » » return -1; | |
189 » } | |
190 » break; | |
191 | |
192 » case 'k': | |
193 » keyName = strdup(optstate->value); | |
194 » break; | |
195 | |
196 » case 'o': | |
197 » outFile = fopen(optstate->value, "wb"); | |
198 » if (!outFile) { | |
199 » » fprintf(stderr, "%s: unable to open \"%s\" for writing\n", | |
200 » » » progName, optstate->value); | |
201 » » return -1; | |
202 » } | |
203 » break; | |
204 » case 'p': | |
205 pwdata.source = PW_PLAINTEXT; | |
206 pwdata.data = strdup (optstate->value); | |
207 break; | |
208 | |
209 » case 'f': | |
210 pwdata.source = PW_FROMFILE; | |
211 pwdata.data = PORT_Strdup (optstate->value); | |
212 break; | |
213 » } | |
214 } | |
215 | |
216 if (!keyName) Usage(progName); | |
217 | |
218 if (!inFile) inFile = PR_STDIN; | |
219 if (!outFile) outFile = stdout; | |
220 | |
221 /* Call the initialization routines */ | |
222 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); | |
223 rv = NSS_Init(SECU_ConfigDirectory(NULL)); | |
224 if (rv != SECSuccess) { | |
225 » SECU_PrintPRandOSError(progName); | |
226 » goto loser; | |
227 } | |
228 | |
229 PK11_SetPasswordFunc(SECU_GetModulePassword); | |
230 | |
231 /* open cert database */ | |
232 certHandle = CERT_GetDefaultCertDB(); | |
233 if (certHandle == NULL) { | |
234 » rv = SECFailure; | |
235 » goto loser; | |
236 } | |
237 | |
238 /* find cert */ | |
239 cert = CERT_FindCertByNickname(certHandle, keyName); | |
240 if (cert == NULL) { | |
241 » SECU_PrintError(progName, | |
242 » » "the corresponding cert for key \"%s\" does not exist", | |
243 » » » keyName); | |
244 » rv = SECFailure; | |
245 » goto loser; | |
246 } | |
247 | |
248 if (SignFile(outFile, inFile, cert, encapsulated)) { | |
249 » SECU_PrintError(progName, "problem signing data"); | |
250 » rv = SECFailure; | |
251 » goto loser; | |
252 } | |
253 | 234 |
254 loser: | 235 loser: |
255 if (pwdata.data) { | 236 if (pwdata.data) { |
256 PORT_Free(pwdata.data); | 237 PORT_Free(pwdata.data); |
257 } | 238 } |
258 if (keyName) { | 239 if (keyName) { |
259 PORT_Free(keyName); | 240 PORT_Free(keyName); |
260 } | 241 } |
261 if (cert) { | 242 if (cert) { |
262 CERT_DestroyCertificate(cert); | 243 CERT_DestroyCertificate(cert); |
263 } | 244 } |
264 if (inFile && inFile != PR_STDIN) { | 245 if (inFile && inFile != PR_STDIN) { |
265 PR_Close(inFile); | 246 PR_Close(inFile); |
266 } | 247 } |
267 if (outFile && outFile != stdout) { | 248 if (outFile && outFile != stdout) { |
268 fclose(outFile); | 249 fclose(outFile); |
269 } | 250 } |
270 if (NSS_Shutdown() != SECSuccess) { | 251 if (NSS_Shutdown() != SECSuccess) { |
271 SECU_PrintError(progName, "NSS shutdown:"); | 252 SECU_PrintError(progName, "NSS shutdown:"); |
272 exit(1); | 253 exit(1); |
273 } | 254 } |
274 | 255 |
275 return (rv != SECSuccess); | 256 return (rv != SECSuccess); |
276 } | 257 } |
OLD | NEW |