Bumping manifests a=b2g-bump
[gecko.git] / security / nss / cmd / signtool / certgen.c
blob92c33fdb95222f26c96796672e13e8b3b9385931
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 #include "signtool.h"
7 #include "secoid.h"
8 #include "cryptohi.h"
9 #include "certdb.h"
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);
30 /***********************************************************************
32 * G e n e r a t e C e r t
34 * Runs the whole process of creating a new cert, getting info from the
35 * user, etc.
37 int
38 GenerateCert(char *nickname, int keysize, char *token)
40 CERTCertDBHandle * db;
41 CERTCertificate * cert;
42 char *subject;
43 unsigned long serial;
44 char stdinbuf[160];
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;
60 db = CERT_GetDefaultCertDB();
61 if (!db) {
62 FatalError("Unable to open certificate database");
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);
73 LL_L2UI(serial, PR_Now());
75 subject = GetSubjectFromUser(serial);
77 cert = GenerateSelfSignedObjectSigningCert(nickname, db, subject,
78 serial, keysize, token);
80 if (cert) {
81 output_ca_cert(cert, db);
82 CERT_DestroyCertificate(cert);
85 PORT_Free(subject);
86 return 0;
90 #undef VERBOSE_PROMPTS
92 /*********************************************************************8
93 * G e t S u b j e c t F r o m U s e r
95 * Construct the subject information line for a certificate by querying
96 * the user on stdin.
98 static char *
99 GetSubjectFromUser(unsigned long serial)
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;
109 common_name = state = orgunit = country = org = locality = email =
110 uid = subject = NULL;
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");
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;
132 common_name = PORT_ZAlloc(strlen(cp) + 6);
133 if (!common_name) {
134 out_of_memory();
136 sprintf(common_name, "CN=%s, ", cp);
137 subjectlen += strlen(common_name);
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();
154 sprintf(org, "O=%s, ", cp);
155 subjectlen += strlen(org);
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();
173 sprintf(orgunit, "OU=%s, ", cp);
174 subjectlen += strlen(orgunit);
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();
191 sprintf(state, "ST=%s, ", cp);
192 subjectlen += strlen(state);
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 */
207 if (*cp != '\0') {
208 country = PORT_ZAlloc(strlen(cp) + 5);
209 if (!country) {
210 out_of_memory();
212 sprintf(country, "C=%s, ", cp);
213 subjectlen += strlen(country);
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();
230 sprintf(uid, "UID=%s, ", cp);
231 subjectlen += strlen(uid);
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();
248 sprintf(email, "E=%s,", cp);
249 subjectlen += strlen(email);
252 subjectlen++;
254 subject = PORT_ZAlloc(subjectlen);
255 if (!subject) {
256 out_of_memory();
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 : ""
268 if ( (strlen(subject) > 1) && (subject[strlen(subject)-1] == ' ') ) {
269 subject[strlen(subject)-2] = '\0';
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);
280 return subject;
284 /**************************************************************************
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*^
290 static CERTCertificate*
291 GenerateSelfSignedObjectSigningCert(char *nickname, CERTCertDBHandle *db,
292 char *subject, unsigned long serial, int keysize, char *token)
294 CERTCertificate * cert, *temp_cert;
295 SECItem * derCert;
296 CERTCertificateRequest * req;
298 PK11SlotInfo * slot = NULL;
299 SECKEYPrivateKey * privk = NULL;
300 SECKEYPublicKey * pubk = NULL;
302 if ( token ) {
303 slot = PK11_FindSlotByName(token);
304 } else {
305 slot = PK11_GetInternalKeySlot();
308 if (slot == NULL) {
309 PR_fprintf(errorFD, "Can't find PKCS11 slot %s\n",
310 token ? token : "");
311 errorCount++;
312 exit (ERRX);
315 if ( GenerateKeyPair(slot, &pubk, &privk, keysize) != SECSuccess) {
316 FatalError("Error generating keypair.");
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");
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");
332 /* !!! Free memory ? !!! */
333 PK11_FreeSlot(slot);
334 SECKEY_DestroyPrivateKey(privk);
335 SECKEY_DestroyPublicKey(pubk);
337 return cert;
341 /**************************************************************************
343 * C h a n g e T r u s t A t t r i b u t e s
345 static SECStatus
346 ChangeTrustAttributes(CERTCertDBHandle *db, CERTCertificate *cert, char *trusts)
349 CERTCertTrust * trust;
351 if (!db || !cert || !trusts) {
352 PR_fprintf(errorFD, "ChangeTrustAttributes got incomplete arguments.\n");
353 errorCount++;
354 return SECFailure;
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;
365 if ( CERT_DecodeTrustString(trust, trusts) ) {
366 return SECFailure;
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;
376 return SECSuccess;
380 /*************************************************************************
382 * s e t _ c e r t _ t y p e
384 static SECStatus
385 set_cert_type(CERTCertificate *cert, unsigned int type)
387 void *context;
388 SECStatus status = SECSuccess;
389 SECItem certType;
390 char ctype;
392 context = CERT_StartCertExtensions(cert);
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;
403 if (CERT_FinishExtensions(context) != SECSuccess) {
404 status = SECFailure;
407 return status;
411 /********************************************************************
413 * s i g n _ c e r t
415 static SECItem *
416 sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk)
418 SECStatus rv;
420 SECItem der2;
421 SECItem * result2;
423 void *dummy;
424 SECOidTag alg = SEC_OID_UNKNOWN;
426 alg = SEC_GetSignatureAlgorithmOidTag(privk->keyType, SEC_OID_UNKNOWN);
427 if (alg == SEC_OID_UNKNOWN) {
428 FatalError("Unknown key type");
431 rv = SECOID_SetAlgorithmID (cert->arena, &cert->signature, alg, 0);
433 if (rv != SECSuccess) {
434 PR_fprintf(errorFD, "%s: unable to set signature alg id\n",
435 PROGRAM_NAME);
436 errorCount++;
437 exit (ERRX);
440 der2.len = 0;
441 der2.data = NULL;
443 dummy = SEC_ASN1EncodeItem
444 (cert->arena, &der2, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
446 if (rv != SECSuccess) {
447 PR_fprintf(errorFD, "%s: error encoding cert\n", PROGRAM_NAME);
448 errorCount++;
449 exit (ERRX);
452 result2 = (SECItem * ) PORT_ArenaZAlloc (cert->arena, sizeof (SECItem));
453 if (result2 == NULL)
454 out_of_memory();
456 rv = SEC_DerSignData
457 (cert->arena, result2, der2.data, der2.len, privk, alg);
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");
467 cert->derCert = *result2;
469 return result2;
473 /*********************************************************************
475 * i n s t a l l _ c e r t
477 * Installs the cert in the permanent database.
479 static CERTCertificate*
480 install_cert(CERTCertDBHandle *db, SECItem *derCert, char *nickname)
482 CERTCertificate * newcert;
483 PK11SlotInfo * newSlot;
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);
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);
502 if (verbosity >= 0) {
503 PR_fprintf(outputFD, "certificate \"%s\" added to database\n",
504 nickname);
507 return newcert;
511 /******************************************************************
513 * G e n e r a t e K e y P a i r
515 static SECStatus
516 GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
517 SECKEYPrivateKey **privk, int keysize)
520 PK11RSAGenParams rsaParams;
522 if ( keysize == -1 ) {
523 rsaParams.keySizeInBits = DEFAULT_RSA_KEY_SIZE;
524 } else {
525 rsaParams.keySizeInBits = keysize;
527 rsaParams.pe = 0x10001;
529 if (PK11_Authenticate( slot, PR_FALSE /*loadCerts*/, &pwdata)
530 != SECSuccess) {
531 SECU_PrintError(progName, "failure authenticating to key database.\n");
532 exit(ERRX);
535 *privk = PK11_GenerateKeyPair (slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams,
537 pubk, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, &pwdata);
539 if (*privk != NULL && *pubk != NULL) {
540 if (verbosity >= 0) {
541 PR_fprintf(outputFD, "generated public/private key pair\n");
543 } else {
544 SECU_PrintError(progName, "failure generating key pair\n");
545 exit (ERRX);
548 return SECSuccess;
553 /******************************************************************
555 * m a k e _ c e r t _ r e q u e s t
557 static CERTCertificateRequest*
558 make_cert_request(char *subject, SECKEYPublicKey *pubk)
560 CERTName * subj;
561 CERTSubjectPublicKeyInfo * spki;
563 CERTCertificateRequest * req;
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);
572 subj = CERT_AsciiToName (subject);
573 if (subj == NULL) {
574 FatalError("Invalid data in certificate description");
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);
584 SECKEY_DestroySubjectPublicKeyInfo(spki);
585 CERT_DestroyName(subj);
587 if (verbosity >= 0) {
588 PR_fprintf(outputFD, "certificate request generated\n");
591 return req;
595 /******************************************************************
597 * m a k e _ c e r t
599 static CERTCertificate *
600 make_cert(CERTCertificateRequest *req, unsigned long serial,
601 CERTName *ca_subject)
603 CERTCertificate * cert;
605 CERTValidity * validity = NULL;
607 PRTime now, after;
608 PRExplodedTime printableTime;
610 now = PR_Now();
611 PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
613 printableTime.tm_month += 3;
614 after = PR_ImplodeTime (&printableTime);
616 validity = CERT_CreateValidity (now, after);
618 if (validity == NULL) {
619 PR_fprintf(errorFD, "%s: error creating certificate validity\n",
620 PROGRAM_NAME);
621 errorCount++;
622 exit (ERRX);
625 cert = CERT_CreateCertificate
626 (serial, ca_subject, validity, req);
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);
636 return cert;
640 /*************************************************************************
642 * o u t p u t _ c a _ c e r t
644 static void
645 output_ca_cert (CERTCertificate *cert, CERTCertDBHandle *db)
647 FILE * out;
649 SECItem * encodedCertChain;
650 SEC_PKCS7ContentInfo * certChain;
651 char *filename;
653 /* the raw */
655 filename = PORT_ZAlloc(strlen(DEFAULT_X509_BASENAME) + 8);
656 if (!filename)
657 out_of_memory();
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);
667 certChain = SEC_PKCS7CreateCertsOnly (cert, PR_TRUE, db);
668 encodedCertChain
669 = SEC_PKCS7EncodeItem (NULL, NULL, certChain, NULL, NULL, NULL);
670 SEC_PKCS7DestroyContentInfo (certChain);
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);
684 fclose (out);
686 /* and the cooked */
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;
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);
701 fclose (out);
703 if (verbosity >= 0) {
704 PR_fprintf(outputFD, "Exported certificate to %s.raw and %s.cacert.\n",
705 DEFAULT_X509_BASENAME, DEFAULT_X509_BASENAME);