security/nss/cmd/signtool/certgen.c

branch
TOR_BUG_9701
changeset 3
141e0f1194b1
equal deleted inserted replaced
-1:000000000000 0:0e96edd7a903
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "signtool.h"
6
7 #include "secoid.h"
8 #include "cryptohi.h"
9 #include "certdb.h"
10
11 static char *GetSubjectFromUser(unsigned long serial);
12 static CERTCertificate*GenerateSelfSignedObjectSigningCert(char *nickname,
13 CERTCertDBHandle *db, char *subject, unsigned long serial, int keysize,
14 char *token);
15 static SECStatus ChangeTrustAttributes(CERTCertDBHandle *db,
16 CERTCertificate *cert, char *trusts);
17 static SECStatus set_cert_type(CERTCertificate *cert, unsigned int type);
18 static SECItem *sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk);
19 static CERTCertificate*install_cert(CERTCertDBHandle *db, SECItem *derCert,
20 char *nickname);
21 static SECStatus GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
22 SECKEYPrivateKey **privk, int keysize);
23 static CERTCertificateRequest*make_cert_request(char *subject,
24 SECKEYPublicKey *pubk);
25 static CERTCertificate *make_cert(CERTCertificateRequest *req,
26 unsigned long serial, CERTName *ca_subject);
27 static void output_ca_cert (CERTCertificate *cert, CERTCertDBHandle *db);
28
29
30 /***********************************************************************
31 *
32 * G e n e r a t e C e r t
33 *
34 * Runs the whole process of creating a new cert, getting info from the
35 * user, etc.
36 */
37 int
38 GenerateCert(char *nickname, int keysize, char *token)
39 {
40 CERTCertDBHandle * db;
41 CERTCertificate * cert;
42 char *subject;
43 unsigned long serial;
44 char stdinbuf[160];
45
46 /* Print warning about having the browser open */
47 PR_fprintf(PR_STDOUT /*always go to console*/,
48 "\nWARNING: Performing this operation while the browser is running could cause"
49 "\ncorruption of your security databases. If the browser is currently running,"
50 "\nyou should exit the browser before continuing this operation. Enter "
51 "\n\"y\" to continue, or anything else to abort: ");
52 pr_fgets(stdinbuf, 160, PR_STDIN);
53 PR_fprintf(PR_STDOUT, "\n");
54 if (tolower(stdinbuf[0]) != 'y') {
55 PR_fprintf(errorFD, "Operation aborted at user's request.\n");
56 errorCount++;
57 return - 1;
58 }
59
60 db = CERT_GetDefaultCertDB();
61 if (!db) {
62 FatalError("Unable to open certificate database");
63 }
64
65 if (PK11_FindCertFromNickname(nickname, &pwdata)) {
66 PR_fprintf(errorFD,
67 "ERROR: Certificate with nickname \"%s\" already exists in database. You\n"
68 "must choose a different nickname.\n", nickname);
69 errorCount++;
70 exit(ERRX);
71 }
72
73 LL_L2UI(serial, PR_Now());
74
75 subject = GetSubjectFromUser(serial);
76
77 cert = GenerateSelfSignedObjectSigningCert(nickname, db, subject,
78 serial, keysize, token);
79
80 if (cert) {
81 output_ca_cert(cert, db);
82 CERT_DestroyCertificate(cert);
83 }
84
85 PORT_Free(subject);
86 return 0;
87 }
88
89
90 #undef VERBOSE_PROMPTS
91
92 /*********************************************************************8
93 * G e t S u b j e c t F r o m U s e r
94 *
95 * Construct the subject information line for a certificate by querying
96 * the user on stdin.
97 */
98 static char *
99 GetSubjectFromUser(unsigned long serial)
100 {
101 char buf[STDIN_BUF_SIZE];
102 char common_name_buf[STDIN_BUF_SIZE];
103 char *common_name, *state, *orgunit, *country, *org, *locality;
104 char *email, *uid;
105 char *subject;
106 char *cp;
107 int subjectlen = 0;
108
109 common_name = state = orgunit = country = org = locality = email =
110 uid = subject = NULL;
111
112 /* Get subject information */
113 PR_fprintf(PR_STDOUT,
114 "\nEnter certificate information. All fields are optional. Acceptable\n"
115 "characters are numbers, letters, spaces, and apostrophes.\n");
116
117 #ifdef VERBOSE_PROMPTS
118 PR_fprintf(PR_STDOUT, "\nCOMMON NAME\n"
119 "Enter the full name you want to give your certificate. (Example: Test-Only\n"
120 "Object Signing Certificate)\n"
121 "-->");
122 #else
123 PR_fprintf(PR_STDOUT, "certificate common name: ");
124 #endif
125 fgets(buf, STDIN_BUF_SIZE, stdin);
126 cp = chop(buf);
127 if (*cp == '\0') {
128 sprintf(common_name_buf, "%s (%lu)", DEFAULT_COMMON_NAME,
129 serial);
130 cp = common_name_buf;
131 }
132 common_name = PORT_ZAlloc(strlen(cp) + 6);
133 if (!common_name) {
134 out_of_memory();
135 }
136 sprintf(common_name, "CN=%s, ", cp);
137 subjectlen += strlen(common_name);
138
139 #ifdef VERBOSE_PROMPTS
140 PR_fprintf(PR_STDOUT, "\nORGANIZATION NAME\n"
141 "Enter the name of your organization. For example, this could be the name\n"
142 "of your company.\n"
143 "-->");
144 #else
145 PR_fprintf(PR_STDOUT, "organization: ");
146 #endif
147 fgets(buf, STDIN_BUF_SIZE, stdin);
148 cp = chop(buf);
149 if (*cp != '\0') {
150 org = PORT_ZAlloc(strlen(cp) + 5);
151 if (!org) {
152 out_of_memory();
153 }
154 sprintf(org, "O=%s, ", cp);
155 subjectlen += strlen(org);
156 }
157
158 #ifdef VERBOSE_PROMPTS
159 PR_fprintf(PR_STDOUT, "\nORGANIZATION UNIT\n"
160 "Enter the name of your organization unit. For example, this could be the\n"
161 "name of your department.\n"
162 "-->");
163 #else
164 PR_fprintf(PR_STDOUT, "organization unit: ");
165 #endif
166 fgets(buf, STDIN_BUF_SIZE, stdin);
167 cp = chop(buf);
168 if (*cp != '\0') {
169 orgunit = PORT_ZAlloc(strlen(cp) + 6);
170 if (!orgunit) {
171 out_of_memory();
172 }
173 sprintf(orgunit, "OU=%s, ", cp);
174 subjectlen += strlen(orgunit);
175 }
176
177 #ifdef VERBOSE_PROMPTS
178 PR_fprintf(PR_STDOUT, "\nSTATE\n"
179 "Enter the name of your state or province.\n"
180 "-->");
181 #else
182 PR_fprintf(PR_STDOUT, "state or province: ");
183 #endif
184 fgets(buf, STDIN_BUF_SIZE, stdin);
185 cp = chop(buf);
186 if (*cp != '\0') {
187 state = PORT_ZAlloc(strlen(cp) + 6);
188 if (!state) {
189 out_of_memory();
190 }
191 sprintf(state, "ST=%s, ", cp);
192 subjectlen += strlen(state);
193 }
194
195 #ifdef VERBOSE_PROMPTS
196 PR_fprintf(PR_STDOUT, "\nCOUNTRY\n"
197 "Enter the 2-character abbreviation for the name of your country.\n"
198 "-->");
199 #else
200 PR_fprintf(PR_STDOUT, "country (must be exactly 2 characters): ");
201 #endif
202 fgets(buf, STDIN_BUF_SIZE, stdin);
203 cp = chop(cp);
204 if (strlen(cp) != 2) {
205 *cp = '\0'; /* country code must be 2 chars */
206 }
207 if (*cp != '\0') {
208 country = PORT_ZAlloc(strlen(cp) + 5);
209 if (!country) {
210 out_of_memory();
211 }
212 sprintf(country, "C=%s, ", cp);
213 subjectlen += strlen(country);
214 }
215
216 #ifdef VERBOSE_PROMPTS
217 PR_fprintf(PR_STDOUT, "\nUSERNAME\n"
218 "Enter your system username or UID\n"
219 "-->");
220 #else
221 PR_fprintf(PR_STDOUT, "username: ");
222 #endif
223 fgets(buf, STDIN_BUF_SIZE, stdin);
224 cp = chop(buf);
225 if (*cp != '\0') {
226 uid = PORT_ZAlloc(strlen(cp) + 7);
227 if (!uid) {
228 out_of_memory();
229 }
230 sprintf(uid, "UID=%s, ", cp);
231 subjectlen += strlen(uid);
232 }
233
234 #ifdef VERBOSE_PROMPTS
235 PR_fprintf(PR_STDOUT, "\nEMAIL ADDRESS\n"
236 "Enter your email address.\n"
237 "-->");
238 #else
239 PR_fprintf(PR_STDOUT, "email address: ");
240 #endif
241 fgets(buf, STDIN_BUF_SIZE, stdin);
242 cp = chop(buf);
243 if (*cp != '\0') {
244 email = PORT_ZAlloc(strlen(cp) + 5);
245 if (!email) {
246 out_of_memory();
247 }
248 sprintf(email, "E=%s,", cp);
249 subjectlen += strlen(email);
250 }
251
252 subjectlen++;
253
254 subject = PORT_ZAlloc(subjectlen);
255 if (!subject) {
256 out_of_memory();
257 }
258
259 sprintf(subject, "%s%s%s%s%s%s%s",
260 common_name ? common_name : "",
261 org ? org : "",
262 orgunit ? orgunit : "",
263 state ? state : "",
264 country ? country : "",
265 uid ? uid : "",
266 email ? email : ""
267 );
268 if ( (strlen(subject) > 1) && (subject[strlen(subject)-1] == ' ') ) {
269 subject[strlen(subject)-2] = '\0';
270 }
271
272 PORT_Free(common_name);
273 PORT_Free(org);
274 PORT_Free(orgunit);
275 PORT_Free(state);
276 PORT_Free(country);
277 PORT_Free(uid);
278 PORT_Free(email);
279
280 return subject;
281 }
282
283
284 /**************************************************************************
285 *
286 * 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
287 * *phew*^
288 *
289 */
290 static CERTCertificate*
291 GenerateSelfSignedObjectSigningCert(char *nickname, CERTCertDBHandle *db,
292 char *subject, unsigned long serial, int keysize, char *token)
293 {
294 CERTCertificate * cert, *temp_cert;
295 SECItem * derCert;
296 CERTCertificateRequest * req;
297
298 PK11SlotInfo * slot = NULL;
299 SECKEYPrivateKey * privk = NULL;
300 SECKEYPublicKey * pubk = NULL;
301
302 if ( token ) {
303 slot = PK11_FindSlotByName(token);
304 } else {
305 slot = PK11_GetInternalKeySlot();
306 }
307
308 if (slot == NULL) {
309 PR_fprintf(errorFD, "Can't find PKCS11 slot %s\n",
310 token ? token : "");
311 errorCount++;
312 exit (ERRX);
313 }
314
315 if ( GenerateKeyPair(slot, &pubk, &privk, keysize) != SECSuccess) {
316 FatalError("Error generating keypair.");
317 }
318 req = make_cert_request (subject, pubk);
319 temp_cert = make_cert (req, serial, &req->subject);
320 if (set_cert_type(temp_cert,
321 NS_CERT_TYPE_OBJECT_SIGNING | NS_CERT_TYPE_OBJECT_SIGNING_CA)
322 != SECSuccess) {
323 FatalError("Unable to set cert type");
324 }
325
326 derCert = sign_cert (temp_cert, privk);
327 cert = install_cert(db, derCert, nickname);
328 if (ChangeTrustAttributes(db, cert, ",,uC") != SECSuccess) {
329 FatalError("Unable to change trust on generated certificate");
330 }
331
332 /* !!! Free memory ? !!! */
333 PK11_FreeSlot(slot);
334 SECKEY_DestroyPrivateKey(privk);
335 SECKEY_DestroyPublicKey(pubk);
336
337 return cert;
338 }
339
340
341 /**************************************************************************
342 *
343 * C h a n g e T r u s t A t t r i b u t e s
344 */
345 static SECStatus
346 ChangeTrustAttributes(CERTCertDBHandle *db, CERTCertificate *cert, char *trusts)
347 {
348
349 CERTCertTrust * trust;
350
351 if (!db || !cert || !trusts) {
352 PR_fprintf(errorFD, "ChangeTrustAttributes got incomplete arguments.\n");
353 errorCount++;
354 return SECFailure;
355 }
356
357 trust = (CERTCertTrust * ) PORT_ZAlloc(sizeof(CERTCertTrust));
358 if (!trust) {
359 PR_fprintf(errorFD, "ChangeTrustAttributes unable to allocate "
360 "CERTCertTrust\n");
361 errorCount++;
362 return SECFailure;
363 }
364
365 if ( CERT_DecodeTrustString(trust, trusts) ) {
366 return SECFailure;
367 }
368
369 if ( CERT_ChangeCertTrust(db, cert, trust) ) {
370 PR_fprintf(errorFD, "unable to modify trust attributes for cert %s\n",
371 cert->nickname ? cert->nickname : "");
372 errorCount++;
373 return SECFailure;
374 }
375
376 return SECSuccess;
377 }
378
379
380 /*************************************************************************
381 *
382 * s e t _ c e r t _ t y p e
383 */
384 static SECStatus
385 set_cert_type(CERTCertificate *cert, unsigned int type)
386 {
387 void *context;
388 SECStatus status = SECSuccess;
389 SECItem certType;
390 char ctype;
391
392 context = CERT_StartCertExtensions(cert);
393
394 certType.type = siBuffer;
395 certType.data = (unsigned char * ) &ctype;
396 certType.len = 1;
397 ctype = (unsigned char)type;
398 if (CERT_EncodeAndAddBitStrExtension(context, SEC_OID_NS_CERT_EXT_CERT_TYPE,
399 &certType, PR_TRUE /*critical*/) != SECSuccess) {
400 status = SECFailure;
401 }
402
403 if (CERT_FinishExtensions(context) != SECSuccess) {
404 status = SECFailure;
405 }
406
407 return status;
408 }
409
410
411 /********************************************************************
412 *
413 * s i g n _ c e r t
414 */
415 static SECItem *
416 sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk)
417 {
418 SECStatus rv;
419
420 SECItem der2;
421 SECItem * result2;
422
423 void *dummy;
424 SECOidTag alg = SEC_OID_UNKNOWN;
425
426 alg = SEC_GetSignatureAlgorithmOidTag(privk->keyType, SEC_OID_UNKNOWN);
427 if (alg == SEC_OID_UNKNOWN) {
428 FatalError("Unknown key type");
429 }
430
431 rv = SECOID_SetAlgorithmID (cert->arena, &cert->signature, alg, 0);
432
433 if (rv != SECSuccess) {
434 PR_fprintf(errorFD, "%s: unable to set signature alg id\n",
435 PROGRAM_NAME);
436 errorCount++;
437 exit (ERRX);
438 }
439
440 der2.len = 0;
441 der2.data = NULL;
442
443 dummy = SEC_ASN1EncodeItem
444 (cert->arena, &der2, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
445
446 if (rv != SECSuccess) {
447 PR_fprintf(errorFD, "%s: error encoding cert\n", PROGRAM_NAME);
448 errorCount++;
449 exit (ERRX);
450 }
451
452 result2 = (SECItem * ) PORT_ArenaZAlloc (cert->arena, sizeof (SECItem));
453 if (result2 == NULL)
454 out_of_memory();
455
456 rv = SEC_DerSignData
457 (cert->arena, result2, der2.data, der2.len, privk, alg);
458
459 if (rv != SECSuccess) {
460 PR_fprintf(errorFD, "can't sign encoded certificate data\n");
461 errorCount++;
462 exit (ERRX);
463 } else if (verbosity >= 0) {
464 PR_fprintf(outputFD, "certificate has been signed\n");
465 }
466
467 cert->derCert = *result2;
468
469 return result2;
470 }
471
472
473 /*********************************************************************
474 *
475 * i n s t a l l _ c e r t
476 *
477 * Installs the cert in the permanent database.
478 */
479 static CERTCertificate*
480 install_cert(CERTCertDBHandle *db, SECItem *derCert, char *nickname)
481 {
482 CERTCertificate * newcert;
483 PK11SlotInfo * newSlot;
484
485
486 newSlot = PK11_ImportDERCertForKey(derCert, nickname, &pwdata);
487 if ( newSlot == NULL ) {
488 PR_fprintf(errorFD, "Unable to install certificate\n");
489 errorCount++;
490 exit(ERRX);
491 }
492
493 newcert = PK11_FindCertFromDERCertItem(newSlot, derCert, &pwdata);
494 PK11_FreeSlot(newSlot);
495 if (newcert == NULL) {
496 PR_fprintf(errorFD, "%s: can't find new certificate\n",
497 PROGRAM_NAME);
498 errorCount++;
499 exit (ERRX);
500 }
501
502 if (verbosity >= 0) {
503 PR_fprintf(outputFD, "certificate \"%s\" added to database\n",
504 nickname);
505 }
506
507 return newcert;
508 }
509
510
511 /******************************************************************
512 *
513 * G e n e r a t e K e y P a i r
514 */
515 static SECStatus
516 GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
517 SECKEYPrivateKey **privk, int keysize)
518 {
519
520 PK11RSAGenParams rsaParams;
521
522 if ( keysize == -1 ) {
523 rsaParams.keySizeInBits = DEFAULT_RSA_KEY_SIZE;
524 } else {
525 rsaParams.keySizeInBits = keysize;
526 }
527 rsaParams.pe = 0x10001;
528
529 if (PK11_Authenticate( slot, PR_FALSE /*loadCerts*/, &pwdata)
530 != SECSuccess) {
531 SECU_PrintError(progName, "failure authenticating to key database.\n");
532 exit(ERRX);
533 }
534
535 *privk = PK11_GenerateKeyPair (slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams,
536
537 pubk, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, &pwdata);
538
539 if (*privk != NULL && *pubk != NULL) {
540 if (verbosity >= 0) {
541 PR_fprintf(outputFD, "generated public/private key pair\n");
542 }
543 } else {
544 SECU_PrintError(progName, "failure generating key pair\n");
545 exit (ERRX);
546 }
547
548 return SECSuccess;
549 }
550
551
552
553 /******************************************************************
554 *
555 * m a k e _ c e r t _ r e q u e s t
556 */
557 static CERTCertificateRequest*
558 make_cert_request(char *subject, SECKEYPublicKey *pubk)
559 {
560 CERTName * subj;
561 CERTSubjectPublicKeyInfo * spki;
562
563 CERTCertificateRequest * req;
564
565 /* Create info about public key */
566 spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
567 if (!spki) {
568 SECU_PrintError(progName, "unable to create subject public key");
569 exit (ERRX);
570 }
571
572 subj = CERT_AsciiToName (subject);
573 if (subj == NULL) {
574 FatalError("Invalid data in certificate description");
575 }
576
577 /* Generate certificate request */
578 req = CERT_CreateCertificateRequest(subj, spki, 0);
579 if (!req) {
580 SECU_PrintError(progName, "unable to make certificate request");
581 exit (ERRX);
582 }
583
584 SECKEY_DestroySubjectPublicKeyInfo(spki);
585 CERT_DestroyName(subj);
586
587 if (verbosity >= 0) {
588 PR_fprintf(outputFD, "certificate request generated\n");
589 }
590
591 return req;
592 }
593
594
595 /******************************************************************
596 *
597 * m a k e _ c e r t
598 */
599 static CERTCertificate *
600 make_cert(CERTCertificateRequest *req, unsigned long serial,
601 CERTName *ca_subject)
602 {
603 CERTCertificate * cert;
604
605 CERTValidity * validity = NULL;
606
607 PRTime now, after;
608 PRExplodedTime printableTime;
609
610 now = PR_Now();
611 PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
612
613 printableTime.tm_month += 3;
614 after = PR_ImplodeTime (&printableTime);
615
616 validity = CERT_CreateValidity (now, after);
617
618 if (validity == NULL) {
619 PR_fprintf(errorFD, "%s: error creating certificate validity\n",
620 PROGRAM_NAME);
621 errorCount++;
622 exit (ERRX);
623 }
624
625 cert = CERT_CreateCertificate
626 (serial, ca_subject, validity, req);
627
628 if (cert == NULL) {
629 /* should probably be more precise here */
630 PR_fprintf(errorFD, "%s: error while generating certificate\n",
631 PROGRAM_NAME);
632 errorCount++;
633 exit (ERRX);
634 }
635
636 return cert;
637 }
638
639
640 /*************************************************************************
641 *
642 * o u t p u t _ c a _ c e r t
643 */
644 static void
645 output_ca_cert (CERTCertificate *cert, CERTCertDBHandle *db)
646 {
647 FILE * out;
648
649 SECItem * encodedCertChain;
650 SEC_PKCS7ContentInfo * certChain;
651 char *filename;
652
653 /* the raw */
654
655 filename = PORT_ZAlloc(strlen(DEFAULT_X509_BASENAME) + 8);
656 if (!filename)
657 out_of_memory();
658
659 sprintf(filename, "%s.raw", DEFAULT_X509_BASENAME);
660 if ((out = fopen (filename, "wb")) == NULL) {
661 PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
662 filename);
663 errorCount++;
664 exit(ERRX);
665 }
666
667 certChain = SEC_PKCS7CreateCertsOnly (cert, PR_TRUE, db);
668 encodedCertChain
669 = SEC_PKCS7EncodeItem (NULL, NULL, certChain, NULL, NULL, NULL);
670 SEC_PKCS7DestroyContentInfo (certChain);
671
672 if (encodedCertChain) {
673 fprintf(out, "Content-type: application/x-x509-ca-cert\n\n");
674 fwrite (encodedCertChain->data, 1, encodedCertChain->len,
675 out);
676 SECITEM_FreeItem(encodedCertChain, PR_TRUE);
677 } else {
678 PR_fprintf(errorFD, "%s: Can't DER encode this certificate\n",
679 PROGRAM_NAME);
680 errorCount++;
681 exit(ERRX);
682 }
683
684 fclose (out);
685
686 /* and the cooked */
687
688 sprintf(filename, "%s.cacert", 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 return;
694 }
695
696 fprintf (out, "%s\n%s\n%s\n",
697 NS_CERT_HEADER,
698 BTOA_DataToAscii (cert->derCert.data, cert->derCert.len),
699 NS_CERT_TRAILER);
700
701 fclose (out);
702
703 if (verbosity >= 0) {
704 PR_fprintf(outputFD, "Exported certificate to %s.raw and %s.cacert.\n",
705 DEFAULT_X509_BASENAME, DEFAULT_X509_BASENAME);
706 }
707 }
708
709

mercurial