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