nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / signtool / certgen.c
blobd2553afed840e0bd74bce371dfc6b12b2b8a2916
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "signtool.h"
39 #include "secoid.h"
40 #include "cryptohi.h"
41 #include "certdb.h"
43 static char *GetSubjectFromUser(unsigned long serial);
44 static CERTCertificate*GenerateSelfSignedObjectSigningCert(char *nickname,
45 CERTCertDBHandle *db, char *subject, unsigned long serial, int keysize,
46 char *token);
47 static SECStatus ChangeTrustAttributes(CERTCertDBHandle *db,
48 CERTCertificate *cert, char *trusts);
49 static SECStatus set_cert_type(CERTCertificate *cert, unsigned int type);
50 static SECItem *sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk);
51 static CERTCertificate*install_cert(CERTCertDBHandle *db, SECItem *derCert,
52 char *nickname);
53 static SECStatus GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
54 SECKEYPrivateKey **privk, int keysize);
55 static CERTCertificateRequest*make_cert_request(char *subject,
56 SECKEYPublicKey *pubk);
57 static CERTCertificate *make_cert(CERTCertificateRequest *req,
58 unsigned long serial, CERTName *ca_subject);
59 static void output_ca_cert (CERTCertificate *cert, CERTCertDBHandle *db);
62 /***********************************************************************
64 * G e n e r a t e C e r t
66 * Runs the whole process of creating a new cert, getting info from the
67 * user, etc.
69 int
70 GenerateCert(char *nickname, int keysize, char *token)
72 CERTCertDBHandle * db;
73 CERTCertificate * cert;
74 char *subject;
75 unsigned long serial;
76 char stdinbuf[160];
78 /* Print warning about having the browser open */
79 PR_fprintf(PR_STDOUT /*always go to console*/,
80 "\nWARNING: Performing this operation while the browser is running could cause"
81 "\ncorruption of your security databases. If the browser is currently running,"
82 "\nyou should exit the browser before continuing this operation. Enter "
83 "\n\"y\" to continue, or anything else to abort: ");
84 pr_fgets(stdinbuf, 160, PR_STDIN);
85 PR_fprintf(PR_STDOUT, "\n");
86 if (tolower(stdinbuf[0]) != 'y') {
87 PR_fprintf(errorFD, "Operation aborted at user's request.\n");
88 errorCount++;
89 return - 1;
92 db = CERT_GetDefaultCertDB();
93 if (!db) {
94 FatalError("Unable to open certificate database");
97 if (PK11_FindCertFromNickname(nickname, NULL)) {
98 PR_fprintf(errorFD,
99 "ERROR: Certificate with nickname \"%s\" already exists in database. You\n"
100 "must choose a different nickname.\n", nickname);
101 errorCount++;
102 exit(ERRX);
105 LL_L2UI(serial, PR_Now());
107 subject = GetSubjectFromUser(serial);
109 cert = GenerateSelfSignedObjectSigningCert(nickname, db, subject,
110 serial, keysize, token);
112 if (cert) {
113 output_ca_cert(cert, db);
114 CERT_DestroyCertificate(cert);
117 PORT_Free(subject);
118 return 0;
122 #undef VERBOSE_PROMPTS
124 /*********************************************************************8
125 * G e t S u b j e c t F r o m U s e r
127 * Construct the subject information line for a certificate by querying
128 * the user on stdin.
130 static char *
131 GetSubjectFromUser(unsigned long serial)
133 char buf[STDIN_BUF_SIZE];
134 char common_name_buf[STDIN_BUF_SIZE];
135 char *common_name, *state, *orgunit, *country, *org, *locality;
136 char *email, *uid;
137 char *subject;
138 char *cp;
139 int subjectlen = 0;
141 common_name = state = orgunit = country = org = locality = email =
142 uid = subject = NULL;
144 /* Get subject information */
145 PR_fprintf(PR_STDOUT,
146 "\nEnter certificate information. All fields are optional. Acceptable\n"
147 "characters are numbers, letters, spaces, and apostrophes.\n");
149 #ifdef VERBOSE_PROMPTS
150 PR_fprintf(PR_STDOUT, "\nCOMMON NAME\n"
151 "Enter the full name you want to give your certificate. (Example: Test-Only\n"
152 "Object Signing Certificate)\n"
153 "-->");
154 #else
155 PR_fprintf(PR_STDOUT, "certificate common name: ");
156 #endif
157 fgets(buf, STDIN_BUF_SIZE, stdin);
158 cp = chop(buf);
159 if (*cp == '\0') {
160 sprintf(common_name_buf, "%s (%lu)", DEFAULT_COMMON_NAME,
161 serial);
162 cp = common_name_buf;
164 common_name = PORT_ZAlloc(strlen(cp) + 6);
165 if (!common_name) {
166 out_of_memory();
168 sprintf(common_name, "CN=%s, ", cp);
169 subjectlen += strlen(common_name);
171 #ifdef VERBOSE_PROMPTS
172 PR_fprintf(PR_STDOUT, "\nORGANIZATION NAME\n"
173 "Enter the name of your organization. For example, this could be the name\n"
174 "of your company.\n"
175 "-->");
176 #else
177 PR_fprintf(PR_STDOUT, "organization: ");
178 #endif
179 fgets(buf, STDIN_BUF_SIZE, stdin);
180 cp = chop(buf);
181 if (*cp != '\0') {
182 org = PORT_ZAlloc(strlen(cp) + 5);
183 if (!org) {
184 out_of_memory();
186 sprintf(org, "O=%s, ", cp);
187 subjectlen += strlen(org);
190 #ifdef VERBOSE_PROMPTS
191 PR_fprintf(PR_STDOUT, "\nORGANIZATION UNIT\n"
192 "Enter the name of your organization unit. For example, this could be the\n"
193 "name of your department.\n"
194 "-->");
195 #else
196 PR_fprintf(PR_STDOUT, "organization unit: ");
197 #endif
198 fgets(buf, STDIN_BUF_SIZE, stdin);
199 cp = chop(buf);
200 if (*cp != '\0') {
201 orgunit = PORT_ZAlloc(strlen(cp) + 6);
202 if (!orgunit) {
203 out_of_memory();
205 sprintf(orgunit, "OU=%s, ", cp);
206 subjectlen += strlen(orgunit);
209 #ifdef VERBOSE_PROMPTS
210 PR_fprintf(PR_STDOUT, "\nSTATE\n"
211 "Enter the name of your state or province.\n"
212 "-->");
213 #else
214 PR_fprintf(PR_STDOUT, "state or province: ");
215 #endif
216 fgets(buf, STDIN_BUF_SIZE, stdin);
217 cp = chop(buf);
218 if (*cp != '\0') {
219 state = PORT_ZAlloc(strlen(cp) + 6);
220 if (!state) {
221 out_of_memory();
223 sprintf(state, "ST=%s, ", cp);
224 subjectlen += strlen(state);
227 #ifdef VERBOSE_PROMPTS
228 PR_fprintf(PR_STDOUT, "\nCOUNTRY\n"
229 "Enter the 2-character abbreviation for the name of your country.\n"
230 "-->");
231 #else
232 PR_fprintf(PR_STDOUT, "country (must be exactly 2 characters): ");
233 #endif
234 fgets(buf, STDIN_BUF_SIZE, stdin);
235 cp = chop(cp);
236 if (strlen(cp) != 2) {
237 *cp = '\0'; /* country code must be 2 chars */
239 if (*cp != '\0') {
240 country = PORT_ZAlloc(strlen(cp) + 5);
241 if (!country) {
242 out_of_memory();
244 sprintf(country, "C=%s, ", cp);
245 subjectlen += strlen(country);
248 #ifdef VERBOSE_PROMPTS
249 PR_fprintf(PR_STDOUT, "\nUSERNAME\n"
250 "Enter your system username or UID\n"
251 "-->");
252 #else
253 PR_fprintf(PR_STDOUT, "username: ");
254 #endif
255 fgets(buf, STDIN_BUF_SIZE, stdin);
256 cp = chop(buf);
257 if (*cp != '\0') {
258 uid = PORT_ZAlloc(strlen(cp) + 7);
259 if (!uid) {
260 out_of_memory();
262 sprintf(uid, "UID=%s, ", cp);
263 subjectlen += strlen(uid);
266 #ifdef VERBOSE_PROMPTS
267 PR_fprintf(PR_STDOUT, "\nEMAIL ADDRESS\n"
268 "Enter your email address.\n"
269 "-->");
270 #else
271 PR_fprintf(PR_STDOUT, "email address: ");
272 #endif
273 fgets(buf, STDIN_BUF_SIZE, stdin);
274 cp = chop(buf);
275 if (*cp != '\0') {
276 email = PORT_ZAlloc(strlen(cp) + 5);
277 if (!email) {
278 out_of_memory();
280 sprintf(email, "E=%s,", cp);
281 subjectlen += strlen(email);
284 subjectlen++;
286 subject = PORT_ZAlloc(subjectlen);
287 if (!subject) {
288 out_of_memory();
291 sprintf(subject, "%s%s%s%s%s%s%s",
292 common_name ? common_name : "",
293 org ? org : "",
294 orgunit ? orgunit : "",
295 state ? state : "",
296 country ? country : "",
297 uid ? uid : "",
298 email ? email : ""
300 if ( (strlen(subject) > 1) && (subject[strlen(subject)-1] == ' ') ) {
301 subject[strlen(subject)-2] = '\0';
304 PORT_Free(common_name);
305 PORT_Free(org);
306 PORT_Free(orgunit);
307 PORT_Free(state);
308 PORT_Free(country);
309 PORT_Free(uid);
310 PORT_Free(email);
312 return subject;
316 /**************************************************************************
318 * G e n e r a t e S e l f S i g n e d O b j e c t S i g n i n g C e r t
319 * *phew*^
322 static CERTCertificate*
323 GenerateSelfSignedObjectSigningCert(char *nickname, CERTCertDBHandle *db,
324 char *subject, unsigned long serial, int keysize, char *token)
326 CERTCertificate * cert, *temp_cert;
327 SECItem * derCert;
328 CERTCertificateRequest * req;
330 PK11SlotInfo * slot = NULL;
331 SECKEYPrivateKey * privk = NULL;
332 SECKEYPublicKey * pubk = NULL;
334 if ( token ) {
335 slot = PK11_FindSlotByName(token);
336 } else {
337 slot = PK11_GetInternalKeySlot();
340 if (slot == NULL) {
341 PR_fprintf(errorFD, "Can't find PKCS11 slot %s\n",
342 token ? token : "");
343 errorCount++;
344 exit (ERRX);
347 if ( GenerateKeyPair(slot, &pubk, &privk, keysize) != SECSuccess) {
348 FatalError("Error generating keypair.");
350 req = make_cert_request (subject, pubk);
351 temp_cert = make_cert (req, serial, &req->subject);
352 if (set_cert_type(temp_cert,
353 NS_CERT_TYPE_OBJECT_SIGNING | NS_CERT_TYPE_OBJECT_SIGNING_CA)
354 != SECSuccess) {
355 FatalError("Unable to set cert type");
358 derCert = sign_cert (temp_cert, privk);
359 cert = install_cert(db, derCert, nickname);
360 if (ChangeTrustAttributes(db, cert, ",,uC") != SECSuccess) {
361 FatalError("Unable to change trust on generated certificate");
364 /* !!! Free memory ? !!! */
365 PK11_FreeSlot(slot);
366 SECKEY_DestroyPrivateKey(privk);
367 SECKEY_DestroyPublicKey(pubk);
369 return cert;
373 /**************************************************************************
375 * C h a n g e T r u s t A t t r i b u t e s
377 static SECStatus
378 ChangeTrustAttributes(CERTCertDBHandle *db, CERTCertificate *cert, char *trusts)
381 CERTCertTrust * trust;
383 if (!db || !cert || !trusts) {
384 PR_fprintf(errorFD, "ChangeTrustAttributes got incomplete arguments.\n");
385 errorCount++;
386 return SECFailure;
389 trust = (CERTCertTrust * ) PORT_ZAlloc(sizeof(CERTCertTrust));
390 if (!trust) {
391 PR_fprintf(errorFD, "ChangeTrustAttributes unable to allocate "
392 "CERTCertTrust\n");
393 errorCount++;
394 return SECFailure;
397 if ( CERT_DecodeTrustString(trust, trusts) ) {
398 return SECFailure;
401 if ( CERT_ChangeCertTrust(db, cert, trust) ) {
402 PR_fprintf(errorFD, "unable to modify trust attributes for cert %s\n",
403 cert->nickname ? cert->nickname : "");
404 errorCount++;
405 return SECFailure;
408 return SECSuccess;
412 /*************************************************************************
414 * s e t _ c e r t _ t y p e
416 static SECStatus
417 set_cert_type(CERTCertificate *cert, unsigned int type)
419 void *context;
420 SECStatus status = SECSuccess;
421 SECItem certType;
422 char ctype;
424 context = CERT_StartCertExtensions(cert);
426 certType.type = siBuffer;
427 certType.data = (unsigned char * ) &ctype;
428 certType.len = 1;
429 ctype = (unsigned char)type;
430 if (CERT_EncodeAndAddBitStrExtension(context, SEC_OID_NS_CERT_EXT_CERT_TYPE,
431 &certType, PR_TRUE /*critical*/) != SECSuccess) {
432 status = SECFailure;
435 if (CERT_FinishExtensions(context) != SECSuccess) {
436 status = SECFailure;
439 return status;
443 /********************************************************************
445 * s i g n _ c e r t
447 static SECItem *
448 sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk)
450 SECStatus rv;
452 SECItem der2;
453 SECItem * result2;
455 void *dummy;
456 SECOidTag alg = SEC_OID_UNKNOWN;
458 alg = SEC_GetSignatureAlgorithmOidTag(privk->keyType, SEC_OID_UNKNOWN);
459 if (alg == SEC_OID_UNKNOWN) {
460 FatalError("Unknown key type");
463 rv = SECOID_SetAlgorithmID (cert->arena, &cert->signature, alg, 0);
465 if (rv != SECSuccess) {
466 PR_fprintf(errorFD, "%s: unable to set signature alg id\n",
467 PROGRAM_NAME);
468 errorCount++;
469 exit (ERRX);
472 der2.len = 0;
473 der2.data = NULL;
475 dummy = SEC_ASN1EncodeItem
476 (cert->arena, &der2, cert, CERT_CertificateTemplate);
478 if (rv != SECSuccess) {
479 PR_fprintf(errorFD, "%s: error encoding cert\n", PROGRAM_NAME);
480 errorCount++;
481 exit (ERRX);
484 result2 = (SECItem * ) PORT_ArenaZAlloc (cert->arena, sizeof (SECItem));
485 if (result2 == NULL)
486 out_of_memory();
488 rv = SEC_DerSignData
489 (cert->arena, result2, der2.data, der2.len, privk, alg);
491 if (rv != SECSuccess) {
492 PR_fprintf(errorFD, "can't sign encoded certificate data\n");
493 errorCount++;
494 exit (ERRX);
495 } else if (verbosity >= 0) {
496 PR_fprintf(outputFD, "certificate has been signed\n");
499 cert->derCert = *result2;
501 return result2;
505 /*********************************************************************
507 * i n s t a l l _ c e r t
509 * Installs the cert in the permanent database.
511 static CERTCertificate*
512 install_cert(CERTCertDBHandle *db, SECItem *derCert, char *nickname)
514 CERTCertificate * newcert;
515 PK11SlotInfo * newSlot;
517 newcert = CERT_DecodeDERCertificate(derCert, PR_TRUE, NULL);
519 if (newcert == NULL) {
520 PR_fprintf(errorFD, "%s: can't create new certificate\n",
521 PROGRAM_NAME);
522 errorCount++;
523 exit (ERRX);
526 newSlot = PK11_ImportCertForKey(newcert, nickname, NULL /*wincx*/);
527 if ( newSlot == NULL ) {
528 PR_fprintf(errorFD, "Unable to install certificate\n");
529 errorCount++;
530 exit(ERRX);
532 PK11_FreeSlot(newSlot);
534 if (verbosity >= 0) {
535 PR_fprintf(outputFD, "certificate \"%s\" added to database\n",
536 nickname);
539 return newcert;
543 /******************************************************************
545 * G e n e r a t e K e y P a i r
547 static SECStatus
548 GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
549 SECKEYPrivateKey **privk, int keysize)
552 PK11RSAGenParams rsaParams;
554 if ( keysize == -1 ) {
555 rsaParams.keySizeInBits = DEFAULT_RSA_KEY_SIZE;
556 } else {
557 rsaParams.keySizeInBits = keysize;
559 rsaParams.pe = 0x10001;
561 if (PK11_Authenticate( slot, PR_FALSE /*loadCerts*/, NULL /*wincx*/)
562 != SECSuccess) {
563 SECU_PrintError(progName, "failure authenticating to key database.\n");
564 exit(ERRX);
567 *privk = PK11_GenerateKeyPair (slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams,
569 pubk, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, NULL /*wincx*/ );
571 if (*privk != NULL && *pubk != NULL) {
572 if (verbosity >= 0) {
573 PR_fprintf(outputFD, "generated public/private key pair\n");
575 } else {
576 SECU_PrintError(progName, "failure generating key pair\n");
577 exit (ERRX);
580 return SECSuccess;
585 /******************************************************************
587 * m a k e _ c e r t _ r e q u e s t
589 static CERTCertificateRequest*
590 make_cert_request(char *subject, SECKEYPublicKey *pubk)
592 CERTName * subj;
593 CERTSubjectPublicKeyInfo * spki;
595 CERTCertificateRequest * req;
597 /* Create info about public key */
598 spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
599 if (!spki) {
600 SECU_PrintError(progName, "unable to create subject public key");
601 exit (ERRX);
604 subj = CERT_AsciiToName (subject);
605 if (subj == NULL) {
606 FatalError("Invalid data in certificate description");
609 /* Generate certificate request */
610 req = CERT_CreateCertificateRequest(subj, spki, 0);
611 if (!req) {
612 SECU_PrintError(progName, "unable to make certificate request");
613 exit (ERRX);
616 if (verbosity >= 0) {
617 PR_fprintf(outputFD, "certificate request generated\n");
620 return req;
624 /******************************************************************
626 * m a k e _ c e r t
628 static CERTCertificate *
629 make_cert(CERTCertificateRequest *req, unsigned long serial,
630 CERTName *ca_subject)
632 CERTCertificate * cert;
634 CERTValidity * validity = NULL;
636 PRTime now, after;
637 PRExplodedTime printableTime;
639 now = PR_Now();
640 PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
642 printableTime.tm_month += 3;
643 after = PR_ImplodeTime (&printableTime);
645 validity = CERT_CreateValidity (now, after);
647 if (validity == NULL) {
648 PR_fprintf(errorFD, "%s: error creating certificate validity\n",
649 PROGRAM_NAME);
650 errorCount++;
651 exit (ERRX);
654 cert = CERT_CreateCertificate
655 (serial, ca_subject, validity, req);
657 if (cert == NULL) {
658 /* should probably be more precise here */
659 PR_fprintf(errorFD, "%s: error while generating certificate\n",
660 PROGRAM_NAME);
661 errorCount++;
662 exit (ERRX);
665 return cert;
669 /*************************************************************************
671 * o u t p u t _ c a _ c e r t
673 static void
674 output_ca_cert (CERTCertificate *cert, CERTCertDBHandle *db)
676 FILE * out;
678 SECItem * encodedCertChain;
679 SEC_PKCS7ContentInfo * certChain;
680 char *filename;
682 /* the raw */
684 filename = PORT_ZAlloc(strlen(DEFAULT_X509_BASENAME) + 8);
685 if (!filename)
686 out_of_memory();
688 sprintf(filename, "%s.raw", DEFAULT_X509_BASENAME);
689 if ((out = fopen (filename, "wb")) == NULL) {
690 PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
691 filename);
692 errorCount++;
693 exit(ERRX);
696 certChain = SEC_PKCS7CreateCertsOnly (cert, PR_TRUE, db);
697 encodedCertChain
698 = SEC_PKCS7EncodeItem (NULL, NULL, certChain, NULL, NULL, NULL);
699 SEC_PKCS7DestroyContentInfo (certChain);
701 if (encodedCertChain) {
702 fprintf(out, "Content-type: application/x-x509-ca-cert\n\n");
703 fwrite (encodedCertChain->data, 1, encodedCertChain->len,
704 out);
705 SECITEM_FreeItem(encodedCertChain, PR_TRUE);
706 } else {
707 PR_fprintf(errorFD, "%s: Can't DER encode this certificate\n",
708 PROGRAM_NAME);
709 errorCount++;
710 exit(ERRX);
713 fclose (out);
715 /* and the cooked */
717 sprintf(filename, "%s.cacert", DEFAULT_X509_BASENAME);
718 if ((out = fopen (filename, "wb")) == NULL) {
719 PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
720 filename);
721 errorCount++;
722 return;
725 fprintf (out, "%s\n%s\n%s\n",
726 NS_CERT_HEADER,
727 BTOA_DataToAscii (cert->derCert.data, cert->derCert.len),
728 NS_CERT_TRAILER);
730 fclose (out);
732 if (verbosity >= 0) {
733 PR_fprintf(outputFD, "Exported certificate to %s.raw and %s.cacert.\n",
734 DEFAULT_X509_BASENAME, DEFAULT_X509_BASENAME);