tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }