p7sign.c (10397B)
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 * p7sign -- A command to create a *detached* pkcs7 signature (over a given 7 * input file). 8 */ 9 10 #include "nspr.h" 11 #include "plgetopt.h" 12 #include "secutil.h" 13 #include "secpkcs7.h" 14 #include "cert.h" 15 #include "certdb.h" 16 #include "sechash.h" /* for HASH_GetHashObject() */ 17 #include "nss.h" 18 #include "pk11func.h" 19 20 #if defined(XP_UNIX) 21 #include <unistd.h> 22 #endif 23 24 #include <stdio.h> 25 #include <string.h> 26 27 #if (defined(XP_WIN) && !defined(WIN32)) || (defined(__sun) && !defined(SVR4)) 28 extern int fread(char *, size_t, size_t, FILE *); 29 extern int fwrite(char *, size_t, size_t, FILE *); 30 extern int fprintf(FILE *, char *, ...); 31 #endif 32 33 static secuPWData pwdata = { PW_NONE, 0 }; 34 35 static void 36 Usage(char *progName) 37 { 38 HASH_HashType hashAlg; 39 40 fprintf(stderr, 41 "Usage: %s -k keyname [-d keydir] [-i input] [-o output] [-e]\n", 42 progName); 43 fprintf(stderr, 44 " %*s [-p password|-f password file] [-a hash] [-u certusage]\n", 45 (int)strlen(progName), ""); 46 fprintf(stderr, "%-20s Nickname of key to use for signature\n", 47 "-k keyname"); 48 fprintf(stderr, "%-20s Key database directory (default is ~/.netscape)\n", 49 "-d keydir"); 50 fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", 51 "-i input"); 52 fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", 53 "-o output"); 54 fprintf(stderr, "%-20s Encapsulate content in signature message\n", 55 "-e"); 56 fprintf(stderr, "%-20s Password to the key databse\n", "-p password"); 57 fprintf(stderr, "%-20s File to read password from\n", "-f password file"); 58 fprintf(stderr, "%-20s Use case-insensitive hash algorithm (default: SHA-1)\n", 59 "-a hash"); 60 fprintf(stderr, "%-25s ", ""); 61 for (hashAlg = HASH_AlgNULL + 1; hashAlg != HASH_AlgTOTAL; ++hashAlg) 62 fprintf(stderr, "%s%s", hashAlg == HASH_AlgNULL + 1 ? "" : ", ", 63 SECOID_FindOIDByTag(HASH_GetHashOidTagByHashType(hashAlg))->desc); 64 fputc('\n', stderr); 65 fprintf(stderr, "%-20s Sign for usage (default: certUsageEmailSigner)\n", 66 "-u certusage"); 67 fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", ""); 68 fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", ""); 69 fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", ""); 70 fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", ""); 71 fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", ""); 72 fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", ""); 73 fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", ""); 74 fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", ""); 75 fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", ""); 76 fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", ""); 77 fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", ""); 78 fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", ""); 79 fprintf(stderr, "%-25s 12 - certUsageIPsec\n", ""); 80 exit(-1); 81 } 82 83 static void 84 SignOut(void *arg, const char *buf, unsigned long len) 85 { 86 FILE *out; 87 88 out = (FILE *)arg; 89 fwrite(buf, len, 1, out); 90 } 91 92 static int 93 CreateDigest(SECItem *data, char *digestdata, unsigned int *len, 94 unsigned int maxlen, HASH_HashType hashAlg) 95 { 96 const SECHashObject *hashObj; 97 void *hashcx; 98 99 hashObj = HASH_GetHashObject(hashAlg); 100 101 hashcx = (*hashObj->create)(); 102 if (hashcx == NULL) 103 return -1; 104 105 (*hashObj->begin)(hashcx); 106 (*hashObj->update)(hashcx, data->data, data->len); 107 (*hashObj->end)(hashcx, (unsigned char *)digestdata, len, maxlen); 108 (*hashObj->destroy)(hashcx, PR_TRUE); 109 return 0; 110 } 111 112 static int 113 SignFile(FILE *outFile, PRFileDesc *inFile, CERTCertificate *cert, 114 PRBool encapsulated, HASH_HashType hashAlg, SECOidTag hashAlgOid, 115 SECCertUsage usage) 116 { 117 char digestdata[HASH_LENGTH_MAX]; 118 unsigned int len; 119 SECItem digest, data2sign; 120 SEC_PKCS7ContentInfo *cinfo; 121 SECStatus rv; 122 123 if (outFile == NULL || inFile == NULL || cert == NULL) 124 return -1; 125 126 /* suck the file in */ 127 if (SECU_ReadDERFromFile(&data2sign, inFile, PR_FALSE, 128 PR_FALSE) != SECSuccess) 129 return -1; 130 131 if (!encapsulated) { 132 /* unfortunately, we must create the digest ourselves */ 133 /* SEC_PKCS7CreateSignedData should have a flag to not include */ 134 /* the content for non-encapsulated content at encode time, but */ 135 /* should always compute the hash itself */ 136 if (CreateDigest(&data2sign, digestdata, &len, 137 sizeof(digestdata), hashAlg) < 0) { 138 SECITEM_FreeItem(&data2sign, PR_FALSE); 139 return -1; 140 } 141 digest.data = (unsigned char *)digestdata; 142 digest.len = len; 143 } 144 145 cinfo = SEC_PKCS7CreateSignedData(cert, usage, NULL, 146 hashAlgOid, 147 encapsulated ? NULL : &digest, 148 NULL, NULL); 149 if (cinfo == NULL) { 150 SECITEM_FreeItem(&data2sign, PR_FALSE); 151 return -1; 152 } 153 154 if (encapsulated) { 155 SEC_PKCS7SetContent(cinfo, (char *)data2sign.data, data2sign.len); 156 } 157 158 rv = SEC_PKCS7IncludeCertChain(cinfo, NULL); 159 if (rv != SECSuccess) { 160 SEC_PKCS7DestroyContentInfo(cinfo); 161 SECITEM_FreeItem(&data2sign, PR_FALSE); 162 return -1; 163 } 164 165 rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, 166 NULL, &pwdata); 167 168 SECITEM_FreeItem(&data2sign, PR_FALSE); 169 SEC_PKCS7DestroyContentInfo(cinfo); 170 171 if (rv != SECSuccess) 172 return -1; 173 174 return 0; 175 } 176 177 int 178 main(int argc, char **argv) 179 { 180 char *progName; 181 FILE *outFile; 182 PRFileDesc *inFile; 183 char *keyName = NULL; 184 CERTCertDBHandle *certHandle; 185 CERTCertificate *cert = NULL; 186 PRBool encapsulated = PR_FALSE; 187 HASH_HashType hashAlg = HASH_AlgSHA1; 188 SECOidTag hashAlgOid = SEC_OID_SHA1; 189 SECCertUsage usage = certUsageEmailSigner; 190 PLOptState *optstate; 191 PLOptStatus status; 192 SECStatus rv; 193 194 progName = strrchr(argv[0], '/'); 195 progName = progName ? progName + 1 : argv[0]; 196 197 inFile = NULL; 198 outFile = NULL; 199 keyName = NULL; 200 201 /* 202 * Parse command line arguments 203 */ 204 optstate = PL_CreateOptState(argc, argv, "ed:k:i:o:p:f:a:u:"); 205 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 206 switch (optstate->option) { 207 case '?': 208 Usage(progName); 209 break; 210 211 case 'e': 212 /* create a message with the signed content encapsulated */ 213 encapsulated = PR_TRUE; 214 break; 215 216 case 'd': 217 SECU_ConfigDirectory(optstate->value); 218 break; 219 220 case 'i': 221 inFile = PR_Open(optstate->value, PR_RDONLY, 0); 222 if (!inFile) { 223 fprintf(stderr, "%s: unable to open \"%s\" for reading\n", 224 progName, optstate->value); 225 return -1; 226 } 227 break; 228 229 case 'k': 230 keyName = strdup(optstate->value); 231 break; 232 233 case 'o': 234 outFile = fopen(optstate->value, "wb"); 235 if (!outFile) { 236 fprintf(stderr, "%s: unable to open \"%s\" for writing\n", 237 progName, optstate->value); 238 return -1; 239 } 240 break; 241 case 'p': 242 pwdata.source = PW_PLAINTEXT; 243 pwdata.data = strdup(optstate->value); 244 break; 245 246 case 'f': 247 pwdata.source = PW_FROMFILE; 248 pwdata.data = PORT_Strdup(optstate->value); 249 break; 250 251 case 'a': 252 for (hashAlg = HASH_AlgNULL + 1; hashAlg != HASH_AlgTOTAL; 253 ++hashAlg) { 254 hashAlgOid = HASH_GetHashOidTagByHashType(hashAlg); 255 if (!PORT_Strcasecmp(optstate->value, 256 SECOID_FindOIDByTag(hashAlgOid)->desc)) 257 break; 258 } 259 if (hashAlg == HASH_AlgTOTAL) 260 Usage(progName); 261 break; 262 263 case 'u': 264 usage = atoi(optstate->value); 265 if (usage < certUsageSSLClient || usage > certUsageIPsec) 266 Usage(progName); 267 break; 268 } 269 } 270 PL_DestroyOptState(optstate); 271 272 if (!keyName) 273 Usage(progName); 274 275 if (!inFile) 276 inFile = PR_STDIN; 277 if (!outFile) 278 outFile = stdout; 279 280 /* Call the initialization routines */ 281 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 282 rv = NSS_Init(SECU_ConfigDirectory(NULL)); 283 if (rv != SECSuccess) { 284 SECU_PrintPRandOSError(progName); 285 goto loser; 286 } 287 288 PK11_SetPasswordFunc(SECU_GetModulePassword); 289 290 /* open cert database */ 291 certHandle = CERT_GetDefaultCertDB(); 292 if (certHandle == NULL) { 293 rv = SECFailure; 294 goto loser; 295 } 296 297 /* find cert */ 298 cert = SECU_FindCertByNicknameOrFilename(certHandle, keyName, PR_FALSE, NULL); 299 if (cert == NULL) { 300 SECU_PrintError(progName, 301 "the corresponding cert for key \"%s\" does not exist", 302 keyName); 303 rv = SECFailure; 304 goto loser; 305 } 306 307 if (SignFile(outFile, inFile, cert, encapsulated, 308 hashAlg, hashAlgOid, usage)) { 309 SECU_PrintError(progName, "problem signing data"); 310 rv = SECFailure; 311 goto loser; 312 } 313 314 loser: 315 if (pwdata.data) { 316 PORT_Free(pwdata.data); 317 } 318 if (keyName) { 319 PORT_Free(keyName); 320 } 321 if (cert) { 322 CERT_DestroyCertificate(cert); 323 } 324 if (inFile && inFile != PR_STDIN) { 325 PR_Close(inFile); 326 } 327 if (outFile && outFile != stdout) { 328 fclose(outFile); 329 } 330 if (NSS_Shutdown() != SECSuccess) { 331 SECU_PrintError(progName, "NSS shutdown:"); 332 exit(1); 333 } 334 335 return (rv != SECSuccess); 336 }