Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/. */
5 /*
6 ** certutil.c
7 **
8 ** utility for managing certificates and the cert database
9 **
10 */
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
15 #if defined(WIN32)
16 #include "fcntl.h"
17 #include "io.h"
18 #endif
20 #include "secutil.h"
22 #if defined(XP_UNIX)
23 #include <unistd.h>
24 #endif
26 #include "nspr.h"
27 #include "prtypes.h"
28 #include "prtime.h"
29 #include "prlong.h"
31 #include "pk11func.h"
32 #include "secasn1.h"
33 #include "cert.h"
34 #include "cryptohi.h"
35 #include "secoid.h"
36 #include "certdb.h"
37 #include "nss.h"
38 #include "certutil.h"
40 #define MIN_KEY_BITS 512
41 /* MAX_KEY_BITS should agree with MAX_RSA_MODULUS in freebl */
42 #define MAX_KEY_BITS 8192
43 #define DEFAULT_KEY_BITS 1024
45 #define GEN_BREAK(e) rv=e; break;
47 char *progName;
49 static CERTCertificateRequest *
50 GetCertRequest(const SECItem *reqDER)
51 {
52 CERTCertificateRequest *certReq = NULL;
53 CERTSignedData signedData;
54 PLArenaPool *arena = NULL;
55 SECStatus rv;
57 do {
58 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
59 if (arena == NULL) {
60 GEN_BREAK (SECFailure);
61 }
63 certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc
64 (arena, sizeof(CERTCertificateRequest));
65 if (!certReq) {
66 GEN_BREAK(SECFailure);
67 }
68 certReq->arena = arena;
70 /* Since cert request is a signed data, must decode to get the inner
71 data
72 */
73 PORT_Memset(&signedData, 0, sizeof(signedData));
74 rv = SEC_ASN1DecodeItem(arena, &signedData,
75 SEC_ASN1_GET(CERT_SignedDataTemplate), reqDER);
76 if (rv) {
77 break;
78 }
79 rv = SEC_ASN1DecodeItem(arena, certReq,
80 SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data);
81 if (rv) {
82 break;
83 }
84 rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData,
85 &certReq->subjectPublicKeyInfo, NULL /* wincx */);
86 } while (0);
88 if (rv) {
89 SECU_PrintError(progName, "bad certificate request\n");
90 if (arena) {
91 PORT_FreeArena(arena, PR_FALSE);
92 }
93 certReq = NULL;
94 }
96 return certReq;
97 }
99 static SECStatus
100 AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, char *name, char *trusts,
101 const SECItem *certDER, PRBool emailcert, void *pwdata)
102 {
103 CERTCertTrust *trust = NULL;
104 CERTCertificate *cert = NULL;
105 SECStatus rv;
107 do {
108 /* Read in an ASCII cert and return a CERTCertificate */
109 cert = CERT_DecodeCertFromPackage((char *)certDER->data, certDER->len);
110 if (!cert) {
111 SECU_PrintError(progName, "could not decode certificate");
112 GEN_BREAK(SECFailure);
113 }
115 /* Create a cert trust */
116 trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
117 if (!trust) {
118 SECU_PrintError(progName, "unable to allocate cert trust");
119 GEN_BREAK(SECFailure);
120 }
122 rv = CERT_DecodeTrustString(trust, trusts);
123 if (rv) {
124 SECU_PrintError(progName, "unable to decode trust string");
125 GEN_BREAK(SECFailure);
126 }
128 rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE);
129 if (rv != SECSuccess) {
130 /* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have
131 * been coded to take a password arg. */
132 if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
133 rv = PK11_Authenticate(slot, PR_TRUE, pwdata);
134 if (rv != SECSuccess) {
135 SECU_PrintError(progName,
136 "could not authenticate to token %s.",
137 PK11_GetTokenName(slot));
138 GEN_BREAK(SECFailure);
139 }
140 rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE,
141 name, PR_FALSE);
142 }
143 if (rv != SECSuccess) {
144 SECU_PrintError(progName,
145 "could not add certificate to token or database");
146 GEN_BREAK(SECFailure);
147 }
148 }
150 rv = CERT_ChangeCertTrust(handle, cert, trust);
151 if (rv != SECSuccess) {
152 if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
153 rv = PK11_Authenticate(slot, PR_TRUE, pwdata);
154 if (rv != SECSuccess) {
155 SECU_PrintError(progName,
156 "could not authenticate to token %s.",
157 PK11_GetTokenName(slot));
158 GEN_BREAK(SECFailure);
159 }
160 rv = CERT_ChangeCertTrust(handle, cert, trust);
161 }
162 if (rv != SECSuccess) {
163 SECU_PrintError(progName,
164 "could not change trust on certificate");
165 GEN_BREAK(SECFailure);
166 }
167 }
169 if ( emailcert ) {
170 CERT_SaveSMimeProfile(cert, NULL, pwdata);
171 }
173 } while (0);
175 CERT_DestroyCertificate (cert);
176 PORT_Free(trust);
178 return rv;
179 }
181 static SECStatus
182 CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType,
183 SECOidTag hashAlgTag, CERTName *subject, char *phone, int ascii,
184 const char *emailAddrs, const char *dnsNames,
185 certutilExtnList extnList, const char *extGeneric,
186 /*out*/ SECItem *result)
187 {
188 CERTSubjectPublicKeyInfo *spki;
189 CERTCertificateRequest *cr;
190 SECItem *encoding;
191 SECOidTag signAlgTag;
192 SECStatus rv;
193 PLArenaPool *arena;
194 void *extHandle;
195 SECItem signedReq = { siBuffer, NULL, 0 };
197 /* Create info about public key */
198 spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
199 if (!spki) {
200 SECU_PrintError(progName, "unable to create subject public key");
201 return SECFailure;
202 }
204 /* Generate certificate request */
205 cr = CERT_CreateCertificateRequest(subject, spki, NULL);
206 SECKEY_DestroySubjectPublicKeyInfo(spki);
207 if (!cr) {
208 SECU_PrintError(progName, "unable to make certificate request");
209 return SECFailure;
210 }
212 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
213 if ( !arena ) {
214 SECU_PrintError(progName, "out of memory");
215 return SECFailure;
216 }
218 extHandle = CERT_StartCertificateRequestAttributes(cr);
219 if (extHandle == NULL) {
220 PORT_FreeArena (arena, PR_FALSE);
221 return SECFailure;
222 }
223 if (AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric)
224 != SECSuccess) {
225 PORT_FreeArena (arena, PR_FALSE);
226 return SECFailure;
227 }
228 CERT_FinishExtensions(extHandle);
229 CERT_FinishCertificateRequestAttributes(cr);
231 /* Der encode the request */
232 encoding = SEC_ASN1EncodeItem(arena, NULL, cr,
233 SEC_ASN1_GET(CERT_CertificateRequestTemplate));
234 CERT_DestroyCertificateRequest(cr);
235 if (encoding == NULL) {
236 PORT_FreeArena (arena, PR_FALSE);
237 SECU_PrintError(progName, "der encoding of request failed");
238 return SECFailure;
239 }
241 /* Sign the request */
242 signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
243 if (signAlgTag == SEC_OID_UNKNOWN) {
244 PORT_FreeArena (arena, PR_FALSE);
245 SECU_PrintError(progName, "unknown Key or Hash type");
246 return SECFailure;
247 }
249 rv = SEC_DerSignData(arena, &signedReq, encoding->data, encoding->len,
250 privk, signAlgTag);
251 if (rv) {
252 PORT_FreeArena (arena, PR_FALSE);
253 SECU_PrintError(progName, "signing of data failed");
254 return SECFailure;
255 }
257 /* Encode request in specified format */
258 if (ascii) {
259 char *obuf;
260 char *header, *name, *email, *org, *state, *country;
262 obuf = BTOA_ConvertItemToAscii(&signedReq);
263 if (!obuf) {
264 goto oom;
265 }
267 name = CERT_GetCommonName(subject);
268 if (!name) {
269 name = PORT_Strdup("(not specified)");
270 }
272 if (!phone)
273 phone = strdup("(not specified)");
275 email = CERT_GetCertEmailAddress(subject);
276 if (!email)
277 email = PORT_Strdup("(not specified)");
279 org = CERT_GetOrgName(subject);
280 if (!org)
281 org = PORT_Strdup("(not specified)");
283 state = CERT_GetStateName(subject);
284 if (!state)
285 state = PORT_Strdup("(not specified)");
287 country = CERT_GetCountryName(subject);
288 if (!country)
289 country = PORT_Strdup("(not specified)");
291 header = PR_smprintf(
292 "\nCertificate request generated by Netscape certutil\n"
293 "Phone: %s\n\n"
294 "Common Name: %s\n"
295 "Email: %s\n"
296 "Organization: %s\n"
297 "State: %s\n"
298 "Country: %s\n\n"
299 "%s\n",
300 phone, name, email, org, state, country, NS_CERTREQ_HEADER);
302 PORT_Free(name);
303 PORT_Free(email);
304 PORT_Free(org);
305 PORT_Free(state);
306 PORT_Free(country);
308 if (header) {
309 char * trailer = PR_smprintf("\n%s\n", NS_CERTREQ_TRAILER);
310 if (trailer) {
311 PRUint32 headerLen = PL_strlen(header);
312 PRUint32 obufLen = PL_strlen(obuf);
313 PRUint32 trailerLen = PL_strlen(trailer);
314 SECITEM_AllocItem(NULL, result,
315 headerLen + obufLen + trailerLen);
316 if (result->data) {
317 PORT_Memcpy(result->data, header, headerLen);
318 PORT_Memcpy(result->data + headerLen, obuf, obufLen);
319 PORT_Memcpy(result->data + headerLen + obufLen,
320 trailer, trailerLen);
321 }
322 PR_smprintf_free(trailer);
323 }
324 PR_smprintf_free(header);
325 }
326 } else {
327 (void) SECITEM_CopyItem(NULL, result, &signedReq);
328 }
330 if (!result->data) {
331 oom: SECU_PrintError(progName, "out of memory");
332 PORT_SetError(SEC_ERROR_NO_MEMORY);
333 rv = SECFailure;
334 }
336 PORT_FreeArena (arena, PR_FALSE);
337 return rv;
338 }
340 static SECStatus
341 ChangeTrustAttributes(CERTCertDBHandle *handle, PK11SlotInfo *slot,
342 char *name, char *trusts, void *pwdata)
343 {
344 SECStatus rv;
345 CERTCertificate *cert;
346 CERTCertTrust *trust;
348 cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
349 if (!cert) {
350 SECU_PrintError(progName, "could not find certificate named \"%s\"",
351 name);
352 return SECFailure;
353 }
355 trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
356 if (!trust) {
357 SECU_PrintError(progName, "unable to allocate cert trust");
358 return SECFailure;
359 }
361 /* This function only decodes these characters: pPwcTCu, */
362 rv = CERT_DecodeTrustString(trust, trusts);
363 if (rv) {
364 SECU_PrintError(progName, "unable to decode trust string");
365 return SECFailure;
366 }
368 /* CERT_ChangeCertTrust API does not have a way to pass in
369 * a context, so NSS can't prompt for the password if it needs to.
370 * check to see if the failure was token not logged in and
371 * log in if need be. */
372 rv = CERT_ChangeCertTrust(handle, cert, trust);
373 if (rv != SECSuccess) {
374 if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
375 rv = PK11_Authenticate(slot, PR_TRUE, pwdata);
376 if (rv != SECSuccess) {
377 SECU_PrintError(progName, "could not authenticate to token %s.",
378 PK11_GetTokenName(slot));
379 return SECFailure;
380 }
381 rv = CERT_ChangeCertTrust(handle, cert, trust);
382 }
383 if (rv != SECSuccess) {
384 SECU_PrintError(progName, "unable to modify trust attributes");
385 return SECFailure;
386 }
387 }
388 CERT_DestroyCertificate(cert);
390 return SECSuccess;
391 }
393 static SECStatus
394 DumpChain(CERTCertDBHandle *handle, char *name, PRBool ascii)
395 {
396 CERTCertificate *the_cert;
397 CERTCertificateList *chain;
398 int i, j;
399 the_cert = SECU_FindCertByNicknameOrFilename(handle, name,
400 ascii, NULL);
401 if (!the_cert) {
402 SECU_PrintError(progName, "Could not find: %s\n", name);
403 return SECFailure;
404 }
405 chain = CERT_CertChainFromCert(the_cert, 0, PR_TRUE);
406 CERT_DestroyCertificate(the_cert);
407 if (!chain) {
408 SECU_PrintError(progName, "Could not obtain chain for: %s\n", name);
409 return SECFailure;
410 }
411 for (i=chain->len-1; i>=0; i--) {
412 CERTCertificate *c;
413 c = CERT_FindCertByDERCert(handle, &chain->certs[i]);
414 for (j=i; j<chain->len-1; j++) printf(" ");
415 printf("\"%s\" [%s]\n\n", c->nickname, c->subjectName);
416 CERT_DestroyCertificate(c);
417 }
418 CERT_DestroyCertificateList(chain);
419 return SECSuccess;
420 }
422 static SECStatus
423 outputCertOrExtension(CERTCertificate *the_cert, PRBool raw, PRBool ascii,
424 SECItem *extensionOID, PRFileDesc *outfile)
425 {
426 SECItem data;
427 PRInt32 numBytes;
428 SECStatus rv = SECFailure;
429 if (extensionOID) {
430 int i;
431 PRBool found = PR_FALSE;
432 for (i=0; the_cert->extensions[i] != NULL; i++) {
433 CERTCertExtension *extension = the_cert->extensions[i];
434 if (SECITEM_CompareItem(&extension->id, extensionOID) == SECEqual) {
435 found = PR_TRUE;
436 numBytes = PR_Write(outfile, extension->value.data,
437 extension->value.len);
438 rv = SECSuccess;
439 if (numBytes != (PRInt32) extension->value.len) {
440 SECU_PrintSystemError(progName, "error writing extension");
441 rv = SECFailure;
442 }
443 rv = SECSuccess;
444 break;
445 }
446 }
447 if (!found) {
448 SECU_PrintSystemError(progName, "extension not found");
449 rv = SECFailure;
450 }
451 } else {
452 data.data = the_cert->derCert.data;
453 data.len = the_cert->derCert.len;
454 if (ascii) {
455 PR_fprintf(outfile, "%s\n%s\n%s\n", NS_CERT_HEADER,
456 BTOA_DataToAscii(data.data, data.len), NS_CERT_TRAILER);
457 rv = SECSuccess;
458 } else if (raw) {
459 numBytes = PR_Write(outfile, data.data, data.len);
460 rv = SECSuccess;
461 if (numBytes != (PRInt32) data.len) {
462 SECU_PrintSystemError(progName, "error writing raw cert");
463 rv = SECFailure;
464 }
465 } else {
466 rv = SEC_PrintCertificateAndTrust(the_cert, "Certificate", NULL);
467 if (rv != SECSuccess) {
468 SECU_PrintError(progName, "problem printing certificate");
469 }
470 }
471 }
472 return rv;
473 }
475 static SECStatus
476 listCerts(CERTCertDBHandle *handle, char *name, char *email,
477 PK11SlotInfo *slot, PRBool raw, PRBool ascii,
478 SECItem *extensionOID,
479 PRFileDesc *outfile, void *pwarg)
480 {
481 SECStatus rv = SECFailure;
482 CERTCertList *certs;
483 CERTCertListNode *node;
485 /* List certs on a non-internal slot. */
486 if (!PK11_IsFriendly(slot) && PK11_NeedLogin(slot)) {
487 SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, pwarg);
488 if (newrv != SECSuccess) {
489 SECU_PrintError(progName, "could not authenticate to token %s.",
490 PK11_GetTokenName(slot));
491 return SECFailure;
492 }
493 }
494 if (name) {
495 CERTCertificate *the_cert =
496 SECU_FindCertByNicknameOrFilename(handle, name, ascii, NULL);
497 if (!the_cert) {
498 SECU_PrintError(progName, "Could not find cert: %s\n", name);
499 return SECFailure;
500 }
501 /* Here, we have one cert with the desired nickname or email
502 * address. Now, we will attempt to get a list of ALL certs
503 * with the same subject name as the cert we have. That list
504 * should contain, at a minimum, the one cert we have already found.
505 * If the list of certs is empty (NULL), the libraries have failed.
506 */
507 certs = CERT_CreateSubjectCertList(NULL, handle, &the_cert->derSubject,
508 PR_Now(), PR_FALSE);
509 CERT_DestroyCertificate(the_cert);
510 if (!certs) {
511 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
512 SECU_PrintError(progName, "problem printing certificates");
513 return SECFailure;
514 }
515 for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs);
516 node = CERT_LIST_NEXT(node)) {
517 rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID,
518 outfile);
519 if (rv != SECSuccess) {
520 break;
521 }
522 }
523 } else if (email) {
524 certs = PK11_FindCertsFromEmailAddress(email, NULL);
525 if (!certs) {
526 SECU_PrintError(progName,
527 "Could not find certificates for email address: %s\n",
528 email);
529 return SECFailure;
530 }
531 for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs);
532 node = CERT_LIST_NEXT(node)) {
533 rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID,
534 outfile);
535 if (rv != SECSuccess) {
536 break;
537 }
538 }
539 } else {
540 certs = PK11_ListCertsInSlot(slot);
541 if (certs) {
542 for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs);
543 node = CERT_LIST_NEXT(node)) {
544 SECU_PrintCertNickname(node,stdout);
545 }
546 rv = SECSuccess;
547 }
548 }
549 if (certs) {
550 CERT_DestroyCertList(certs);
551 }
552 if (rv) {
553 SECU_PrintError(progName, "problem printing certificate nicknames");
554 return SECFailure;
555 }
557 return SECSuccess; /* not rv ?? */
558 }
560 static SECStatus
561 ListCerts(CERTCertDBHandle *handle, char *nickname, char *email,
562 PK11SlotInfo *slot, PRBool raw, PRBool ascii,
563 SECItem *extensionOID,
564 PRFileDesc *outfile, secuPWData *pwdata)
565 {
566 SECStatus rv;
568 if (!ascii && !raw && !nickname && !email) {
569 PR_fprintf(outfile, "\n%-60s %-5s\n%-60s %-5s\n\n",
570 "Certificate Nickname", "Trust Attributes", "",
571 "SSL,S/MIME,JAR/XPI");
572 }
573 if (slot == NULL) {
574 CERTCertList *list;
575 CERTCertListNode *node;
577 list = PK11_ListCerts(PK11CertListAll, pwdata);
578 for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
579 node = CERT_LIST_NEXT(node)) {
580 SECU_PrintCertNickname(node, stdout);
581 }
582 CERT_DestroyCertList(list);
583 return SECSuccess;
584 }
585 rv = listCerts(handle, nickname, email, slot, raw, ascii,
586 extensionOID, outfile, pwdata);
587 return rv;
588 }
590 static SECStatus
591 DeleteCert(CERTCertDBHandle *handle, char *name)
592 {
593 SECStatus rv;
594 CERTCertificate *cert;
596 cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
597 if (!cert) {
598 SECU_PrintError(progName, "could not find certificate named \"%s\"",
599 name);
600 return SECFailure;
601 }
603 rv = SEC_DeletePermCertificate(cert);
604 CERT_DestroyCertificate(cert);
605 if (rv) {
606 SECU_PrintError(progName, "unable to delete certificate");
607 }
608 return rv;
609 }
611 static SECStatus
612 ValidateCert(CERTCertDBHandle *handle, char *name, char *date,
613 char *certUsage, PRBool checkSig, PRBool logit,
614 PRBool ascii, secuPWData *pwdata)
615 {
616 SECStatus rv;
617 CERTCertificate *cert = NULL;
618 PRTime timeBoundary;
619 SECCertificateUsage usage;
620 CERTVerifyLog reallog;
621 CERTVerifyLog *log = NULL;
623 if (!certUsage) {
624 PORT_SetError (SEC_ERROR_INVALID_ARGS);
625 return (SECFailure);
626 }
628 switch (*certUsage) {
629 case 'O':
630 usage = certificateUsageStatusResponder;
631 break;
632 case 'L':
633 usage = certificateUsageSSLCA;
634 break;
635 case 'A':
636 usage = certificateUsageAnyCA;
637 break;
638 case 'Y':
639 usage = certificateUsageVerifyCA;
640 break;
641 case 'C':
642 usage = certificateUsageSSLClient;
643 break;
644 case 'V':
645 usage = certificateUsageSSLServer;
646 break;
647 case 'S':
648 usage = certificateUsageEmailSigner;
649 break;
650 case 'R':
651 usage = certificateUsageEmailRecipient;
652 break;
653 case 'J':
654 usage = certificateUsageObjectSigner;
655 break;
656 default:
657 PORT_SetError (SEC_ERROR_INVALID_ARGS);
658 return (SECFailure);
659 }
660 do {
661 cert = SECU_FindCertByNicknameOrFilename(handle, name, ascii,
662 NULL);
663 if (!cert) {
664 SECU_PrintError(progName, "could not find certificate named \"%s\"",
665 name);
666 GEN_BREAK (SECFailure)
667 }
669 if (date != NULL) {
670 rv = DER_AsciiToTime(&timeBoundary, date);
671 if (rv) {
672 SECU_PrintError(progName, "invalid input date");
673 GEN_BREAK (SECFailure)
674 }
675 } else {
676 timeBoundary = PR_Now();
677 }
679 if ( logit ) {
680 log = &reallog;
682 log->count = 0;
683 log->head = NULL;
684 log->tail = NULL;
685 log->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
686 if ( log->arena == NULL ) {
687 SECU_PrintError(progName, "out of memory");
688 GEN_BREAK (SECFailure)
689 }
690 }
692 rv = CERT_VerifyCertificate(handle, cert, checkSig, usage,
693 timeBoundary, pwdata, log, &usage);
694 if ( log ) {
695 if ( log->head == NULL ) {
696 fprintf(stdout, "%s: certificate is valid\n", progName);
697 GEN_BREAK (SECSuccess)
698 } else {
699 char *name;
700 CERTVerifyLogNode *node;
702 node = log->head;
703 while ( node ) {
704 if ( node->cert->nickname != NULL ) {
705 name = node->cert->nickname;
706 } else {
707 name = node->cert->subjectName;
708 }
709 fprintf(stderr, "%s : %s\n", name,
710 SECU_Strerror(node->error));
711 CERT_DestroyCertificate(node->cert);
712 node = node->next;
713 }
714 }
715 } else {
716 if (rv != SECSuccess) {
717 PRErrorCode perr = PORT_GetError();
718 fprintf(stdout, "%s: certificate is invalid: %s\n",
719 progName, SECU_Strerror(perr));
720 GEN_BREAK (SECFailure)
721 }
722 fprintf(stdout, "%s: certificate is valid\n", progName);
723 GEN_BREAK (SECSuccess)
724 }
725 } while (0);
727 if (cert) {
728 CERT_DestroyCertificate(cert);
729 }
731 return (rv);
732 }
734 static PRBool
735 ItemIsPrintableASCII(const SECItem * item)
736 {
737 unsigned char *src = item->data;
738 unsigned int len = item->len;
739 while (len-- > 0) {
740 unsigned char uc = *src++;
741 if (uc < 0x20 || uc > 0x7e)
742 return PR_FALSE;
743 }
744 return PR_TRUE;
745 }
747 /* Caller ensures that dst is at least item->len*2+1 bytes long */
748 static void
749 SECItemToHex(const SECItem * item, char * dst)
750 {
751 if (dst && item && item->data) {
752 unsigned char * src = item->data;
753 unsigned int len = item->len;
754 for (; len > 0; --len, dst += 2) {
755 sprintf(dst, "%02x", *src++);
756 }
757 *dst = '\0';
758 }
759 }
761 static const char * const keyTypeName[] = {
762 "null", "rsa", "dsa", "fortezza", "dh", "kea", "ec" };
764 #define MAX_CKA_ID_BIN_LEN 20
765 #define MAX_CKA_ID_STR_LEN 40
767 /* print key number, key ID (in hex or ASCII), key label (nickname) */
768 static SECStatus
769 PrintKey(PRFileDesc *out, const char *nickName, int count,
770 SECKEYPrivateKey *key, void *pwarg)
771 {
772 SECItem * ckaID;
773 char ckaIDbuf[MAX_CKA_ID_STR_LEN + 4];
775 pwarg = NULL;
776 ckaID = PK11_GetLowLevelKeyIDForPrivateKey(key);
777 if (!ckaID) {
778 strcpy(ckaIDbuf, "(no CKA_ID)");
779 } else if (ItemIsPrintableASCII(ckaID)) {
780 int len = PR_MIN(MAX_CKA_ID_STR_LEN, ckaID->len);
781 ckaIDbuf[0] = '"';
782 memcpy(ckaIDbuf + 1, ckaID->data, len);
783 ckaIDbuf[1 + len] = '"';
784 ckaIDbuf[2 + len] = '\0';
785 } else {
786 /* print ckaid in hex */
787 SECItem idItem = *ckaID;
788 if (idItem.len > MAX_CKA_ID_BIN_LEN)
789 idItem.len = MAX_CKA_ID_BIN_LEN;
790 SECItemToHex(&idItem, ckaIDbuf);
791 }
793 PR_fprintf(out, "<%2d> %-8.8s %-42.42s %s\n", count,
794 keyTypeName[key->keyType], ckaIDbuf, nickName);
795 SECITEM_ZfreeItem(ckaID, PR_TRUE);
797 return SECSuccess;
798 }
800 /* returns SECSuccess if ANY keys are found, SECFailure otherwise. */
801 static SECStatus
802 ListKeysInSlot(PK11SlotInfo *slot, const char *nickName, KeyType keyType,
803 void *pwarg)
804 {
805 SECKEYPrivateKeyList *list;
806 SECKEYPrivateKeyListNode *node;
807 int count = 0;
809 if (PK11_NeedLogin(slot)) {
810 SECStatus rv = PK11_Authenticate(slot, PR_TRUE, pwarg);
811 if (rv != SECSuccess) {
812 SECU_PrintError(progName, "could not authenticate to token %s.",
813 PK11_GetTokenName(slot));
814 return SECFailure;
815 }
816 }
818 if (nickName && nickName[0])
819 list = PK11_ListPrivKeysInSlot(slot, (char *)nickName, pwarg);
820 else
821 list = PK11_ListPrivateKeysInSlot(slot);
822 if (list == NULL) {
823 SECU_PrintError(progName, "problem listing keys");
824 return SECFailure;
825 }
826 for (node=PRIVKEY_LIST_HEAD(list);
827 !PRIVKEY_LIST_END(node,list);
828 node=PRIVKEY_LIST_NEXT(node)) {
829 char * keyName;
830 static const char orphan[] = { "(orphan)" };
832 if (keyType != nullKey && keyType != node->key->keyType)
833 continue;
834 keyName = PK11_GetPrivateKeyNickname(node->key);
835 if (!keyName || !keyName[0]) {
836 /* Try extra hard to find nicknames for keys that lack them. */
837 CERTCertificate * cert;
838 PORT_Free((void *)keyName);
839 keyName = NULL;
840 cert = PK11_GetCertFromPrivateKey(node->key);
841 if (cert) {
842 if (cert->nickname && cert->nickname[0]) {
843 keyName = PORT_Strdup(cert->nickname);
844 } else if (cert->emailAddr && cert->emailAddr[0]) {
845 keyName = PORT_Strdup(cert->emailAddr);
846 }
847 CERT_DestroyCertificate(cert);
848 }
849 }
850 if (nickName) {
851 if (!keyName || PL_strcmp(keyName,nickName)) {
852 /* PKCS#11 module returned unwanted keys */
853 PORT_Free((void *)keyName);
854 continue;
855 }
856 }
857 if (!keyName)
858 keyName = (char *)orphan;
860 PrintKey(PR_STDOUT, keyName, count, node->key, pwarg);
862 if (keyName != (char *)orphan)
863 PORT_Free((void *)keyName);
864 count++;
865 }
866 SECKEY_DestroyPrivateKeyList(list);
868 if (count == 0) {
869 PR_fprintf(PR_STDOUT, "%s: no keys found\n", progName);
870 return SECFailure;
871 }
872 return SECSuccess;
873 }
875 /* returns SECSuccess if ANY keys are found, SECFailure otherwise. */
876 static SECStatus
877 ListKeys(PK11SlotInfo *slot, const char *nickName, int index,
878 KeyType keyType, PRBool dopriv, secuPWData *pwdata)
879 {
880 SECStatus rv = SECFailure;
881 static const char fmt[] = \
882 "%s: Checking token \"%.33s\" in slot \"%.65s\"\n";
884 if (slot == NULL) {
885 PK11SlotList *list;
886 PK11SlotListElement *le;
888 list= PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_FALSE,pwdata);
889 if (list) {
890 for (le = list->head; le; le = le->next) {
891 PR_fprintf(PR_STDOUT, fmt, progName,
892 PK11_GetTokenName(le->slot),
893 PK11_GetSlotName(le->slot));
894 rv &= ListKeysInSlot(le->slot,nickName,keyType,pwdata);
895 }
896 PK11_FreeSlotList(list);
897 }
898 } else {
899 PR_fprintf(PR_STDOUT, fmt, progName, PK11_GetTokenName(slot),
900 PK11_GetSlotName(slot));
901 rv = ListKeysInSlot(slot,nickName,keyType,pwdata);
902 }
903 return rv;
904 }
906 static SECStatus
907 DeleteKey(char *nickname, secuPWData *pwdata)
908 {
909 SECStatus rv;
910 CERTCertificate *cert;
911 PK11SlotInfo *slot;
913 slot = PK11_GetInternalKeySlot();
914 if (PK11_NeedLogin(slot)) {
915 SECStatus rv = PK11_Authenticate(slot, PR_TRUE, pwdata);
916 if (rv != SECSuccess) {
917 SECU_PrintError(progName, "could not authenticate to token %s.",
918 PK11_GetTokenName(slot));
919 return SECFailure;
920 }
921 }
922 cert = PK11_FindCertFromNickname(nickname, pwdata);
923 if (!cert) {
924 PK11_FreeSlot(slot);
925 return SECFailure;
926 }
927 rv = PK11_DeleteTokenCertAndKey(cert, pwdata);
928 if (rv != SECSuccess) {
929 SECU_PrintError("problem deleting private key \"%s\"\n", nickname);
930 }
931 CERT_DestroyCertificate(cert);
932 PK11_FreeSlot(slot);
933 return rv;
934 }
937 /*
938 * L i s t M o d u l e s
939 *
940 * Print a list of the PKCS11 modules that are
941 * available. This is useful for smartcard people to
942 * make sure they have the drivers loaded.
943 *
944 */
945 static SECStatus
946 ListModules(void)
947 {
948 PK11SlotList *list;
949 PK11SlotListElement *le;
951 /* get them all! */
952 list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_FALSE,NULL);
953 if (list == NULL) return SECFailure;
955 /* look at each slot*/
956 for (le = list->head ; le; le = le->next) {
957 printf ("\n");
958 printf (" slot: %s\n", PK11_GetSlotName(le->slot));
959 printf (" token: %s\n", PK11_GetTokenName(le->slot));
960 }
961 PK11_FreeSlotList(list);
963 return SECSuccess;
964 }
966 static void
967 PrintSyntax(char *progName)
968 {
969 #define FPS fprintf(stderr,
970 FPS "Type %s -H for more detailed descriptions\n", progName);
971 FPS "Usage: %s -N [-d certdir] [-P dbprefix] [-f pwfile] [--empty-password]\n", progName);
972 FPS "Usage: %s -T [-d certdir] [-P dbprefix] [-h token-name]\n"
973 "\t\t [-f pwfile] [-0 SSO-password]\n", progName);
974 FPS "\t%s -A -n cert-name -t trustargs [-d certdir] [-P dbprefix] [-a] [-i input]\n",
975 progName);
976 FPS "\t%s -B -i batch-file\n", progName);
977 FPS "\t%s -C [-c issuer-name | -x] -i cert-request-file -o cert-file\n"
978 "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n"
979 "\t\t [-f pwfile] [-d certdir] [-P dbprefix]\n"
980 "\t\t [-1 | --keyUsage [keyUsageKeyword,..]] [-2] [-3] [-4]\n"
981 "\t\t [-5 | --nsCertType [nsCertTypeKeyword,...]]\n"
982 "\t\t [-6 | --extKeyUsage [extKeyUsageKeyword,...]] [-7 emailAddrs]\n"
983 "\t\t [-8 dns-names] [-a]\n",
984 progName);
985 FPS "\t%s -D -n cert-name [-d certdir] [-P dbprefix]\n", progName);
986 FPS "\t%s -E -n cert-name -t trustargs [-d certdir] [-P dbprefix] [-a] [-i input]\n",
987 progName);
988 FPS "\t%s -F -n nickname [-d certdir] [-P dbprefix]\n",
989 progName);
990 FPS "\t%s -G -n key-name [-h token-name] [-k rsa] [-g key-size] [-y exp]\n"
991 "\t\t [-f pwfile] [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
992 FPS "\t%s -G [-h token-name] -k dsa [-q pqgfile -g key-size] [-f pwfile]\n"
993 "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
994 #ifndef NSS_DISABLE_ECC
995 FPS "\t%s -G [-h token-name] -k ec -q curve [-f pwfile]\n"
996 "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
997 FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|ec|rsa|all]\n",
998 progName);
999 #else
1000 FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|rsa|all]\n",
1001 progName);
1002 #endif /* NSS_DISABLE_ECC */
1003 FPS "\t\t [-f pwfile] [-X] [-d certdir] [-P dbprefix]\n");
1004 FPS "\t%s --upgrade-merge --source-dir upgradeDir --upgrade-id uniqueID\n",
1005 progName);
1006 FPS "\t\t [--upgrade-token-name tokenName] [-d targetDBDir]\n");
1007 FPS "\t\t [-P targetDBPrefix] [--source-prefix upgradeDBPrefix]\n");
1008 FPS "\t\t [-f targetPWfile] [-@ upgradePWFile]\n");
1009 FPS "\t%s --merge --source-dir sourceDBDir [-d targetDBdir]\n",
1010 progName);
1011 FPS "\t\t [-P targetDBPrefix] [--source-prefix sourceDBPrefix]\n");
1012 FPS "\t\t [-f targetPWfile] [-@ sourcePWFile]\n");
1013 FPS "\t%s -L [-n cert-name] [--email email-address] [-X] [-r] [-a]\n",
1014 progName);
1015 FPS "\t\t [--dump-ext-val OID] [-d certdir] [-P dbprefix]\n");
1016 FPS "\t%s -M -n cert-name -t trustargs [-d certdir] [-P dbprefix]\n",
1017 progName);
1018 FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n", progName);
1019 FPS "\t%s -R -s subj -o cert-request-file [-d certdir] [-P dbprefix] [-p phone] [-a]\n"
1020 "\t\t [-7 emailAddrs] [-k key-type-or-id] [-h token-name] [-f pwfile] [-g key-size]\n",
1021 progName);
1022 FPS "\t%s -V -n cert-name -u usage [-b time] [-e] [-a]\n"
1023 "\t\t[-X] [-d certdir] [-P dbprefix]\n",
1024 progName);
1025 FPS "Usage: %s -W [-d certdir] [-f pwfile] [-@newpwfile]\n",
1026 progName);
1027 FPS "\t%s -S -n cert-name -s subj [-c issuer-name | -x] -t trustargs\n"
1028 "\t\t [-k key-type-or-id] [-q key-params] [-h token-name] [-g key-size]\n"
1029 "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n"
1030 "\t\t [-f pwfile] [-d certdir] [-P dbprefix]\n"
1031 "\t\t [-p phone] [-1] [-2] [-3] [-4] [-5] [-6] [-7 emailAddrs]\n"
1032 "\t\t [-8 DNS-names]\n"
1033 "\t\t [--extAIA] [--extSIA] [--extCP] [--extPM] [--extPC] [--extIA]\n"
1034 "\t\t [--extSKID] [--extNC] [--extSAN type:name[,type:name]...]\n"
1035 "\t\t [--extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...]\n", progName);
1036 FPS "\t%s -U [-X] [-d certdir] [-P dbprefix]\n", progName);
1037 exit(1);
1038 }
1040 enum usage_level {
1041 usage_all = 0, usage_selected = 1
1042 };
1044 static void luCommonDetailsAE();
1046 static void luA(enum usage_level ul, const char *command)
1047 {
1048 int is_my_command = (command && 0 == strcmp(command, "A"));
1049 if (ul == usage_all || !command || is_my_command)
1050 FPS "%-15s Add a certificate to the database (create if needed)\n",
1051 "-A");
1052 if (ul == usage_selected && !is_my_command)
1053 return;
1054 if (ul == usage_all) {
1055 FPS "%-20s\n", " All options under -E apply");
1056 }
1057 else {
1058 luCommonDetailsAE();
1059 }
1060 }
1062 static void luB(enum usage_level ul, const char *command)
1063 {
1064 int is_my_command = (command && 0 == strcmp(command, "B"));
1065 if (ul == usage_all || !command || is_my_command)
1066 FPS "%-15s Run a series of certutil commands from a batch file\n", "-B");
1067 if (ul == usage_selected && !is_my_command)
1068 return;
1069 FPS "%-20s Specify the batch file\n", " -i batch-file");
1070 }
1072 static void luE(enum usage_level ul, const char *command)
1073 {
1074 int is_my_command = (command && 0 == strcmp(command, "E"));
1075 if (ul == usage_all || !command || is_my_command)
1076 FPS "%-15s Add an Email certificate to the database (create if needed)\n",
1077 "-E");
1078 if (ul == usage_selected && !is_my_command)
1079 return;
1080 luCommonDetailsAE();
1081 }
1083 static void luCommonDetailsAE()
1084 {
1085 FPS "%-20s Specify the nickname of the certificate to add\n",
1086 " -n cert-name");
1087 FPS "%-20s Set the certificate trust attributes:\n",
1088 " -t trustargs");
1089 FPS "%-25s trustargs is of the form x,y,z where x is for SSL, y is for S/MIME,\n", "");
1090 FPS "%-25s and z is for code signing. Use ,, for no explicit trust.\n", "");
1091 FPS "%-25s p \t prohibited (explicitly distrusted)\n", "");
1092 FPS "%-25s P \t trusted peer\n", "");
1093 FPS "%-25s c \t valid CA\n", "");
1094 FPS "%-25s T \t trusted CA to issue client certs (implies c)\n", "");
1095 FPS "%-25s C \t trusted CA to issue server certs (implies c)\n", "");
1096 FPS "%-25s u \t user cert\n", "");
1097 FPS "%-25s w \t send warning\n", "");
1098 FPS "%-25s g \t make step-up cert\n", "");
1099 FPS "%-20s Specify the password file\n",
1100 " -f pwfile");
1101 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1102 " -d certdir");
1103 FPS "%-20s Cert & Key database prefix\n",
1104 " -P dbprefix");
1105 FPS "%-20s The input certificate is encoded in ASCII (RFC1113)\n",
1106 " -a");
1107 FPS "%-20s Specify the certificate file (default is stdin)\n",
1108 " -i input");
1109 FPS "\n");
1110 }
1112 static void luC(enum usage_level ul, const char *command)
1113 {
1114 int is_my_command = (command && 0 == strcmp(command, "C"));
1115 if (ul == usage_all || !command || is_my_command)
1116 FPS "%-15s Create a new binary certificate from a BINARY cert request\n",
1117 "-C");
1118 if (ul == usage_selected && !is_my_command)
1119 return;
1120 FPS "%-20s The nickname of the issuer cert\n",
1121 " -c issuer-name");
1122 FPS "%-20s The BINARY certificate request file\n",
1123 " -i cert-request ");
1124 FPS "%-20s Output binary cert to this file (default is stdout)\n",
1125 " -o output-cert");
1126 FPS "%-20s Self sign\n",
1127 " -x");
1128 FPS "%-20s Cert serial number\n",
1129 " -m serial-number");
1130 FPS "%-20s Time Warp\n",
1131 " -w warp-months");
1132 FPS "%-20s Months valid (default is 3)\n",
1133 " -v months-valid");
1134 FPS "%-20s Specify the password file\n",
1135 " -f pwfile");
1136 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1137 " -d certdir");
1138 FPS "%-20s Cert & Key database prefix\n",
1139 " -P dbprefix");
1140 FPS "%-20s \n"
1141 "%-20s Create key usage extension. Possible keywords:\n"
1142 "%-20s \"digitalSignature\", \"nonRepudiation\", \"keyEncipherment\",\n"
1143 "%-20s \"dataEncipherment\", \"keyAgreement\", \"certSigning\",\n"
1144 "%-20s \"crlSigning\", \"critical\"\n",
1145 " -1 | --keyUsage keyword,keyword,...", "", "", "", "");
1146 FPS "%-20s Create basic constraint extension\n",
1147 " -2 ");
1148 FPS "%-20s Create authority key ID extension\n",
1149 " -3 ");
1150 FPS "%-20s Create crl distribution point extension\n",
1151 " -4 ");
1152 FPS "%-20s \n"
1153 "%-20s Create netscape cert type extension. Possible keywords:\n"
1154 "%-20s \"sslClient\", \"sslServer\", \"smime\", \"objectSigning\",\n"
1155 "%-20s \"sslCA\", \"smimeCA\", \"objectSigningCA\", \"critical\".\n",
1156 " -5 | --nsCertType keyword,keyword,... ", "", "", "");
1157 FPS "%-20s \n"
1158 "%-20s Create extended key usage extension. Possible keywords:\n"
1159 "%-20s \"serverAuth\", \"clientAuth\",\"codeSigning\",\n"
1160 "%-20s \"emailProtection\", \"timeStamp\",\"ocspResponder\",\n"
1161 "%-20s \"stepUp\", \"msTrustListSign\", \"critical\"\n",
1162 " -6 | --extKeyUsage keyword,keyword,...", "", "", "", "");
1163 FPS "%-20s Create an email subject alt name extension\n",
1164 " -7 emailAddrs");
1165 FPS "%-20s Create an dns subject alt name extension\n",
1166 " -8 dnsNames");
1167 FPS "%-20s The input certificate request is encoded in ASCII (RFC1113)\n",
1168 " -a");
1169 FPS "\n");
1170 }
1172 static void luG(enum usage_level ul, const char *command)
1173 {
1174 int is_my_command = (command && 0 == strcmp(command, "G"));
1175 if (ul == usage_all || !command || is_my_command)
1176 FPS "%-15s Generate a new key pair\n",
1177 "-G");
1178 if (ul == usage_selected && !is_my_command)
1179 return;
1180 FPS "%-20s Name of token in which to generate key (default is internal)\n",
1181 " -h token-name");
1182 #ifndef NSS_DISABLE_ECC
1183 FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
1184 " -k key-type");
1185 FPS "%-20s Key size in bits, (min %d, max %d, default %d) (not for ec)\n",
1186 " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
1187 #else
1188 FPS "%-20s Type of key pair to generate (\"dsa\", \"rsa\" (default))\n",
1189 " -k key-type");
1190 FPS "%-20s Key size in bits, (min %d, max %d, default %d)\n",
1191 " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
1192 #endif /* NSS_DISABLE_ECC */
1193 FPS "%-20s Set the public exponent value (3, 17, 65537) (rsa only)\n",
1194 " -y exp");
1195 FPS "%-20s Specify the password file\n",
1196 " -f password-file");
1197 FPS "%-20s Specify the noise file to be used\n",
1198 " -z noisefile");
1199 FPS "%-20s read PQG value from pqgfile (dsa only)\n",
1200 " -q pqgfile");
1201 #ifndef NSS_DISABLE_ECC
1202 FPS "%-20s Elliptic curve name (ec only)\n",
1203 " -q curve-name");
1204 FPS "%-20s One of nistp256, nistp384, nistp521\n", "");
1205 #ifdef NSS_ECC_MORE_THAN_SUITE_B
1206 FPS "%-20s sect163k1, nistk163, sect163r1, sect163r2,\n", "");
1207 FPS "%-20s nistb163, sect193r1, sect193r2, sect233k1, nistk233,\n", "");
1208 FPS "%-20s sect233r1, nistb233, sect239k1, sect283k1, nistk283,\n", "");
1209 FPS "%-20s sect283r1, nistb283, sect409k1, nistk409, sect409r1,\n", "");
1210 FPS "%-20s nistb409, sect571k1, nistk571, sect571r1, nistb571,\n", "");
1211 FPS "%-20s secp160k1, secp160r1, secp160r2, secp192k1, secp192r1,\n", "");
1212 FPS "%-20s nistp192, secp224k1, secp224r1, nistp224, secp256k1,\n", "");
1213 FPS "%-20s secp256r1, secp384r1, secp521r1,\n", "");
1214 FPS "%-20s prime192v1, prime192v2, prime192v3, \n", "");
1215 FPS "%-20s prime239v1, prime239v2, prime239v3, c2pnb163v1, \n", "");
1216 FPS "%-20s c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1, \n", "");
1217 FPS "%-20s c2tnb191v2, c2tnb191v3, \n", "");
1218 FPS "%-20s c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3, \n", "");
1219 FPS "%-20s c2pnb272w1, c2pnb304w1, \n", "");
1220 FPS "%-20s c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, \n", "");
1221 FPS "%-20s secp112r2, secp128r1, secp128r2, sect113r1, sect113r2\n", "");
1222 FPS "%-20s sect131r1, sect131r2\n", "");
1223 #endif /* NSS_ECC_MORE_THAN_SUITE_B */
1224 #endif
1225 FPS "%-20s Key database directory (default is ~/.netscape)\n",
1226 " -d keydir");
1227 FPS "%-20s Cert & Key database prefix\n",
1228 " -P dbprefix");
1229 FPS "%-20s\n"
1230 "%-20s PKCS #11 key Attributes.\n",
1231 " --keyAttrFlags attrflags", "");
1232 FPS "%-20s Comma separated list of key attribute attribute flags,\n", "");
1233 FPS "%-20s selected from the following list of choices:\n", "");
1234 FPS "%-20s {token | session} {public | private} {sensitive | insensitive}\n", "");
1235 FPS "%-20s {modifiable | unmodifiable} {extractable | unextractable}\n", "");
1236 FPS "%-20s\n",
1237 " --keyOpFlagsOn opflags");
1238 FPS "%-20s\n"
1239 "%-20s PKCS #11 key Operation Flags.\n",
1240 " --keyOpFlagsOff opflags", "");
1241 FPS "%-20s Comma separated list of one or more of the following:\n", "");
1242 FPS "%-20s encrypt, decrypt, sign, sign_recover, verify,\n", "");
1243 FPS "%-20s verify_recover, wrap, unwrap, derive\n", "");
1244 FPS "\n");
1245 }
1247 static void luD(enum usage_level ul, const char *command)
1248 {
1249 int is_my_command = (command && 0 == strcmp(command, "D"));
1250 if (ul == usage_all || !command || is_my_command)
1251 FPS "%-15s Delete a certificate from the database\n",
1252 "-D");
1253 if (ul == usage_selected && !is_my_command)
1254 return;
1255 FPS "%-20s The nickname of the cert to delete\n",
1256 " -n cert-name");
1257 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1258 " -d certdir");
1259 FPS "%-20s Cert & Key database prefix\n",
1260 " -P dbprefix");
1261 FPS "\n");
1263 }
1265 static void luF(enum usage_level ul, const char *command)
1266 {
1267 int is_my_command = (command && 0 == strcmp(command, "F"));
1268 if (ul == usage_all || !command || is_my_command)
1269 FPS "%-15s Delete a key from the database\n",
1270 "-F");
1271 if (ul == usage_selected && !is_my_command)
1272 return;
1273 FPS "%-20s The nickname of the key to delete\n",
1274 " -n cert-name");
1275 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1276 " -d certdir");
1277 FPS "%-20s Cert & Key database prefix\n",
1278 " -P dbprefix");
1279 FPS "\n");
1281 }
1283 static void luU(enum usage_level ul, const char *command)
1284 {
1285 int is_my_command = (command && 0 == strcmp(command, "U"));
1286 if (ul == usage_all || !command || is_my_command)
1287 FPS "%-15s List all modules\n", /*, or print out a single named module\n",*/
1288 "-U");
1289 if (ul == usage_selected && !is_my_command)
1290 return;
1291 FPS "%-20s Module database directory (default is '~/.netscape')\n",
1292 " -d moddir");
1293 FPS "%-20s Cert & Key database prefix\n",
1294 " -P dbprefix");
1295 FPS "%-20s force the database to open R/W\n",
1296 " -X");
1297 FPS "\n");
1299 }
1301 static void luK(enum usage_level ul, const char *command)
1302 {
1303 int is_my_command = (command && 0 == strcmp(command, "K"));
1304 if (ul == usage_all || !command || is_my_command)
1305 FPS "%-15s List all private keys\n",
1306 "-K");
1307 if (ul == usage_selected && !is_my_command)
1308 return;
1309 FPS "%-20s Name of token to search (\"all\" for all tokens)\n",
1310 " -h token-name ");
1312 FPS "%-20s Key type (\"all\" (default), \"dsa\","
1313 #ifndef NSS_DISABLE_ECC
1314 " \"ec\","
1315 #endif
1316 " \"rsa\")\n",
1317 " -k key-type");
1318 FPS "%-20s The nickname of the key or associated certificate\n",
1319 " -n name");
1320 FPS "%-20s Specify the password file\n",
1321 " -f password-file");
1322 FPS "%-20s Key database directory (default is ~/.netscape)\n",
1323 " -d keydir");
1324 FPS "%-20s Cert & Key database prefix\n",
1325 " -P dbprefix");
1326 FPS "%-20s force the database to open R/W\n",
1327 " -X");
1328 FPS "\n");
1329 }
1331 static void luL(enum usage_level ul, const char *command)
1332 {
1333 int is_my_command = (command && 0 == strcmp(command, "L"));
1334 if (ul == usage_all || !command || is_my_command)
1335 FPS "%-15s List all certs, or print out a single named cert (or a subset)\n",
1336 "-L");
1337 if (ul == usage_selected && !is_my_command)
1338 return;
1339 FPS "%-20s Pretty print named cert (list all if unspecified)\n",
1340 " -n cert-name");
1341 FPS "%-20s \n"
1342 "%-20s Pretty print cert with email address (list all if unspecified)\n",
1343 " --email email-address", "");
1344 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1345 " -d certdir");
1346 FPS "%-20s Cert & Key database prefix\n",
1347 " -P dbprefix");
1348 FPS "%-20s force the database to open R/W\n",
1349 " -X");
1350 FPS "%-20s For single cert, print binary DER encoding\n",
1351 " -r");
1352 FPS "%-20s For single cert, print ASCII encoding (RFC1113)\n",
1353 " -a");
1354 FPS "%-20s \n"
1355 "%-20s For single cert, print binary DER encoding of extension OID\n",
1356 " --dump-ext-val OID", "");
1357 FPS "\n");
1358 }
1360 static void luM(enum usage_level ul, const char *command)
1361 {
1362 int is_my_command = (command && 0 == strcmp(command, "M"));
1363 if (ul == usage_all || !command || is_my_command)
1364 FPS "%-15s Modify trust attributes of certificate\n",
1365 "-M");
1366 if (ul == usage_selected && !is_my_command)
1367 return;
1368 FPS "%-20s The nickname of the cert to modify\n",
1369 " -n cert-name");
1370 FPS "%-20s Set the certificate trust attributes (see -A above)\n",
1371 " -t trustargs");
1372 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1373 " -d certdir");
1374 FPS "%-20s Cert & Key database prefix\n",
1375 " -P dbprefix");
1376 FPS "\n");
1377 }
1379 static void luN(enum usage_level ul, const char *command)
1380 {
1381 int is_my_command = (command && 0 == strcmp(command, "N"));
1382 if (ul == usage_all || !command || is_my_command)
1383 FPS "%-15s Create a new certificate database\n",
1384 "-N");
1385 if (ul == usage_selected && !is_my_command)
1386 return;
1387 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1388 " -d certdir");
1389 FPS "%-20s Cert & Key database prefix\n",
1390 " -P dbprefix");
1391 FPS "%-20s use empty password when creating a new database\n",
1392 " --empty-password");
1393 FPS "\n");
1394 }
1396 static void luT(enum usage_level ul, const char *command)
1397 {
1398 int is_my_command = (command && 0 == strcmp(command, "T"));
1399 if (ul == usage_all || !command || is_my_command)
1400 FPS "%-15s Reset the Key database or token\n",
1401 "-T");
1402 if (ul == usage_selected && !is_my_command)
1403 return;
1404 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1405 " -d certdir");
1406 FPS "%-20s Cert & Key database prefix\n",
1407 " -P dbprefix");
1408 FPS "%-20s Token to reset (default is internal)\n",
1409 " -h token-name");
1410 FPS "%-20s Set token's Site Security Officer password\n",
1411 " -0 SSO-password");
1412 FPS "\n");
1413 }
1415 static void luO(enum usage_level ul, const char *command)
1416 {
1417 int is_my_command = (command && 0 == strcmp(command, "O"));
1418 if (ul == usage_all || !command || is_my_command)
1419 FPS "%-15s Print the chain of a certificate\n",
1420 "-O");
1421 if (ul == usage_selected && !is_my_command)
1422 return;
1423 FPS "%-20s The nickname of the cert to modify\n",
1424 " -n cert-name");
1425 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1426 " -d certdir");
1427 FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n",
1428 " -a");
1429 FPS "%-20s Cert & Key database prefix\n",
1430 " -P dbprefix");
1431 FPS "%-20s force the database to open R/W\n",
1432 " -X");
1433 FPS "\n");
1434 }
1436 static void luR(enum usage_level ul, const char *command)
1437 {
1438 int is_my_command = (command && 0 == strcmp(command, "R"));
1439 if (ul == usage_all || !command || is_my_command)
1440 FPS "%-15s Generate a certificate request (stdout)\n",
1441 "-R");
1442 if (ul == usage_selected && !is_my_command)
1443 return;
1444 FPS "%-20s Specify the subject name (using RFC1485)\n",
1445 " -s subject");
1446 FPS "%-20s Output the cert request to this file\n",
1447 " -o output-req");
1448 #ifndef NSS_DISABLE_ECC
1449 FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
1450 #else
1451 FPS "%-20s Type of key pair to generate (\"dsa\", \"rsa\" (default))\n",
1452 #endif /* NSS_DISABLE_ECC */
1453 " -k key-type-or-id");
1454 FPS "%-20s or nickname of the cert key to use \n",
1455 "");
1456 FPS "%-20s Name of token in which to generate key (default is internal)\n",
1457 " -h token-name");
1458 FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n",
1459 " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
1460 FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
1461 " -q pqgfile");
1462 #ifndef NSS_DISABLE_ECC
1463 FPS "%-20s Elliptic curve name (ec only)\n",
1464 " -q curve-name");
1465 FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
1466 "");
1467 #endif /* NSS_DISABLE_ECC */
1468 FPS "%-20s Specify the password file\n",
1469 " -f pwfile");
1470 FPS "%-20s Key database directory (default is ~/.netscape)\n",
1471 " -d keydir");
1472 FPS "%-20s Cert & Key database prefix\n",
1473 " -P dbprefix");
1474 FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n",
1475 " -p phone");
1476 FPS "%-20s Output the cert request in ASCII (RFC1113); default is binary\n",
1477 " -a");
1478 FPS "%-20s \n",
1479 " See -S for available extension options");
1480 FPS "%-20s \n",
1481 " See -G for available key flag options");
1482 FPS "\n");
1483 }
1485 static void luV(enum usage_level ul, const char *command)
1486 {
1487 int is_my_command = (command && 0 == strcmp(command, "V"));
1488 if (ul == usage_all || !command || is_my_command)
1489 FPS "%-15s Validate a certificate\n",
1490 "-V");
1491 if (ul == usage_selected && !is_my_command)
1492 return;
1493 FPS "%-20s The nickname of the cert to Validate\n",
1494 " -n cert-name");
1495 FPS "%-20s validity time (\"YYMMDDHHMMSS[+HHMM|-HHMM|Z]\")\n",
1496 " -b time");
1497 FPS "%-20s Check certificate signature \n",
1498 " -e ");
1499 FPS "%-20s Specify certificate usage:\n", " -u certusage");
1500 FPS "%-25s C \t SSL Client\n", "");
1501 FPS "%-25s V \t SSL Server\n", "");
1502 FPS "%-25s L \t SSL CA\n", "");
1503 FPS "%-25s A \t Any CA\n", "");
1504 FPS "%-25s Y \t Verify CA\n", "");
1505 FPS "%-25s S \t Email signer\n", "");
1506 FPS "%-25s R \t Email Recipient\n", "");
1507 FPS "%-25s O \t OCSP status responder\n", "");
1508 FPS "%-25s J \t Object signer\n", "");
1509 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1510 " -d certdir");
1511 FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n",
1512 " -a");
1513 FPS "%-20s Cert & Key database prefix\n",
1514 " -P dbprefix");
1515 FPS "%-20s force the database to open R/W\n",
1516 " -X");
1517 FPS "\n");
1518 }
1520 static void luW(enum usage_level ul, const char *command)
1521 {
1522 int is_my_command = (command && 0 == strcmp(command, "W"));
1523 if (ul == usage_all || !command || is_my_command)
1524 FPS "%-15s Change the key database password\n",
1525 "-W");
1526 if (ul == usage_selected && !is_my_command)
1527 return;
1528 FPS "%-20s cert and key database directory\n",
1529 " -d certdir");
1530 FPS "%-20s Specify a file with the current password\n",
1531 " -f pwfile");
1532 FPS "%-20s Specify a file with the new password in two lines\n",
1533 " -@ newpwfile");
1534 FPS "\n");
1535 }
1537 static void luUpgradeMerge(enum usage_level ul, const char *command)
1538 {
1539 int is_my_command = (command && 0 == strcmp(command, "upgrade-merge"));
1540 if (ul == usage_all || !command || is_my_command)
1541 FPS "%-15s Upgrade an old database and merge it into a new one\n",
1542 "--upgrade-merge");
1543 if (ul == usage_selected && !is_my_command)
1544 return;
1545 FPS "%-20s Cert database directory to merge into (default is ~/.netscape)\n",
1546 " -d certdir");
1547 FPS "%-20s Cert & Key database prefix of the target database\n",
1548 " -P dbprefix");
1549 FPS "%-20s Specify the password file for the target database\n",
1550 " -f pwfile");
1551 FPS "%-20s \n%-20s Cert database directory to upgrade from\n",
1552 " --source-dir certdir", "");
1553 FPS "%-20s \n%-20s Cert & Key database prefix of the upgrade database\n",
1554 " --source-prefix dbprefix", "");
1555 FPS "%-20s \n%-20s Unique identifier for the upgrade database\n",
1556 " --upgrade-id uniqueID", "");
1557 FPS "%-20s \n%-20s Name of the token while it is in upgrade state\n",
1558 " --upgrade-token-name name", "");
1559 FPS "%-20s Specify the password file for the upgrade database\n",
1560 " -@ pwfile");
1561 FPS "\n");
1562 }
1564 static void luMerge(enum usage_level ul, const char *command)
1565 {
1566 int is_my_command = (command && 0 == strcmp(command, "merge"));
1567 if (ul == usage_all || !command || is_my_command)
1568 FPS "%-15s Merge source database into the target database\n",
1569 "--merge");
1570 if (ul == usage_selected && !is_my_command)
1571 return;
1572 FPS "%-20s Cert database directory of target (default is ~/.netscape)\n",
1573 " -d certdir");
1574 FPS "%-20s Cert & Key database prefix of the target database\n",
1575 " -P dbprefix");
1576 FPS "%-20s Specify the password file for the target database\n",
1577 " -f pwfile");
1578 FPS "%-20s \n%-20s Cert database directory of the source database\n",
1579 " --source-dir certdir", "");
1580 FPS "%-20s \n%-20s Cert & Key database prefix of the source database\n",
1581 " --source-prefix dbprefix", "");
1582 FPS "%-20s Specify the password file for the source database\n",
1583 " -@ pwfile");
1584 FPS "\n");
1585 }
1587 static void luS(enum usage_level ul, const char *command)
1588 {
1589 int is_my_command = (command && 0 == strcmp(command, "S"));
1590 if (ul == usage_all || !command || is_my_command)
1591 FPS "%-15s Make a certificate and add to database\n",
1592 "-S");
1593 if (ul == usage_selected && !is_my_command)
1594 return;
1595 FPS "%-20s Specify the nickname of the cert\n",
1596 " -n key-name");
1597 FPS "%-20s Specify the subject name (using RFC1485)\n",
1598 " -s subject");
1599 FPS "%-20s The nickname of the issuer cert\n",
1600 " -c issuer-name");
1601 FPS "%-20s Set the certificate trust attributes (see -A above)\n",
1602 " -t trustargs");
1603 #ifndef NSS_DISABLE_ECC
1604 FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n",
1605 #else
1606 FPS "%-20s Type of key pair to generate (\"dsa\", \"rsa\" (default))\n",
1607 #endif /* NSS_DISABLE_ECC */
1608 " -k key-type-or-id");
1609 FPS "%-20s Name of token in which to generate key (default is internal)\n",
1610 " -h token-name");
1611 FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n",
1612 " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS);
1613 FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
1614 " -q pqgfile");
1615 #ifndef NSS_DISABLE_ECC
1616 FPS "%-20s Elliptic curve name (ec only)\n",
1617 " -q curve-name");
1618 FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
1619 "");
1620 #endif /* NSS_DISABLE_ECC */
1621 FPS "%-20s Self sign\n",
1622 " -x");
1623 FPS "%-20s Cert serial number\n",
1624 " -m serial-number");
1625 FPS "%-20s Time Warp\n",
1626 " -w warp-months");
1627 FPS "%-20s Months valid (default is 3)\n",
1628 " -v months-valid");
1629 FPS "%-20s Specify the password file\n",
1630 " -f pwfile");
1631 FPS "%-20s Cert database directory (default is ~/.netscape)\n",
1632 " -d certdir");
1633 FPS "%-20s Cert & Key database prefix\n",
1634 " -P dbprefix");
1635 FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n",
1636 " -p phone");
1637 FPS "%-20s Create key usage extension\n",
1638 " -1 ");
1639 FPS "%-20s Create basic constraint extension\n",
1640 " -2 ");
1641 FPS "%-20s Create authority key ID extension\n",
1642 " -3 ");
1643 FPS "%-20s Create crl distribution point extension\n",
1644 " -4 ");
1645 FPS "%-20s Create netscape cert type extension\n",
1646 " -5 ");
1647 FPS "%-20s Create extended key usage extension\n",
1648 " -6 ");
1649 FPS "%-20s Create an email subject alt name extension\n",
1650 " -7 emailAddrs ");
1651 FPS "%-20s Create a DNS subject alt name extension\n",
1652 " -8 DNS-names");
1653 FPS "%-20s Create an Authority Information Access extension\n",
1654 " --extAIA ");
1655 FPS "%-20s Create a Subject Information Access extension\n",
1656 " --extSIA ");
1657 FPS "%-20s Create a Certificate Policies extension\n",
1658 " --extCP ");
1659 FPS "%-20s Create a Policy Mappings extension\n",
1660 " --extPM ");
1661 FPS "%-20s Create a Policy Constraints extension\n",
1662 " --extPC ");
1663 FPS "%-20s Create an Inhibit Any Policy extension\n",
1664 " --extIA ");
1665 FPS "%-20s Create a subject key ID extension\n",
1666 " --extSKID ");
1667 FPS "%-20s \n",
1668 " See -G for available key flag options");
1669 FPS "%-20s Create a name constraints extension\n",
1670 " --extNC ");
1671 FPS "%-20s \n"
1672 "%-20s Create a Subject Alt Name extension with one or multiple names\n",
1673 " --extSAN type:name[,type:name]...", "");
1674 FPS "%-20s - type: directory, dn, dns, edi, ediparty, email, ip, ipaddr,\n", "");
1675 FPS "%-20s other, registerid, rfc822, uri, x400, x400addr\n", "");
1676 FPS "%-20s \n"
1677 "%-20s Add one or multiple extensions that certutil cannot encode yet,\n"
1678 "%-20s by loading their encodings from external files.\n",
1679 " --extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...", "", "");
1680 FPS "%-20s - OID (example): 1.2.3.4\n", "");
1681 FPS "%-20s - critical-flag: critical or not-critical\n", "");
1682 FPS "%-20s - filename: full path to a file containing an encoded extension\n", "");
1683 FPS "\n");
1684 }
1686 static void LongUsage(char *progName, enum usage_level ul, const char *command)
1687 {
1688 luA(ul, command);
1689 luB(ul, command);
1690 luE(ul, command);
1691 luC(ul, command);
1692 luG(ul, command);
1693 luD(ul, command);
1694 luF(ul, command);
1695 luU(ul, command);
1696 luK(ul, command);
1697 luL(ul, command);
1698 luM(ul, command);
1699 luN(ul, command);
1700 luT(ul, command);
1701 luO(ul, command);
1702 luR(ul, command);
1703 luV(ul, command);
1704 luW(ul, command);
1705 luUpgradeMerge(ul, command);
1706 luMerge(ul, command);
1707 luS(ul, command);
1708 #undef FPS
1709 }
1711 static void
1712 Usage(char *progName)
1713 {
1714 PR_fprintf(PR_STDERR,
1715 "%s - Utility to manipulate NSS certificate databases\n\n"
1716 "Usage: %s <command> -d <database-directory> <options>\n\n"
1717 "Valid commands:\n", progName, progName);
1718 LongUsage(progName, usage_selected, NULL);
1719 PR_fprintf(PR_STDERR, "\n"
1720 "%s -H <command> : Print available options for the given command\n"
1721 "%s -H : Print complete help output of all commands and options\n"
1722 "%s --syntax : Print a short summary of all commands and options\n",
1723 progName, progName, progName);
1724 exit(1);
1725 }
1727 static CERTCertificate *
1728 MakeV1Cert( CERTCertDBHandle * handle,
1729 CERTCertificateRequest *req,
1730 char * issuerNickName,
1731 PRBool selfsign,
1732 unsigned int serialNumber,
1733 int warpmonths,
1734 int validityMonths)
1735 {
1736 CERTCertificate *issuerCert = NULL;
1737 CERTValidity *validity;
1738 CERTCertificate *cert = NULL;
1739 PRExplodedTime printableTime;
1740 PRTime now, after;
1742 if ( !selfsign ) {
1743 issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName);
1744 if (!issuerCert) {
1745 SECU_PrintError(progName, "could not find certificate named \"%s\"",
1746 issuerNickName);
1747 return NULL;
1748 }
1749 }
1751 now = PR_Now();
1752 PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
1753 if ( warpmonths ) {
1754 printableTime.tm_month += warpmonths;
1755 now = PR_ImplodeTime (&printableTime);
1756 PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
1757 }
1758 printableTime.tm_month += validityMonths;
1759 after = PR_ImplodeTime (&printableTime);
1761 /* note that the time is now in micro-second unit */
1762 validity = CERT_CreateValidity (now, after);
1763 if (validity) {
1764 cert = CERT_CreateCertificate(serialNumber,
1765 (selfsign ? &req->subject
1766 : &issuerCert->subject),
1767 validity, req);
1769 CERT_DestroyValidity(validity);
1770 }
1771 if ( issuerCert ) {
1772 CERT_DestroyCertificate (issuerCert);
1773 }
1775 return(cert);
1776 }
1778 static SECStatus
1779 SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign,
1780 SECOidTag hashAlgTag,
1781 SECKEYPrivateKey *privKey, char *issuerNickName,
1782 int certVersion, void *pwarg)
1783 {
1784 SECItem der;
1785 SECKEYPrivateKey *caPrivateKey = NULL;
1786 SECStatus rv;
1787 PLArenaPool *arena;
1788 SECOidTag algID;
1789 void *dummy;
1791 if( !selfsign ) {
1792 CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg);
1793 if( (CERTCertificate *)NULL == issuer ) {
1794 SECU_PrintError(progName, "unable to find issuer with nickname %s",
1795 issuerNickName);
1796 return SECFailure;
1797 }
1799 privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg);
1800 CERT_DestroyCertificate(issuer);
1801 if (caPrivateKey == NULL) {
1802 SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName);
1803 return SECFailure;
1804 }
1805 }
1807 arena = cert->arena;
1809 algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag);
1810 if (algID == SEC_OID_UNKNOWN) {
1811 fprintf(stderr, "Unknown key or hash type for issuer.");
1812 rv = SECFailure;
1813 goto done;
1814 }
1816 rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0);
1817 if (rv != SECSuccess) {
1818 fprintf(stderr, "Could not set signature algorithm id.");
1819 goto done;
1820 }
1822 switch(certVersion) {
1823 case (SEC_CERTIFICATE_VERSION_1):
1824 /* The initial version for x509 certificates is version one
1825 * and this default value must be an implicit DER encoding. */
1826 cert->version.data = NULL;
1827 cert->version.len = 0;
1828 break;
1829 case (SEC_CERTIFICATE_VERSION_2):
1830 case (SEC_CERTIFICATE_VERSION_3):
1831 case 3: /* unspecified format (would be version 4 certificate). */
1832 *(cert->version.data) = certVersion;
1833 cert->version.len = 1;
1834 break;
1835 default:
1836 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1837 return SECFailure;
1838 }
1840 der.len = 0;
1841 der.data = NULL;
1842 dummy = SEC_ASN1EncodeItem (arena, &der, cert,
1843 SEC_ASN1_GET(CERT_CertificateTemplate));
1844 if (!dummy) {
1845 fprintf (stderr, "Could not encode certificate.\n");
1846 rv = SECFailure;
1847 goto done;
1848 }
1850 rv = SEC_DerSignData(arena, &cert->derCert, der.data, der.len, privKey, algID);
1851 if (rv != SECSuccess) {
1852 fprintf (stderr, "Could not sign encoded certificate data.\n");
1853 /* result allocated out of the arena, it will be freed
1854 * when the arena is freed */
1855 goto done;
1856 }
1857 done:
1858 if (caPrivateKey) {
1859 SECKEY_DestroyPrivateKey(caPrivateKey);
1860 }
1861 return rv;
1862 }
1864 static SECStatus
1865 CreateCert(
1866 CERTCertDBHandle *handle,
1867 PK11SlotInfo *slot,
1868 char * issuerNickName,
1869 const SECItem * certReqDER,
1870 SECKEYPrivateKey **selfsignprivkey,
1871 void *pwarg,
1872 SECOidTag hashAlgTag,
1873 unsigned int serialNumber,
1874 int warpmonths,
1875 int validityMonths,
1876 const char *emailAddrs,
1877 const char *dnsNames,
1878 PRBool ascii,
1879 PRBool selfsign,
1880 certutilExtnList extnList,
1881 const char *extGeneric,
1882 int certVersion,
1883 SECItem * certDER)
1884 {
1885 void * extHandle;
1886 CERTCertificate *subjectCert = NULL;
1887 CERTCertificateRequest *certReq = NULL;
1888 SECStatus rv = SECSuccess;
1889 CERTCertExtension **CRexts;
1891 do {
1892 /* Create a certrequest object from the input cert request der */
1893 certReq = GetCertRequest(certReqDER);
1894 if (certReq == NULL) {
1895 GEN_BREAK (SECFailure)
1896 }
1898 subjectCert = MakeV1Cert (handle, certReq, issuerNickName, selfsign,
1899 serialNumber, warpmonths, validityMonths);
1900 if (subjectCert == NULL) {
1901 GEN_BREAK (SECFailure)
1902 }
1905 extHandle = CERT_StartCertExtensions (subjectCert);
1906 if (extHandle == NULL) {
1907 GEN_BREAK (SECFailure)
1908 }
1910 rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric);
1911 if (rv != SECSuccess) {
1912 GEN_BREAK (SECFailure)
1913 }
1915 if (certReq->attributes != NULL &&
1916 certReq->attributes[0] != NULL &&
1917 certReq->attributes[0]->attrType.data != NULL &&
1918 certReq->attributes[0]->attrType.len > 0 &&
1919 SECOID_FindOIDTag(&certReq->attributes[0]->attrType)
1920 == SEC_OID_PKCS9_EXTENSION_REQUEST) {
1921 rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts);
1922 if (rv != SECSuccess)
1923 break;
1924 rv = CERT_MergeExtensions(extHandle, CRexts);
1925 if (rv != SECSuccess)
1926 break;
1927 }
1929 CERT_FinishExtensions(extHandle);
1931 /* self-signing a cert request, find the private key */
1932 if (selfsign && *selfsignprivkey == NULL) {
1933 *selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg);
1934 if (!*selfsignprivkey) {
1935 fprintf(stderr, "Failed to locate private key.\n");
1936 rv = SECFailure;
1937 break;
1938 }
1939 }
1941 rv = SignCert(handle, subjectCert, selfsign, hashAlgTag,
1942 *selfsignprivkey, issuerNickName,
1943 certVersion, pwarg);
1944 if (rv != SECSuccess)
1945 break;
1947 rv = SECFailure;
1948 if (ascii) {
1949 char * asciiDER = BTOA_DataToAscii(subjectCert->derCert.data,
1950 subjectCert->derCert.len);
1951 if (asciiDER) {
1952 char * wrapped = PR_smprintf("%s\n%s\n%s\n",
1953 NS_CERT_HEADER,
1954 asciiDER,
1955 NS_CERT_TRAILER);
1956 if (wrapped) {
1957 PRUint32 wrappedLen = PL_strlen(wrapped);
1958 if (SECITEM_AllocItem(NULL, certDER, wrappedLen)) {
1959 PORT_Memcpy(certDER->data, wrapped, wrappedLen);
1960 rv = SECSuccess;
1961 }
1962 PR_smprintf_free(wrapped);
1963 }
1964 PORT_Free(asciiDER);
1965 }
1966 } else {
1967 rv = SECITEM_CopyItem(NULL, certDER, &subjectCert->derCert);
1968 }
1969 } while (0);
1970 CERT_DestroyCertificateRequest (certReq);
1971 CERT_DestroyCertificate (subjectCert);
1972 if (rv != SECSuccess) {
1973 PRErrorCode perr = PR_GetError();
1974 fprintf(stderr, "%s: unable to create cert (%s)\n", progName,
1975 SECU_Strerror(perr));
1976 }
1977 return (rv);
1978 }
1981 /*
1982 * map a class to a user presentable string
1983 */
1984 static const char *objClassArray[] = {
1985 "Data",
1986 "Certificate",
1987 "Public Key",
1988 "Private Key",
1989 "Secret Key",
1990 "Hardware Feature",
1991 "Domain Parameters",
1992 "Mechanism"
1993 };
1995 static const char *objNSSClassArray[] = {
1996 "CKO_NSS",
1997 "Crl",
1998 "SMIME Record",
1999 "Trust",
2000 "Builtin Root List"
2001 };
2004 const char *
2005 getObjectClass(CK_ULONG classType)
2006 {
2007 static char buf[sizeof(CK_ULONG)*2+3];
2009 if (classType <= CKO_MECHANISM) {
2010 return objClassArray[classType];
2011 }
2012 if (classType >= CKO_NSS && classType <= CKO_NSS_BUILTIN_ROOT_LIST) {
2013 return objNSSClassArray[classType - CKO_NSS];
2014 }
2015 sprintf(buf, "0x%lx", classType);
2016 return buf;
2017 }
2019 typedef struct {
2020 char *name;
2021 int nameSize;
2022 CK_ULONG value;
2023 } flagArray;
2025 #define NAME_SIZE(x) #x,sizeof(#x)-1
2027 flagArray opFlagsArray[] =
2028 {
2029 {NAME_SIZE(encrypt), CKF_ENCRYPT},
2030 {NAME_SIZE(decrypt), CKF_DECRYPT},
2031 {NAME_SIZE(sign), CKF_SIGN},
2032 {NAME_SIZE(sign_recover), CKF_SIGN_RECOVER},
2033 {NAME_SIZE(verify), CKF_VERIFY},
2034 {NAME_SIZE(verify_recover), CKF_VERIFY_RECOVER},
2035 {NAME_SIZE(wrap), CKF_WRAP},
2036 {NAME_SIZE(unwrap), CKF_UNWRAP},
2037 {NAME_SIZE(derive), CKF_DERIVE},
2038 };
2040 int opFlagsCount = sizeof(opFlagsArray)/sizeof(flagArray);
2042 flagArray attrFlagsArray[] =
2043 {
2044 {NAME_SIZE(token), PK11_ATTR_TOKEN},
2045 {NAME_SIZE(session), PK11_ATTR_SESSION},
2046 {NAME_SIZE(private), PK11_ATTR_PRIVATE},
2047 {NAME_SIZE(public), PK11_ATTR_PUBLIC},
2048 {NAME_SIZE(modifiable), PK11_ATTR_MODIFIABLE},
2049 {NAME_SIZE(unmodifiable), PK11_ATTR_UNMODIFIABLE},
2050 {NAME_SIZE(sensitive), PK11_ATTR_SENSITIVE},
2051 {NAME_SIZE(insensitive), PK11_ATTR_INSENSITIVE},
2052 {NAME_SIZE(extractable), PK11_ATTR_EXTRACTABLE},
2053 {NAME_SIZE(unextractable), PK11_ATTR_UNEXTRACTABLE}
2055 };
2057 int attrFlagsCount = sizeof(attrFlagsArray)/sizeof(flagArray);
2059 #define MAX_STRING 30
2060 CK_ULONG
2061 GetFlags(char *flagsString, flagArray *flagArray, int count)
2062 {
2063 CK_ULONG flagsValue = strtol(flagsString, NULL, 0);
2064 int i;
2066 if ((flagsValue != 0) || (*flagsString == 0)) {
2067 return flagsValue;
2068 }
2069 while (*flagsString) {
2070 for (i=0; i < count; i++) {
2071 if (strncmp(flagsString, flagArray[i].name, flagArray[i].nameSize)
2072 == 0) {
2073 flagsValue |= flagArray[i].value;
2074 flagsString += flagArray[i].nameSize;
2075 if (*flagsString != 0) {
2076 flagsString++;
2077 }
2078 break;
2079 }
2080 }
2081 if (i == count) {
2082 char name[MAX_STRING];
2083 char *tok;
2085 strncpy(name,flagsString, MAX_STRING);
2086 name[MAX_STRING-1] = 0;
2087 tok = strchr(name, ',');
2088 if (tok) {
2089 *tok = 0;
2090 }
2091 fprintf(stderr,"Unknown flag (%s)\n",name);
2092 tok = strchr(flagsString, ',');
2093 if (tok == NULL) {
2094 break;
2095 }
2096 flagsString = tok+1;
2097 }
2098 }
2099 return flagsValue;
2100 }
2102 CK_FLAGS
2103 GetOpFlags(char *flags)
2104 {
2105 return GetFlags(flags, opFlagsArray, opFlagsCount);
2106 }
2108 PK11AttrFlags
2109 GetAttrFlags(char *flags)
2110 {
2111 return GetFlags(flags, attrFlagsArray, attrFlagsCount);
2112 }
2114 char *mkNickname(unsigned char *data, int len)
2115 {
2116 char *nick = PORT_Alloc(len+1);
2117 if (!nick) {
2118 return nick;
2119 }
2120 PORT_Memcpy(nick, data, len);
2121 nick[len] = 0;
2122 return nick;
2123 }
2125 /*
2126 * dump a PK11_MergeTokens error log to the console
2127 */
2128 void
2129 DumpMergeLog(const char *progname, PK11MergeLog *log)
2130 {
2131 PK11MergeLogNode *node;
2133 for (node = log->head; node; node = node->next) {
2134 SECItem attrItem;
2135 char *nickname = NULL;
2136 const char *objectClass = NULL;
2137 SECStatus rv;
2139 attrItem.data = NULL;
2140 rv = PK11_ReadRawAttribute(PK11_TypeGeneric, node->object,
2141 CKA_LABEL, &attrItem);
2142 if (rv == SECSuccess) {
2143 nickname = mkNickname(attrItem.data, attrItem.len);
2144 PORT_Free(attrItem.data);
2145 }
2146 attrItem.data = NULL;
2147 rv = PK11_ReadRawAttribute(PK11_TypeGeneric, node->object,
2148 CKA_CLASS, &attrItem);
2149 if (rv == SECSuccess) {
2150 if (attrItem.len == sizeof(CK_ULONG)) {
2151 objectClass = getObjectClass(*(CK_ULONG *)attrItem.data);
2152 }
2153 PORT_Free(attrItem.data);
2154 }
2156 fprintf(stderr, "%s: Could not merge object %s (type %s): %s\n",
2157 progName,
2158 nickname ? nickname : "unnamed",
2159 objectClass ? objectClass : "unknown",
2160 SECU_Strerror(node->error));
2162 if (nickname) {
2163 PORT_Free(nickname);
2164 }
2165 }
2166 }
2168 /* Certutil commands */
2169 enum {
2170 cmd_AddCert = 0,
2171 cmd_CreateNewCert,
2172 cmd_DeleteCert,
2173 cmd_AddEmailCert,
2174 cmd_DeleteKey,
2175 cmd_GenKeyPair,
2176 cmd_PrintHelp,
2177 cmd_PrintSyntax,
2178 cmd_ListKeys,
2179 cmd_ListCerts,
2180 cmd_ModifyCertTrust,
2181 cmd_NewDBs,
2182 cmd_DumpChain,
2183 cmd_CertReq,
2184 cmd_CreateAndAddCert,
2185 cmd_TokenReset,
2186 cmd_ListModules,
2187 cmd_CheckCertValidity,
2188 cmd_ChangePassword,
2189 cmd_Version,
2190 cmd_Batch,
2191 cmd_Merge,
2192 cmd_UpgradeMerge, /* test only */
2193 max_cmd
2194 };
2196 /* Certutil options */
2197 enum certutilOpts {
2198 opt_SSOPass = 0,
2199 opt_AddKeyUsageExt,
2200 opt_AddBasicConstraintExt,
2201 opt_AddAuthorityKeyIDExt,
2202 opt_AddCRLDistPtsExt,
2203 opt_AddNSCertTypeExt,
2204 opt_AddExtKeyUsageExt,
2205 opt_ExtendedEmailAddrs,
2206 opt_ExtendedDNSNames,
2207 opt_ASCIIForIO,
2208 opt_ValidityTime,
2209 opt_IssuerName,
2210 opt_CertDir,
2211 opt_VerifySig,
2212 opt_PasswordFile,
2213 opt_KeySize,
2214 opt_TokenName,
2215 opt_InputFile,
2216 opt_Emailaddress,
2217 opt_KeyIndex,
2218 opt_KeyType,
2219 opt_DetailedInfo,
2220 opt_SerialNumber,
2221 opt_Nickname,
2222 opt_OutputFile,
2223 opt_PhoneNumber,
2224 opt_DBPrefix,
2225 opt_PQGFile,
2226 opt_BinaryDER,
2227 opt_Subject,
2228 opt_Trust,
2229 opt_Usage,
2230 opt_Validity,
2231 opt_OffsetMonths,
2232 opt_SelfSign,
2233 opt_RW,
2234 opt_Exponent,
2235 opt_NoiseFile,
2236 opt_Hash,
2237 opt_NewPasswordFile,
2238 opt_AddAuthInfoAccExt,
2239 opt_AddSubjInfoAccExt,
2240 opt_AddCertPoliciesExt,
2241 opt_AddPolicyMapExt,
2242 opt_AddPolicyConstrExt,
2243 opt_AddInhibAnyExt,
2244 opt_AddNameConstraintsExt,
2245 opt_AddSubjectKeyIDExt,
2246 opt_AddCmdKeyUsageExt,
2247 opt_AddCmdNSCertTypeExt,
2248 opt_AddCmdExtKeyUsageExt,
2249 opt_SourceDir,
2250 opt_SourcePrefix,
2251 opt_UpgradeID,
2252 opt_UpgradeTokenName,
2253 opt_KeyOpFlagsOn,
2254 opt_KeyOpFlagsOff,
2255 opt_KeyAttrFlags,
2256 opt_EmptyPassword,
2257 opt_CertVersion,
2258 opt_AddSubjectAltNameExt,
2259 opt_DumpExtensionValue,
2260 opt_GenericExtensions,
2261 opt_Help
2262 };
2264 static const
2265 secuCommandFlag commands_init[] =
2266 {
2267 { /* cmd_AddCert */ 'A', PR_FALSE, 0, PR_FALSE },
2268 { /* cmd_CreateNewCert */ 'C', PR_FALSE, 0, PR_FALSE },
2269 { /* cmd_DeleteCert */ 'D', PR_FALSE, 0, PR_FALSE },
2270 { /* cmd_AddEmailCert */ 'E', PR_FALSE, 0, PR_FALSE },
2271 { /* cmd_DeleteKey */ 'F', PR_FALSE, 0, PR_FALSE },
2272 { /* cmd_GenKeyPair */ 'G', PR_FALSE, 0, PR_FALSE },
2273 { /* cmd_PrintHelp */ 'H', PR_FALSE, 0, PR_FALSE, "help" },
2274 { /* cmd_PrintSyntax */ 0, PR_FALSE, 0, PR_FALSE,
2275 "syntax" },
2276 { /* cmd_ListKeys */ 'K', PR_FALSE, 0, PR_FALSE },
2277 { /* cmd_ListCerts */ 'L', PR_FALSE, 0, PR_FALSE },
2278 { /* cmd_ModifyCertTrust */ 'M', PR_FALSE, 0, PR_FALSE },
2279 { /* cmd_NewDBs */ 'N', PR_FALSE, 0, PR_FALSE },
2280 { /* cmd_DumpChain */ 'O', PR_FALSE, 0, PR_FALSE },
2281 { /* cmd_CertReq */ 'R', PR_FALSE, 0, PR_FALSE },
2282 { /* cmd_CreateAndAddCert */ 'S', PR_FALSE, 0, PR_FALSE },
2283 { /* cmd_TokenReset */ 'T', PR_FALSE, 0, PR_FALSE },
2284 { /* cmd_ListModules */ 'U', PR_FALSE, 0, PR_FALSE },
2285 { /* cmd_CheckCertValidity */ 'V', PR_FALSE, 0, PR_FALSE },
2286 { /* cmd_ChangePassword */ 'W', PR_FALSE, 0, PR_FALSE },
2287 { /* cmd_Version */ 'Y', PR_FALSE, 0, PR_FALSE },
2288 { /* cmd_Batch */ 'B', PR_FALSE, 0, PR_FALSE },
2289 { /* cmd_Merge */ 0, PR_FALSE, 0, PR_FALSE, "merge" },
2290 { /* cmd_UpgradeMerge */ 0, PR_FALSE, 0, PR_FALSE,
2291 "upgrade-merge" }
2292 };
2293 #define NUM_COMMANDS ((sizeof commands_init) / (sizeof commands_init[0]))
2295 static const
2296 secuCommandFlag options_init[] =
2297 {
2298 { /* opt_SSOPass */ '0', PR_TRUE, 0, PR_FALSE },
2299 { /* opt_AddKeyUsageExt */ '1', PR_FALSE, 0, PR_FALSE },
2300 { /* opt_AddBasicConstraintExt*/ '2', PR_FALSE, 0, PR_FALSE },
2301 { /* opt_AddAuthorityKeyIDExt*/ '3', PR_FALSE, 0, PR_FALSE },
2302 { /* opt_AddCRLDistPtsExt */ '4', PR_FALSE, 0, PR_FALSE },
2303 { /* opt_AddNSCertTypeExt */ '5', PR_FALSE, 0, PR_FALSE },
2304 { /* opt_AddExtKeyUsageExt */ '6', PR_FALSE, 0, PR_FALSE },
2305 { /* opt_ExtendedEmailAddrs */ '7', PR_TRUE, 0, PR_FALSE },
2306 { /* opt_ExtendedDNSNames */ '8', PR_TRUE, 0, PR_FALSE },
2307 { /* opt_ASCIIForIO */ 'a', PR_FALSE, 0, PR_FALSE },
2308 { /* opt_ValidityTime */ 'b', PR_TRUE, 0, PR_FALSE },
2309 { /* opt_IssuerName */ 'c', PR_TRUE, 0, PR_FALSE },
2310 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
2311 { /* opt_VerifySig */ 'e', PR_FALSE, 0, PR_FALSE },
2312 { /* opt_PasswordFile */ 'f', PR_TRUE, 0, PR_FALSE },
2313 { /* opt_KeySize */ 'g', PR_TRUE, 0, PR_FALSE },
2314 { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE },
2315 { /* opt_InputFile */ 'i', PR_TRUE, 0, PR_FALSE },
2316 { /* opt_Emailaddress */ 0, PR_TRUE, 0, PR_FALSE, "email" },
2317 { /* opt_KeyIndex */ 'j', PR_TRUE, 0, PR_FALSE },
2318 { /* opt_KeyType */ 'k', PR_TRUE, 0, PR_FALSE },
2319 { /* opt_DetailedInfo */ 'l', PR_FALSE, 0, PR_FALSE },
2320 { /* opt_SerialNumber */ 'm', PR_TRUE, 0, PR_FALSE },
2321 { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE },
2322 { /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE },
2323 { /* opt_PhoneNumber */ 'p', PR_TRUE, 0, PR_FALSE },
2324 { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE },
2325 { /* opt_PQGFile */ 'q', PR_TRUE, 0, PR_FALSE },
2326 { /* opt_BinaryDER */ 'r', PR_FALSE, 0, PR_FALSE },
2327 { /* opt_Subject */ 's', PR_TRUE, 0, PR_FALSE },
2328 { /* opt_Trust */ 't', PR_TRUE, 0, PR_FALSE },
2329 { /* opt_Usage */ 'u', PR_TRUE, 0, PR_FALSE },
2330 { /* opt_Validity */ 'v', PR_TRUE, 0, PR_FALSE },
2331 { /* opt_OffsetMonths */ 'w', PR_TRUE, 0, PR_FALSE },
2332 { /* opt_SelfSign */ 'x', PR_FALSE, 0, PR_FALSE },
2333 { /* opt_RW */ 'X', PR_FALSE, 0, PR_FALSE },
2334 { /* opt_Exponent */ 'y', PR_TRUE, 0, PR_FALSE },
2335 { /* opt_NoiseFile */ 'z', PR_TRUE, 0, PR_FALSE },
2336 { /* opt_Hash */ 'Z', PR_TRUE, 0, PR_FALSE },
2337 { /* opt_NewPasswordFile */ '@', PR_TRUE, 0, PR_FALSE },
2338 { /* opt_AddAuthInfoAccExt */ 0, PR_FALSE, 0, PR_FALSE, "extAIA" },
2339 { /* opt_AddSubjInfoAccExt */ 0, PR_FALSE, 0, PR_FALSE, "extSIA" },
2340 { /* opt_AddCertPoliciesExt */ 0, PR_FALSE, 0, PR_FALSE, "extCP" },
2341 { /* opt_AddPolicyMapExt */ 0, PR_FALSE, 0, PR_FALSE, "extPM" },
2342 { /* opt_AddPolicyConstrExt */ 0, PR_FALSE, 0, PR_FALSE, "extPC" },
2343 { /* opt_AddInhibAnyExt */ 0, PR_FALSE, 0, PR_FALSE, "extIA" },
2344 { /* opt_AddNameConstraintsExt*/ 0, PR_FALSE, 0, PR_FALSE, "extNC" },
2345 { /* opt_AddSubjectKeyIDExt */ 0, PR_FALSE, 0, PR_FALSE,
2346 "extSKID" },
2347 { /* opt_AddCmdKeyUsageExt */ 0, PR_TRUE, 0, PR_FALSE,
2348 "keyUsage" },
2349 { /* opt_AddCmdNSCertTypeExt */ 0, PR_TRUE, 0, PR_FALSE,
2350 "nsCertType" },
2351 { /* opt_AddCmdExtKeyUsageExt*/ 0, PR_TRUE, 0, PR_FALSE,
2352 "extKeyUsage" },
2354 { /* opt_SourceDir */ 0, PR_TRUE, 0, PR_FALSE,
2355 "source-dir"},
2356 { /* opt_SourcePrefix */ 0, PR_TRUE, 0, PR_FALSE,
2357 "source-prefix"},
2358 { /* opt_UpgradeID */ 0, PR_TRUE, 0, PR_FALSE,
2359 "upgrade-id"},
2360 { /* opt_UpgradeTokenName */ 0, PR_TRUE, 0, PR_FALSE,
2361 "upgrade-token-name"},
2362 { /* opt_KeyOpFlagsOn */ 0, PR_TRUE, 0, PR_FALSE,
2363 "keyOpFlagsOn"},
2364 { /* opt_KeyOpFlagsOff */ 0, PR_TRUE, 0, PR_FALSE,
2365 "keyOpFlagsOff"},
2366 { /* opt_KeyAttrFlags */ 0, PR_TRUE, 0, PR_FALSE,
2367 "keyAttrFlags"},
2368 { /* opt_EmptyPassword */ 0, PR_FALSE, 0, PR_FALSE,
2369 "empty-password"},
2370 { /* opt_CertVersion */ 0, PR_FALSE, 0, PR_FALSE,
2371 "certVersion"},
2372 { /* opt_AddSubjectAltExt */ 0, PR_TRUE, 0, PR_FALSE, "extSAN"},
2373 { /* opt_DumpExtensionValue */ 0, PR_TRUE, 0, PR_FALSE,
2374 "dump-ext-val"},
2375 { /* opt_GenericExtensions */ 0, PR_TRUE, 0, PR_FALSE,
2376 "extGeneric"},
2377 };
2378 #define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0]))
2380 static secuCommandFlag certutil_commands[NUM_COMMANDS];
2381 static secuCommandFlag certutil_options [NUM_OPTIONS ];
2383 static const secuCommand certutil = {
2384 NUM_COMMANDS,
2385 NUM_OPTIONS,
2386 certutil_commands,
2387 certutil_options
2388 };
2390 static certutilExtnList certutil_extns;
2392 static int
2393 certutil_main(int argc, char **argv, PRBool initialize)
2394 {
2395 CERTCertDBHandle *certHandle;
2396 PK11SlotInfo *slot = NULL;
2397 CERTName * subject = 0;
2398 PRFileDesc *inFile = PR_STDIN;
2399 PRFileDesc *outFile = PR_STDOUT;
2400 SECItem certReqDER = { siBuffer, NULL, 0 };
2401 SECItem certDER = { siBuffer, NULL, 0 };
2402 char * slotname = "internal";
2403 char * certPrefix = "";
2404 char * sourceDir = "";
2405 char * srcCertPrefix = "";
2406 char * upgradeID = "";
2407 char * upgradeTokenName = "";
2408 KeyType keytype = rsaKey;
2409 char * name = NULL;
2410 char * email = NULL;
2411 char * keysource = NULL;
2412 SECOidTag hashAlgTag = SEC_OID_UNKNOWN;
2413 int keysize = DEFAULT_KEY_BITS;
2414 int publicExponent = 0x010001;
2415 int certVersion = SEC_CERTIFICATE_VERSION_3;
2416 unsigned int serialNumber = 0;
2417 int warpmonths = 0;
2418 int validityMonths = 3;
2419 int commandsEntered = 0;
2420 char commandToRun = '\0';
2421 secuPWData pwdata = { PW_NONE, 0 };
2422 secuPWData pwdata2 = { PW_NONE, 0 };
2423 PRBool readOnly = PR_FALSE;
2424 PRBool initialized = PR_FALSE;
2425 CK_FLAGS keyOpFlagsOn = 0;
2426 CK_FLAGS keyOpFlagsOff = 0;
2427 PK11AttrFlags keyAttrFlags =
2428 PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE;
2430 SECKEYPrivateKey *privkey = NULL;
2431 SECKEYPublicKey *pubkey = NULL;
2433 int i;
2434 SECStatus rv;
2436 progName = PORT_Strrchr(argv[0], '/');
2437 progName = progName ? progName+1 : argv[0];
2438 memcpy(certutil_commands, commands_init, sizeof commands_init);
2439 memcpy(certutil_options, options_init, sizeof options_init);
2441 rv = SECU_ParseCommandLine(argc, argv, progName, &certutil);
2443 if (rv != SECSuccess)
2444 Usage(progName);
2446 if (certutil.commands[cmd_PrintSyntax].activated) {
2447 PrintSyntax(progName);
2448 }
2450 if (certutil.commands[cmd_PrintHelp].activated) {
2451 int i;
2452 char buf[2];
2453 const char *command = NULL;
2454 for (i = 0; i < max_cmd; i++) {
2455 if (i == cmd_PrintHelp)
2456 continue;
2457 if (certutil.commands[i].activated) {
2458 if (certutil.commands[i].flag) {
2459 buf[0] = certutil.commands[i].flag;
2460 buf[1] = 0;
2461 command = buf;
2462 }
2463 else {
2464 command = certutil.commands[i].longform;
2465 }
2466 break;
2467 }
2468 }
2469 LongUsage(progName, (command ? usage_selected : usage_all), command);
2470 exit(1);
2471 }
2473 if (certutil.options[opt_PasswordFile].arg) {
2474 pwdata.source = PW_FROMFILE;
2475 pwdata.data = certutil.options[opt_PasswordFile].arg;
2476 }
2477 if (certutil.options[opt_NewPasswordFile].arg) {
2478 pwdata2.source = PW_FROMFILE;
2479 pwdata2.data = certutil.options[opt_NewPasswordFile].arg;
2480 }
2482 if (certutil.options[opt_CertDir].activated)
2483 SECU_ConfigDirectory(certutil.options[opt_CertDir].arg);
2485 if (certutil.options[opt_SourceDir].activated)
2486 sourceDir = certutil.options[opt_SourceDir].arg;
2488 if (certutil.options[opt_UpgradeID].activated)
2489 upgradeID = certutil.options[opt_UpgradeID].arg;
2491 if (certutil.options[opt_UpgradeTokenName].activated)
2492 upgradeTokenName = certutil.options[opt_UpgradeTokenName].arg;
2494 if (certutil.options[opt_KeySize].activated) {
2495 keysize = PORT_Atoi(certutil.options[opt_KeySize].arg);
2496 if ((keysize < MIN_KEY_BITS) || (keysize > MAX_KEY_BITS)) {
2497 PR_fprintf(PR_STDERR,
2498 "%s -g: Keysize must be between %d and %d.\n",
2499 progName, MIN_KEY_BITS, MAX_KEY_BITS);
2500 return 255;
2501 }
2502 #ifndef NSS_DISABLE_ECC
2503 if (keytype == ecKey) {
2504 PR_fprintf(PR_STDERR, "%s -g: Not for ec keys.\n", progName);
2505 return 255;
2506 }
2507 #endif /* NSS_DISABLE_ECC */
2509 }
2511 /* -h specify token name */
2512 if (certutil.options[opt_TokenName].activated) {
2513 if (PL_strcmp(certutil.options[opt_TokenName].arg, "all") == 0)
2514 slotname = NULL;
2515 else
2516 slotname = PL_strdup(certutil.options[opt_TokenName].arg);
2517 }
2519 /* -Z hash type */
2520 if (certutil.options[opt_Hash].activated) {
2521 char * arg = certutil.options[opt_Hash].arg;
2522 hashAlgTag = SECU_StringToSignatureAlgTag(arg);
2523 if (hashAlgTag == SEC_OID_UNKNOWN) {
2524 PR_fprintf(PR_STDERR, "%s -Z: %s is not a recognized type.\n",
2525 progName, arg);
2526 return 255;
2527 }
2528 }
2530 /* -k key type */
2531 if (certutil.options[opt_KeyType].activated) {
2532 char * arg = certutil.options[opt_KeyType].arg;
2533 if (PL_strcmp(arg, "rsa") == 0) {
2534 keytype = rsaKey;
2535 } else if (PL_strcmp(arg, "dsa") == 0) {
2536 keytype = dsaKey;
2537 #ifndef NSS_DISABLE_ECC
2538 } else if (PL_strcmp(arg, "ec") == 0) {
2539 keytype = ecKey;
2540 #endif /* NSS_DISABLE_ECC */
2541 } else if (PL_strcmp(arg, "all") == 0) {
2542 keytype = nullKey;
2543 } else {
2544 /* use an existing private/public key pair */
2545 keysource = arg;
2546 }
2547 } else if (certutil.commands[cmd_ListKeys].activated) {
2548 keytype = nullKey;
2549 }
2551 if (certutil.options[opt_KeyOpFlagsOn].activated) {
2552 keyOpFlagsOn = GetOpFlags(certutil.options[opt_KeyOpFlagsOn].arg);
2553 }
2554 if (certutil.options[opt_KeyOpFlagsOff].activated) {
2555 keyOpFlagsOff = GetOpFlags(certutil.options[opt_KeyOpFlagsOff].arg);
2556 keyOpFlagsOn &=~keyOpFlagsOff; /* make off override on */
2557 }
2558 if (certutil.options[opt_KeyAttrFlags].activated) {
2559 keyAttrFlags = GetAttrFlags(certutil.options[opt_KeyAttrFlags].arg);
2560 }
2562 /* -m serial number */
2563 if (certutil.options[opt_SerialNumber].activated) {
2564 int sn = PORT_Atoi(certutil.options[opt_SerialNumber].arg);
2565 if (sn < 0) {
2566 PR_fprintf(PR_STDERR, "%s -m: %s is not a valid serial number.\n",
2567 progName, certutil.options[opt_SerialNumber].arg);
2568 return 255;
2569 }
2570 serialNumber = sn;
2571 }
2573 /* -P certdb name prefix */
2574 if (certutil.options[opt_DBPrefix].activated) {
2575 if (certutil.options[opt_DBPrefix].arg) {
2576 certPrefix = strdup(certutil.options[opt_DBPrefix].arg);
2577 } else {
2578 Usage(progName);
2579 }
2580 }
2582 /* --source-prefix certdb name prefix */
2583 if (certutil.options[opt_SourcePrefix].activated) {
2584 if (certutil.options[opt_SourcePrefix].arg) {
2585 srcCertPrefix = strdup(certutil.options[opt_SourcePrefix].arg);
2586 } else {
2587 Usage(progName);
2588 }
2589 }
2591 /* -q PQG file or curve name */
2592 if (certutil.options[opt_PQGFile].activated) {
2593 #ifndef NSS_DISABLE_ECC
2594 if ((keytype != dsaKey) && (keytype != ecKey)) {
2595 PR_fprintf(PR_STDERR, "%s -q: specifies a PQG file for DSA keys" \
2596 " (-k dsa) or a named curve for EC keys (-k ec)\n)",
2597 progName);
2598 #else /* } */
2599 if (keytype != dsaKey) {
2600 PR_fprintf(PR_STDERR, "%s -q: PQG file is for DSA key (-k dsa).\n)",
2601 progName);
2602 #endif /* NSS_DISABLE_ECC */
2603 return 255;
2604 }
2605 }
2607 /* -s subject name */
2608 if (certutil.options[opt_Subject].activated) {
2609 subject = CERT_AsciiToName(certutil.options[opt_Subject].arg);
2610 if (!subject) {
2611 PR_fprintf(PR_STDERR, "%s -s: improperly formatted name: \"%s\"\n",
2612 progName, certutil.options[opt_Subject].arg);
2613 return 255;
2614 }
2615 }
2617 /* -v validity period */
2618 if (certutil.options[opt_Validity].activated) {
2619 validityMonths = PORT_Atoi(certutil.options[opt_Validity].arg);
2620 if (validityMonths < 0) {
2621 PR_fprintf(PR_STDERR, "%s -v: incorrect validity period: \"%s\"\n",
2622 progName, certutil.options[opt_Validity].arg);
2623 return 255;
2624 }
2625 }
2627 /* -w warp months */
2628 if (certutil.options[opt_OffsetMonths].activated)
2629 warpmonths = PORT_Atoi(certutil.options[opt_OffsetMonths].arg);
2631 /* -y public exponent (for RSA) */
2632 if (certutil.options[opt_Exponent].activated) {
2633 publicExponent = PORT_Atoi(certutil.options[opt_Exponent].arg);
2634 if ((publicExponent != 3) &&
2635 (publicExponent != 17) &&
2636 (publicExponent != 65537)) {
2637 PR_fprintf(PR_STDERR, "%s -y: incorrect public exponent %d.",
2638 progName, publicExponent);
2639 PR_fprintf(PR_STDERR, "Must be 3, 17, or 65537.\n");
2640 return 255;
2641 }
2642 }
2644 /* --certVersion */
2645 if (certutil.options[opt_CertVersion].activated) {
2646 certVersion = PORT_Atoi(certutil.options[opt_CertVersion].arg);
2647 if (certVersion < 1 || certVersion > 4) {
2648 PR_fprintf(PR_STDERR, "%s -certVersion: incorrect certificate version %d.",
2649 progName, certVersion);
2650 PR_fprintf(PR_STDERR, "Must be 1, 2, 3 or 4.\n");
2651 return 255;
2652 }
2653 certVersion = certVersion - 1;
2654 }
2657 /* Check number of commands entered. */
2658 commandsEntered = 0;
2659 for (i=0; i< certutil.numCommands; i++) {
2660 if (certutil.commands[i].activated) {
2661 commandToRun = certutil.commands[i].flag;
2662 commandsEntered++;
2663 }
2664 if (commandsEntered > 1)
2665 break;
2666 }
2667 if (commandsEntered > 1) {
2668 PR_fprintf(PR_STDERR, "%s: only one command at a time!\n", progName);
2669 PR_fprintf(PR_STDERR, "You entered: ");
2670 for (i=0; i< certutil.numCommands; i++) {
2671 if (certutil.commands[i].activated)
2672 PR_fprintf(PR_STDERR, " -%c", certutil.commands[i].flag);
2673 }
2674 PR_fprintf(PR_STDERR, "\n");
2675 return 255;
2676 }
2677 if (commandsEntered == 0) {
2678 Usage(progName);
2679 }
2681 if (certutil.commands[cmd_ListCerts].activated ||
2682 certutil.commands[cmd_PrintHelp].activated ||
2683 certutil.commands[cmd_ListKeys].activated ||
2684 certutil.commands[cmd_ListModules].activated ||
2685 certutil.commands[cmd_CheckCertValidity].activated ||
2686 certutil.commands[cmd_Version].activated ) {
2687 readOnly = !certutil.options[opt_RW].activated;
2688 }
2690 /* -A, -D, -F, -M, -S, -V, and all require -n */
2691 if ((certutil.commands[cmd_AddCert].activated ||
2692 certutil.commands[cmd_DeleteCert].activated ||
2693 certutil.commands[cmd_DeleteKey].activated ||
2694 certutil.commands[cmd_DumpChain].activated ||
2695 certutil.commands[cmd_ModifyCertTrust].activated ||
2696 certutil.commands[cmd_CreateAndAddCert].activated ||
2697 certutil.commands[cmd_CheckCertValidity].activated) &&
2698 !certutil.options[opt_Nickname].activated) {
2699 PR_fprintf(PR_STDERR,
2700 "%s -%c: nickname is required for this command (-n).\n",
2701 progName, commandToRun);
2702 return 255;
2703 }
2705 /* -A, -E, -M, -S require trust */
2706 if ((certutil.commands[cmd_AddCert].activated ||
2707 certutil.commands[cmd_AddEmailCert].activated ||
2708 certutil.commands[cmd_ModifyCertTrust].activated ||
2709 certutil.commands[cmd_CreateAndAddCert].activated) &&
2710 !certutil.options[opt_Trust].activated) {
2711 PR_fprintf(PR_STDERR,
2712 "%s -%c: trust is required for this command (-t).\n",
2713 progName, commandToRun);
2714 return 255;
2715 }
2717 /* if -L is given raw, ascii or dump mode, it must be for only one cert. */
2718 if (certutil.commands[cmd_ListCerts].activated &&
2719 (certutil.options[opt_ASCIIForIO].activated ||
2720 certutil.options[opt_DumpExtensionValue].activated ||
2721 certutil.options[opt_BinaryDER].activated) &&
2722 !certutil.options[opt_Nickname].activated) {
2723 PR_fprintf(PR_STDERR,
2724 "%s: nickname is required to dump cert in raw or ascii mode.\n",
2725 progName);
2726 return 255;
2727 }
2729 /* -L can only be in (raw || ascii). */
2730 if (certutil.commands[cmd_ListCerts].activated &&
2731 certutil.options[opt_ASCIIForIO].activated &&
2732 certutil.options[opt_BinaryDER].activated) {
2733 PR_fprintf(PR_STDERR,
2734 "%s: cannot specify both -r and -a when dumping cert.\n",
2735 progName);
2736 return 255;
2737 }
2739 /* If making a cert request, need a subject. */
2740 if ((certutil.commands[cmd_CertReq].activated ||
2741 certutil.commands[cmd_CreateAndAddCert].activated) &&
2742 !(certutil.options[opt_Subject].activated || keysource)) {
2743 PR_fprintf(PR_STDERR,
2744 "%s -%c: subject is required to create a cert request.\n",
2745 progName, commandToRun);
2746 return 255;
2747 }
2749 /* If making a cert, need a serial number. */
2750 if ((certutil.commands[cmd_CreateNewCert].activated ||
2751 certutil.commands[cmd_CreateAndAddCert].activated) &&
2752 !certutil.options[opt_SerialNumber].activated) {
2753 /* Make a default serial number from the current time. */
2754 PRTime now = PR_Now();
2755 LL_USHR(now, now, 19);
2756 LL_L2UI(serialNumber, now);
2757 }
2759 /* Validation needs the usage to validate for. */
2760 if (certutil.commands[cmd_CheckCertValidity].activated &&
2761 !certutil.options[opt_Usage].activated) {
2762 PR_fprintf(PR_STDERR,
2763 "%s -V: specify a usage to validate the cert for (-u).\n",
2764 progName);
2765 return 255;
2766 }
2768 /* Upgrade/Merge needs a source database and a upgrade id. */
2769 if (certutil.commands[cmd_UpgradeMerge].activated &&
2770 !(certutil.options[opt_SourceDir].activated &&
2771 certutil.options[opt_UpgradeID].activated)) {
2773 PR_fprintf(PR_STDERR,
2774 "%s --upgrade-merge: specify an upgrade database directory "
2775 "(--source-dir) and\n"
2776 " an upgrade ID (--upgrade-id).\n",
2777 progName);
2778 return 255;
2779 }
2781 /* Merge needs a source database */
2782 if (certutil.commands[cmd_Merge].activated &&
2783 !certutil.options[opt_SourceDir].activated) {
2786 PR_fprintf(PR_STDERR,
2787 "%s --merge: specify an source database directory "
2788 "(--source-dir)\n",
2789 progName);
2790 return 255;
2791 }
2794 /* To make a cert, need either a issuer or to self-sign it. */
2795 if (certutil.commands[cmd_CreateAndAddCert].activated &&
2796 !(certutil.options[opt_IssuerName].activated ||
2797 certutil.options[opt_SelfSign].activated)) {
2798 PR_fprintf(PR_STDERR,
2799 "%s -S: must specify issuer (-c) or self-sign (-x).\n",
2800 progName);
2801 return 255;
2802 }
2804 /* Using slotname == NULL for listing keys and certs on all slots,
2805 * but only that. */
2806 if (!(certutil.commands[cmd_ListKeys].activated ||
2807 certutil.commands[cmd_DumpChain].activated ||
2808 certutil.commands[cmd_ListCerts].activated) && slotname == NULL) {
2809 PR_fprintf(PR_STDERR,
2810 "%s -%c: cannot use \"-h all\" for this command.\n",
2811 progName, commandToRun);
2812 return 255;
2813 }
2815 /* Using keytype == nullKey for list all key types, but only that. */
2816 if (!certutil.commands[cmd_ListKeys].activated && keytype == nullKey) {
2817 PR_fprintf(PR_STDERR,
2818 "%s -%c: cannot use \"-k all\" for this command.\n",
2819 progName, commandToRun);
2820 return 255;
2821 }
2823 /* Open the input file. */
2824 if (certutil.options[opt_InputFile].activated) {
2825 inFile = PR_Open(certutil.options[opt_InputFile].arg, PR_RDONLY, 0);
2826 if (!inFile) {
2827 PR_fprintf(PR_STDERR,
2828 "%s: unable to open \"%s\" for reading (%ld, %ld).\n",
2829 progName, certutil.options[opt_InputFile].arg,
2830 PR_GetError(), PR_GetOSError());
2831 return 255;
2832 }
2833 }
2835 /* Open the output file. */
2836 if (certutil.options[opt_OutputFile].activated) {
2837 outFile = PR_Open(certutil.options[opt_OutputFile].arg,
2838 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660);
2839 if (!outFile) {
2840 PR_fprintf(PR_STDERR,
2841 "%s: unable to open \"%s\" for writing (%ld, %ld).\n",
2842 progName, certutil.options[opt_OutputFile].arg,
2843 PR_GetError(), PR_GetOSError());
2844 return 255;
2845 }
2846 }
2848 name = SECU_GetOptionArg(&certutil, opt_Nickname);
2849 email = SECU_GetOptionArg(&certutil, opt_Emailaddress);
2851 PK11_SetPasswordFunc(SECU_GetModulePassword);
2853 if (PR_TRUE == initialize) {
2854 /* Initialize NSPR and NSS. */
2855 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
2856 if (!certutil.commands[cmd_UpgradeMerge].activated) {
2857 rv = NSS_Initialize(SECU_ConfigDirectory(NULL),
2858 certPrefix, certPrefix,
2859 "secmod.db", readOnly ? NSS_INIT_READONLY: 0);
2860 } else {
2861 rv = NSS_InitWithMerge(SECU_ConfigDirectory(NULL),
2862 certPrefix, certPrefix, "secmod.db",
2863 sourceDir, srcCertPrefix, srcCertPrefix,
2864 upgradeID, upgradeTokenName,
2865 readOnly ? NSS_INIT_READONLY: 0);
2866 }
2867 if (rv != SECSuccess) {
2868 SECU_PrintPRandOSError(progName);
2869 rv = SECFailure;
2870 goto shutdown;
2871 }
2872 initialized = PR_TRUE;
2873 SECU_RegisterDynamicOids();
2874 }
2875 certHandle = CERT_GetDefaultCertDB();
2877 if (certutil.commands[cmd_Version].activated) {
2878 printf("Certificate database content version: command not implemented.\n");
2879 }
2881 if (PL_strcmp(slotname, "internal") == 0)
2882 slot = PK11_GetInternalKeySlot();
2883 else if (slotname != NULL)
2884 slot = PK11_FindSlotByName(slotname);
2886 if ( !slot && (certutil.commands[cmd_NewDBs].activated ||
2887 certutil.commands[cmd_ModifyCertTrust].activated ||
2888 certutil.commands[cmd_ChangePassword].activated ||
2889 certutil.commands[cmd_TokenReset].activated ||
2890 certutil.commands[cmd_CreateAndAddCert].activated ||
2891 certutil.commands[cmd_AddCert].activated ||
2892 certutil.commands[cmd_Merge].activated ||
2893 certutil.commands[cmd_UpgradeMerge].activated ||
2894 certutil.commands[cmd_AddEmailCert].activated)) {
2896 SECU_PrintError(progName, "could not find the slot %s",slotname);
2897 rv = SECFailure;
2898 goto shutdown;
2899 }
2901 /* If creating new database, initialize the password. */
2902 if (certutil.commands[cmd_NewDBs].activated) {
2903 if(certutil.options[opt_EmptyPassword].activated && (PK11_NeedUserInit(slot)))
2904 PK11_InitPin(slot, (char*)NULL, "");
2905 else
2906 SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg,
2907 certutil.options[opt_NewPasswordFile].arg);
2908 }
2910 /* walk through the upgrade merge if necessary.
2911 * This option is more to test what some applications will want to do
2912 * to do an automatic upgrade. The --merge command is more useful for
2913 * the general case where 2 database need to be merged together.
2914 */
2915 if (certutil.commands[cmd_UpgradeMerge].activated) {
2916 if (*upgradeTokenName == 0) {
2917 upgradeTokenName = upgradeID;
2918 }
2919 if (!PK11_IsInternal(slot)) {
2920 fprintf(stderr, "Only internal DB's can be upgraded\n");
2921 rv = SECSuccess;
2922 goto shutdown;
2923 }
2924 if (!PK11_IsRemovable(slot)) {
2925 printf("database already upgraded.\n");
2926 rv = SECSuccess;
2927 goto shutdown;
2928 }
2929 if (!PK11_NeedLogin(slot)) {
2930 printf("upgrade complete!\n");
2931 rv = SECSuccess;
2932 goto shutdown;
2933 }
2934 /* authenticate to the old DB if necessary */
2935 if (PORT_Strcmp(PK11_GetTokenName(slot), upgradeTokenName) == 0) {
2936 /* if we need a password, supply it. This will be the password
2937 * for the old database */
2938 rv = PK11_Authenticate(slot, PR_FALSE, &pwdata2);
2939 if (rv != SECSuccess) {
2940 SECU_PrintError(progName, "Could not get password for %s",
2941 upgradeTokenName);
2942 goto shutdown;
2943 }
2944 /*
2945 * if we succeeded above, but still aren't logged in, that means
2946 * we just supplied the password for the old database. We may
2947 * need the password for the new database. NSS will automatically
2948 * change the token names at this point
2949 */
2950 if (PK11_IsLoggedIn(slot, &pwdata)) {
2951 printf("upgrade complete!\n");
2952 rv = SECSuccess;
2953 goto shutdown;
2954 }
2955 }
2957 /* call PK11_IsPresent to update our cached token information */
2958 if (!PK11_IsPresent(slot)) {
2959 /* this shouldn't happen. We call isPresent to force a token
2960 * info update */
2961 fprintf(stderr, "upgrade/merge internal error\n");
2962 rv = SECFailure;
2963 goto shutdown;
2964 }
2966 /* the token is now set to the state of the source database,
2967 * if we need a password for it, PK11_Authenticate will
2968 * automatically prompt us */
2969 rv = PK11_Authenticate(slot, PR_FALSE, &pwdata);
2970 if (rv == SECSuccess) {
2971 printf("upgrade complete!\n");
2972 } else {
2973 SECU_PrintError(progName, "Could not get password for %s",
2974 PK11_GetTokenName(slot));
2975 }
2976 goto shutdown;
2977 }
2979 /*
2980 * merge 2 databases.
2981 */
2982 if (certutil.commands[cmd_Merge].activated) {
2983 PK11SlotInfo *sourceSlot = NULL;
2984 PK11MergeLog *log;
2985 char *modspec = PR_smprintf(
2986 "configDir='%s' certPrefix='%s' tokenDescription='%s'",
2987 sourceDir, srcCertPrefix,
2988 *upgradeTokenName ? upgradeTokenName : "Source Database");
2990 if (!modspec) {
2991 rv = SECFailure;
2992 goto shutdown;
2993 }
2995 sourceSlot = SECMOD_OpenUserDB(modspec);
2996 PR_smprintf_free(modspec);
2997 if (!sourceSlot) {
2998 SECU_PrintError(progName, "couldn't open source database");
2999 rv = SECFailure;
3000 goto shutdown;
3001 }
3003 rv = PK11_Authenticate(slot, PR_FALSE, &pwdata);
3004 if (rv != SECSuccess) {
3005 SECU_PrintError(progName, "Couldn't get password for %s",
3006 PK11_GetTokenName(slot));
3007 goto merge_fail;
3008 }
3010 rv = PK11_Authenticate(sourceSlot, PR_FALSE, &pwdata2);
3011 if (rv != SECSuccess) {
3012 SECU_PrintError(progName, "Couldn't get password for %s",
3013 PK11_GetTokenName(sourceSlot));
3014 goto merge_fail;
3015 }
3017 log = PK11_CreateMergeLog();
3018 if (!log) {
3019 rv = SECFailure;
3020 SECU_PrintError(progName, "couldn't create error log");
3021 goto merge_fail;
3022 }
3024 rv = PK11_MergeTokens(slot, sourceSlot, log, &pwdata, &pwdata2);
3025 if (rv != SECSuccess) {
3026 DumpMergeLog(progName, log);
3027 }
3028 PK11_DestroyMergeLog(log);
3030 merge_fail:
3031 SECMOD_CloseUserDB(sourceSlot);
3032 PK11_FreeSlot(sourceSlot);
3033 goto shutdown;
3034 }
3036 /* The following 8 options are mutually exclusive with all others. */
3038 /* List certs (-L) */
3039 if (certutil.commands[cmd_ListCerts].activated) {
3040 if (certutil.options[opt_DumpExtensionValue].activated) {
3041 const char *oid_str;
3042 SECItem oid_item;
3043 SECStatus srv;
3044 oid_item.data = NULL;
3045 oid_item.len = 0;
3046 oid_str = certutil.options[opt_DumpExtensionValue].arg;
3047 srv = GetOidFromString(NULL, &oid_item, oid_str, strlen(oid_str));
3048 if (srv != SECSuccess) {
3049 SECU_PrintError(progName, "malformed extension OID %s",
3050 oid_str);
3051 goto shutdown;
3052 }
3053 rv = ListCerts(certHandle, name, email, slot,
3054 PR_TRUE /*binary*/, PR_FALSE /*ascii*/,
3055 &oid_item,
3056 outFile, &pwdata);
3057 } else {
3058 rv = ListCerts(certHandle, name, email, slot,
3059 certutil.options[opt_BinaryDER].activated,
3060 certutil.options[opt_ASCIIForIO].activated,
3061 NULL, outFile, &pwdata);
3062 }
3063 goto shutdown;
3064 }
3065 if (certutil.commands[cmd_DumpChain].activated) {
3066 rv = DumpChain(certHandle, name,
3067 certutil.options[opt_ASCIIForIO].activated);
3068 goto shutdown;
3069 }
3070 /* XXX needs work */
3071 /* List keys (-K) */
3072 if (certutil.commands[cmd_ListKeys].activated) {
3073 rv = ListKeys(slot, name, 0 /*keyindex*/, keytype, PR_FALSE /*dopriv*/,
3074 &pwdata);
3075 goto shutdown;
3076 }
3077 /* List modules (-U) */
3078 if (certutil.commands[cmd_ListModules].activated) {
3079 rv = ListModules();
3080 goto shutdown;
3081 }
3082 /* Delete cert (-D) */
3083 if (certutil.commands[cmd_DeleteCert].activated) {
3084 rv = DeleteCert(certHandle, name);
3085 goto shutdown;
3086 }
3087 /* Delete key (-F) */
3088 if (certutil.commands[cmd_DeleteKey].activated) {
3089 rv = DeleteKey(name, &pwdata);
3090 goto shutdown;
3091 }
3092 /* Modify trust attribute for cert (-M) */
3093 if (certutil.commands[cmd_ModifyCertTrust].activated) {
3094 rv = ChangeTrustAttributes(certHandle, slot, name,
3095 certutil.options[opt_Trust].arg, &pwdata);
3096 goto shutdown;
3097 }
3098 /* Change key db password (-W) (future - change pw to slot?) */
3099 if (certutil.commands[cmd_ChangePassword].activated) {
3100 rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg,
3101 certutil.options[opt_NewPasswordFile].arg);
3102 goto shutdown;
3103 }
3104 /* Reset the a token */
3105 if (certutil.commands[cmd_TokenReset].activated) {
3106 char *sso_pass = "";
3108 if (certutil.options[opt_SSOPass].activated) {
3109 sso_pass = certutil.options[opt_SSOPass].arg;
3110 }
3111 rv = PK11_ResetToken(slot,sso_pass);
3113 goto shutdown;
3114 }
3115 /* Check cert validity against current time (-V) */
3116 if (certutil.commands[cmd_CheckCertValidity].activated) {
3117 /* XXX temporary hack for fips - must log in to get priv key */
3118 if (certutil.options[opt_VerifySig].activated) {
3119 if (slot && PK11_NeedLogin(slot)) {
3120 SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, &pwdata);
3121 if (newrv != SECSuccess) {
3122 SECU_PrintError(progName, "could not authenticate to token %s.",
3123 PK11_GetTokenName(slot));
3124 goto shutdown;
3125 }
3126 }
3127 }
3128 rv = ValidateCert(certHandle, name,
3129 certutil.options[opt_ValidityTime].arg,
3130 certutil.options[opt_Usage].arg,
3131 certutil.options[opt_VerifySig].activated,
3132 certutil.options[opt_DetailedInfo].activated,
3133 certutil.options[opt_ASCIIForIO].activated,
3134 &pwdata);
3135 if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INVALID_ARGS)
3136 SECU_PrintError(progName, "validation failed");
3137 goto shutdown;
3138 }
3140 /*
3141 * Key generation
3142 */
3144 /* These commands may require keygen. */
3145 if (certutil.commands[cmd_CertReq].activated ||
3146 certutil.commands[cmd_CreateAndAddCert].activated ||
3147 certutil.commands[cmd_GenKeyPair].activated) {
3148 if (keysource) {
3149 CERTCertificate *keycert;
3150 keycert = CERT_FindCertByNicknameOrEmailAddr(certHandle, keysource);
3151 if (!keycert) {
3152 keycert = PK11_FindCertFromNickname(keysource, NULL);
3153 if (!keycert) {
3154 SECU_PrintError(progName,
3155 "%s is neither a key-type nor a nickname", keysource);
3156 return SECFailure;
3157 }
3158 }
3159 privkey = PK11_FindKeyByDERCert(slot, keycert, &pwdata);
3160 if (privkey)
3161 pubkey = CERT_ExtractPublicKey(keycert);
3162 if (!pubkey) {
3163 SECU_PrintError(progName,
3164 "Could not get keys from cert %s", keysource);
3165 rv = SECFailure;
3166 CERT_DestroyCertificate(keycert);
3167 goto shutdown;
3168 }
3169 keytype = privkey->keyType;
3170 /* On CertReq for renewal if no subject has been
3171 * specified obtain it from the certificate.
3172 */
3173 if (certutil.commands[cmd_CertReq].activated && !subject) {
3174 subject = CERT_AsciiToName(keycert->subjectName);
3175 if (!subject) {
3176 SECU_PrintError(progName,
3177 "Could not get subject from certificate %s", keysource);
3178 CERT_DestroyCertificate(keycert);
3179 rv = SECFailure;
3180 goto shutdown;
3181 }
3182 }
3183 CERT_DestroyCertificate(keycert);
3184 } else {
3185 privkey =
3186 CERTUTIL_GeneratePrivateKey(keytype, slot, keysize,
3187 publicExponent,
3188 certutil.options[opt_NoiseFile].arg,
3189 &pubkey,
3190 certutil.options[opt_PQGFile].arg,
3191 keyAttrFlags,
3192 keyOpFlagsOn,
3193 keyOpFlagsOff,
3194 &pwdata);
3195 if (privkey == NULL) {
3196 SECU_PrintError(progName, "unable to generate key(s)\n");
3197 rv = SECFailure;
3198 goto shutdown;
3199 }
3200 }
3201 privkey->wincx = &pwdata;
3202 PORT_Assert(pubkey != NULL);
3204 /* If all that was needed was keygen, exit. */
3205 if (certutil.commands[cmd_GenKeyPair].activated) {
3206 rv = SECSuccess;
3207 goto shutdown;
3208 }
3209 }
3211 /* If we need a list of extensions convert the flags into list format */
3212 if (certutil.commands[cmd_CertReq].activated ||
3213 certutil.commands[cmd_CreateAndAddCert].activated ||
3214 certutil.commands[cmd_CreateNewCert].activated) {
3215 certutil_extns[ext_keyUsage].activated =
3216 certutil.options[opt_AddCmdKeyUsageExt].activated;
3217 if (!certutil_extns[ext_keyUsage].activated) {
3218 certutil_extns[ext_keyUsage].activated =
3219 certutil.options[opt_AddKeyUsageExt].activated;
3220 } else {
3221 certutil_extns[ext_keyUsage].arg =
3222 certutil.options[opt_AddCmdKeyUsageExt].arg;
3223 }
3224 certutil_extns[ext_basicConstraint].activated =
3225 certutil.options[opt_AddBasicConstraintExt].activated;
3226 certutil_extns[ext_nameConstraints].activated =
3227 certutil.options[opt_AddNameConstraintsExt].activated;
3228 certutil_extns[ext_authorityKeyID].activated =
3229 certutil.options[opt_AddAuthorityKeyIDExt].activated;
3230 certutil_extns[ext_subjectKeyID].activated =
3231 certutil.options[opt_AddSubjectKeyIDExt].activated;
3232 certutil_extns[ext_CRLDistPts].activated =
3233 certutil.options[opt_AddCRLDistPtsExt].activated;
3234 certutil_extns[ext_NSCertType].activated =
3235 certutil.options[opt_AddCmdNSCertTypeExt].activated;
3236 if (!certutil_extns[ext_NSCertType].activated) {
3237 certutil_extns[ext_NSCertType].activated =
3238 certutil.options[opt_AddNSCertTypeExt].activated;
3239 } else {
3240 certutil_extns[ext_NSCertType].arg =
3241 certutil.options[opt_AddCmdNSCertTypeExt].arg;
3242 }
3244 certutil_extns[ext_extKeyUsage].activated =
3245 certutil.options[opt_AddCmdExtKeyUsageExt].activated;
3246 if (!certutil_extns[ext_extKeyUsage].activated) {
3247 certutil_extns[ext_extKeyUsage].activated =
3248 certutil.options[opt_AddExtKeyUsageExt].activated;
3249 } else {
3250 certutil_extns[ext_extKeyUsage].arg =
3251 certutil.options[opt_AddCmdExtKeyUsageExt].arg;
3252 }
3253 certutil_extns[ext_subjectAltName].activated =
3254 certutil.options[opt_AddSubjectAltNameExt].activated;
3255 if (certutil_extns[ext_subjectAltName].activated) {
3256 certutil_extns[ext_subjectAltName].arg =
3257 certutil.options[opt_AddSubjectAltNameExt].arg;
3258 }
3260 certutil_extns[ext_authInfoAcc].activated =
3261 certutil.options[opt_AddAuthInfoAccExt].activated;
3262 certutil_extns[ext_subjInfoAcc].activated =
3263 certutil.options[opt_AddSubjInfoAccExt].activated;
3264 certutil_extns[ext_certPolicies].activated =
3265 certutil.options[opt_AddCertPoliciesExt].activated;
3266 certutil_extns[ext_policyMappings].activated =
3267 certutil.options[opt_AddPolicyMapExt].activated;
3268 certutil_extns[ext_policyConstr].activated =
3269 certutil.options[opt_AddPolicyConstrExt].activated;
3270 certutil_extns[ext_inhibitAnyPolicy].activated =
3271 certutil.options[opt_AddInhibAnyExt].activated;
3272 }
3274 /* -A -C or -E Read inFile */
3275 if (certutil.commands[cmd_CreateNewCert].activated ||
3276 certutil.commands[cmd_AddCert].activated ||
3277 certutil.commands[cmd_AddEmailCert].activated) {
3278 PRBool isCreate = certutil.commands[cmd_CreateNewCert].activated;
3279 rv = SECU_ReadDERFromFile(isCreate ? &certReqDER : &certDER, inFile,
3280 certutil.options[opt_ASCIIForIO].activated,
3281 PR_TRUE);
3282 if (rv)
3283 goto shutdown;
3284 }
3286 /*
3287 * Certificate request
3288 */
3290 /* Make a cert request (-R). */
3291 if (certutil.commands[cmd_CertReq].activated) {
3292 rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject,
3293 certutil.options[opt_PhoneNumber].arg,
3294 certutil.options[opt_ASCIIForIO].activated,
3295 certutil.options[opt_ExtendedEmailAddrs].arg,
3296 certutil.options[opt_ExtendedDNSNames].arg,
3297 certutil_extns,
3298 (certutil.options[opt_GenericExtensions].activated ?
3299 certutil.options[opt_GenericExtensions].arg : NULL),
3300 &certReqDER);
3301 if (rv)
3302 goto shutdown;
3303 privkey->wincx = &pwdata;
3304 }
3306 /*
3307 * Certificate creation
3308 */
3310 /* If making and adding a cert, create a cert request file first without
3311 * any extensions, then load it with the command line extensions
3312 * and output the cert to another file.
3313 */
3314 if (certutil.commands[cmd_CreateAndAddCert].activated) {
3315 static certutilExtnList nullextnlist = {{PR_FALSE, NULL}};
3316 rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject,
3317 certutil.options[opt_PhoneNumber].arg,
3318 PR_FALSE, /* do not BASE64-encode regardless of -a option */
3319 NULL,
3320 NULL,
3321 nullextnlist,
3322 (certutil.options[opt_GenericExtensions].activated ?
3323 certutil.options[opt_GenericExtensions].arg : NULL),
3324 &certReqDER);
3325 if (rv)
3326 goto shutdown;
3327 privkey->wincx = &pwdata;
3328 }
3330 /* Create a certificate (-C or -S). */
3331 if (certutil.commands[cmd_CreateAndAddCert].activated ||
3332 certutil.commands[cmd_CreateNewCert].activated) {
3333 rv = CreateCert(certHandle, slot,
3334 certutil.options[opt_IssuerName].arg,
3335 &certReqDER, &privkey, &pwdata, hashAlgTag,
3336 serialNumber, warpmonths, validityMonths,
3337 certutil.options[opt_ExtendedEmailAddrs].arg,
3338 certutil.options[opt_ExtendedDNSNames].arg,
3339 certutil.options[opt_ASCIIForIO].activated &&
3340 certutil.commands[cmd_CreateNewCert].activated,
3341 certutil.options[opt_SelfSign].activated,
3342 certutil_extns,
3343 (certutil.options[opt_GenericExtensions].activated ?
3344 certutil.options[opt_GenericExtensions].arg : NULL),
3345 certVersion,
3346 &certDER);
3347 if (rv)
3348 goto shutdown;
3349 }
3351 /*
3352 * Adding a cert to the database (or slot)
3353 */
3355 /* -A -E or -S Add the cert to the DB */
3356 if (certutil.commands[cmd_CreateAndAddCert].activated ||
3357 certutil.commands[cmd_AddCert].activated ||
3358 certutil.commands[cmd_AddEmailCert].activated) {
3359 if (strstr(certutil.options[opt_Trust].arg, "u")) {
3360 fprintf(stderr, "Notice: Trust flag u is set automatically if the "
3361 "private key is present.\n");
3362 }
3363 rv = AddCert(slot, certHandle, name,
3364 certutil.options[opt_Trust].arg,
3365 &certDER,
3366 certutil.commands[cmd_AddEmailCert].activated,&pwdata);
3367 if (rv)
3368 goto shutdown;
3369 }
3371 if (certutil.commands[cmd_CertReq].activated ||
3372 certutil.commands[cmd_CreateNewCert].activated) {
3373 SECItem * item = certutil.commands[cmd_CertReq].activated ? &certReqDER
3374 : &certDER;
3375 PRInt32 written = PR_Write(outFile, item->data, item->len);
3376 if (written < 0 || (PRUint32) written != item->len) {
3377 rv = SECFailure;
3378 }
3379 }
3381 shutdown:
3382 if (slot) {
3383 PK11_FreeSlot(slot);
3384 }
3385 if (privkey) {
3386 SECKEY_DestroyPrivateKey(privkey);
3387 }
3388 if (pubkey) {
3389 SECKEY_DestroyPublicKey(pubkey);
3390 }
3391 if (subject) {
3392 CERT_DestroyName(subject);
3393 }
3394 if (name) {
3395 PL_strfree(name);
3396 }
3397 if (inFile && inFile != PR_STDIN) {
3398 PR_Close(inFile);
3399 }
3400 if (outFile && outFile != PR_STDOUT) {
3401 PR_Close(outFile);
3402 }
3403 SECITEM_FreeItem(&certReqDER, PR_FALSE);
3404 SECITEM_FreeItem(&certDER, PR_FALSE);
3405 if (pwdata.data && pwdata.source == PW_PLAINTEXT) {
3406 /* Allocated by a PL_strdup call in SECU_GetModulePassword. */
3407 PL_strfree(pwdata.data);
3408 }
3410 /* Open the batch command file.
3411 *
3412 * - If -B <command line> option is specified, the contents in the
3413 * command file will be interpreted as subsequent certutil
3414 * commands to be executed in the current certutil process
3415 * context after the current certutil command has been executed.
3416 * - Each line in the command file consists of the command
3417 * line arguments for certutil.
3418 * - The -d <configdir> option will be ignored if specified in the
3419 * command file.
3420 * - Quoting with double quote characters ("...") is supported
3421 * to allow white space in a command line argument. The
3422 * double quote character cannot be escaped and quoting cannot
3423 * be nested in this version.
3424 * - each line in the batch file is limited to 512 characters
3425 */
3427 if ((SECSuccess == rv) && certutil.commands[cmd_Batch].activated) {
3428 FILE* batchFile = NULL;
3429 char *nextcommand = NULL;
3430 PRInt32 cmd_len = 0, buf_size = 0;
3431 static const int increment = 512;
3433 if (!certutil.options[opt_InputFile].activated ||
3434 !certutil.options[opt_InputFile].arg) {
3435 PR_fprintf(PR_STDERR,
3436 "%s: no batch input file specified.\n",
3437 progName);
3438 return 255;
3439 }
3440 batchFile = fopen(certutil.options[opt_InputFile].arg, "r");
3441 if (!batchFile) {
3442 PR_fprintf(PR_STDERR,
3443 "%s: unable to open \"%s\" for reading (%ld, %ld).\n",
3444 progName, certutil.options[opt_InputFile].arg,
3445 PR_GetError(), PR_GetOSError());
3446 return 255;
3447 }
3448 /* read and execute command-lines in a loop */
3449 while ( SECSuccess == rv ) {
3450 PRBool invalid = PR_FALSE;
3451 int newargc = 2;
3452 char* space = NULL;
3453 char* nextarg = NULL;
3454 char** newargv = NULL;
3455 char* crlf;
3457 if (cmd_len + increment > buf_size) {
3458 char * new_buf;
3459 buf_size += increment;
3460 new_buf = PORT_Realloc(nextcommand, buf_size);
3461 if (!new_buf) {
3462 PR_fprintf(PR_STDERR, "%s: PORT_Realloc(%ld) failed\n",
3463 progName, buf_size);
3464 break;
3465 }
3466 nextcommand = new_buf;
3467 nextcommand[cmd_len] = '\0';
3468 }
3469 if (!fgets(nextcommand + cmd_len, buf_size - cmd_len, batchFile)) {
3470 break;
3471 }
3472 crlf = PORT_Strrchr(nextcommand, '\n');
3473 if (crlf) {
3474 *crlf = '\0';
3475 }
3476 cmd_len = strlen(nextcommand);
3477 if (cmd_len && nextcommand[cmd_len - 1] == '\\') {
3478 nextcommand[--cmd_len] = '\0';
3479 continue;
3480 }
3482 /* we now need to split the command into argc / argv format */
3484 newargv = PORT_Alloc(sizeof(char*)*(newargc+1));
3485 newargv[0] = progName;
3486 newargv[1] = nextcommand;
3487 nextarg = nextcommand;
3488 while ((space = PORT_Strpbrk(nextarg, " \f\n\r\t\v")) ) {
3489 while (isspace(*space) ) {
3490 *space = '\0';
3491 space ++;
3492 }
3493 if (*space == '\0') {
3494 break;
3495 } else if (*space != '\"') {
3496 nextarg = space;
3497 } else {
3498 char* closingquote = strchr(space+1, '\"');
3499 if (closingquote) {
3500 *closingquote = '\0';
3501 space++;
3502 nextarg = closingquote+1;
3503 } else {
3504 invalid = PR_TRUE;
3505 nextarg = space;
3506 }
3507 }
3508 newargc++;
3509 newargv = PORT_Realloc(newargv, sizeof(char*)*(newargc+1));
3510 newargv[newargc-1] = space;
3511 }
3512 newargv[newargc] = NULL;
3514 /* invoke next command */
3515 if (PR_TRUE == invalid) {
3516 PR_fprintf(PR_STDERR, "Missing closing quote in batch command :\n%s\nNot executed.\n",
3517 nextcommand);
3518 rv = SECFailure;
3519 } else {
3520 if (0 != certutil_main(newargc, newargv, PR_FALSE) )
3521 rv = SECFailure;
3522 }
3523 PORT_Free(newargv);
3524 cmd_len = 0;
3525 nextcommand[0] = '\0';
3526 }
3527 PORT_Free(nextcommand);
3528 fclose(batchFile);
3529 }
3531 if ((initialized == PR_TRUE) && NSS_Shutdown() != SECSuccess) {
3532 exit(1);
3533 }
3534 if (rv == SECSuccess) {
3535 return 0;
3536 } else {
3537 return 255;
3538 }
3539 }
3541 int
3542 main(int argc, char **argv)
3543 {
3544 int rv = certutil_main(argc, argv, PR_TRUE);
3545 PL_ArenaFinish();
3546 PR_Cleanup();
3547 return rv;
3548 }