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
GenerateCert(char * nickname,int keysize,char * token)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(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 *
GetSubjectFromUser(unsigned long serial)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         sprintf(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     sprintf(common_name, "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         sprintf(org, "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         sprintf(orgunit, "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         sprintf(state, "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         sprintf(country, "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         sprintf(uid, "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         sprintf(email, "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     sprintf(subject, "%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 *
GenerateSelfSignedObjectSigningCert(char * nickname,CERTCertDBHandle * db,char * subject,unsigned long serial,int keysize,char * token)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
ChangeTrustAttributes(CERTCertDBHandle * db,CERTCertificate * cert,char * trusts)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
set_cert_type(CERTCertificate * cert,unsigned int type)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 *
sign_cert(CERTCertificate * cert,SECKEYPrivateKey * privk)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     alg = SEC_GetSignatureAlgorithmOidTag(privk->keyType, SEC_OID_UNKNOWN);
442     if (alg == SEC_OID_UNKNOWN) {
443         FatalError("Unknown key type");
444     }
445 
446     rv = SECOID_SetAlgorithmID(cert->arena, &cert->signature, alg, 0);
447 
448     if (rv != SECSuccess) {
449         PR_fprintf(errorFD, "%s: unable to set signature alg id\n",
450                    PROGRAM_NAME);
451         errorCount++;
452         exit(ERRX);
453     }
454 
455     der2.len = 0;
456     der2.data = NULL;
457 
458     (void)SEC_ASN1EncodeItem(cert->arena, &der2, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
459 
460     if (rv != SECSuccess) {
461         PR_fprintf(errorFD, "%s: error encoding cert\n", PROGRAM_NAME);
462         errorCount++;
463         exit(ERRX);
464     }
465 
466     result2 = (SECItem *)PORT_ArenaZAlloc(cert->arena, sizeof(SECItem));
467     if (result2 == NULL)
468         out_of_memory();
469 
470     rv = SEC_DerSignData(cert->arena, result2, der2.data, der2.len, privk, alg);
471 
472     if (rv != SECSuccess) {
473         PR_fprintf(errorFD, "can't sign encoded certificate data\n");
474         errorCount++;
475         exit(ERRX);
476     } else if (verbosity >= 0) {
477         PR_fprintf(outputFD, "certificate has been signed\n");
478     }
479 
480     cert->derCert = *result2;
481 
482     return result2;
483 }
484 
485 /*********************************************************************
486  *
487  * i n s t a l l _ c e r t
488  *
489  * Installs the cert in the permanent database.
490  */
491 static CERTCertificate *
install_cert(CERTCertDBHandle * db,SECItem * derCert,char * nickname)492 install_cert(CERTCertDBHandle *db, SECItem *derCert, char *nickname)
493 {
494     CERTCertificate *newcert;
495     PK11SlotInfo *newSlot;
496 
497     newSlot = PK11_ImportDERCertForKey(derCert, nickname, &pwdata);
498     if (newSlot == NULL) {
499         PR_fprintf(errorFD, "Unable to install certificate\n");
500         errorCount++;
501         exit(ERRX);
502     }
503 
504     newcert = PK11_FindCertFromDERCertItem(newSlot, derCert, &pwdata);
505     PK11_FreeSlot(newSlot);
506     if (newcert == NULL) {
507         PR_fprintf(errorFD, "%s: can't find new certificate\n",
508                    PROGRAM_NAME);
509         errorCount++;
510         exit(ERRX);
511     }
512 
513     if (verbosity >= 0) {
514         PR_fprintf(outputFD, "certificate \"%s\" added to database\n",
515                    nickname);
516     }
517 
518     return newcert;
519 }
520 
521 /******************************************************************
522  *
523  * G e n e r a t e K e y P a i r
524  */
525 static SECStatus
GenerateKeyPair(PK11SlotInfo * slot,SECKEYPublicKey ** pubk,SECKEYPrivateKey ** privk,int keysize)526 GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
527                 SECKEYPrivateKey **privk, int keysize)
528 {
529 
530     PK11RSAGenParams rsaParams;
531 
532     if (keysize == -1) {
533         rsaParams.keySizeInBits = DEFAULT_RSA_KEY_SIZE;
534     } else {
535         rsaParams.keySizeInBits = keysize;
536     }
537     rsaParams.pe = 0x10001;
538 
539     if (PK11_Authenticate(slot, PR_FALSE /*loadCerts*/, &pwdata) !=
540         SECSuccess) {
541         SECU_PrintError(progName, "failure authenticating to key database.\n");
542         exit(ERRX);
543     }
544 
545     *privk = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams,
546 
547                                   pubk, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, &pwdata);
548 
549     if (*privk != NULL && *pubk != NULL) {
550         if (verbosity >= 0) {
551             PR_fprintf(outputFD, "generated public/private key pair\n");
552         }
553     } else {
554         SECU_PrintError(progName, "failure generating key pair\n");
555         exit(ERRX);
556     }
557 
558     return SECSuccess;
559 }
560 
561 /******************************************************************
562  *
563  * m a k e _ c e r t _ r e q u e s t
564  */
565 static CERTCertificateRequest *
make_cert_request(char * subject,SECKEYPublicKey * pubk)566 make_cert_request(char *subject, SECKEYPublicKey *pubk)
567 {
568     CERTName *subj;
569     CERTSubjectPublicKeyInfo *spki;
570 
571     CERTCertificateRequest *req;
572 
573     /* Create info about public key */
574     spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
575     if (!spki) {
576         SECU_PrintError(progName, "unable to create subject public key");
577         exit(ERRX);
578     }
579 
580     subj = CERT_AsciiToName(subject);
581     if (subj == NULL) {
582         FatalError("Invalid data in certificate description");
583     }
584 
585     /* Generate certificate request */
586     req = CERT_CreateCertificateRequest(subj, spki, 0);
587     if (!req) {
588         SECU_PrintError(progName, "unable to make certificate request");
589         exit(ERRX);
590     }
591 
592     SECKEY_DestroySubjectPublicKeyInfo(spki);
593     CERT_DestroyName(subj);
594 
595     if (verbosity >= 0) {
596         PR_fprintf(outputFD, "certificate request generated\n");
597     }
598 
599     return req;
600 }
601 
602 /******************************************************************
603  *
604  * m a k e _ c e r t
605  */
606 static CERTCertificate *
make_cert(CERTCertificateRequest * req,unsigned long serial,CERTName * ca_subject)607 make_cert(CERTCertificateRequest *req, unsigned long serial,
608           CERTName *ca_subject)
609 {
610     CERTCertificate *cert;
611 
612     CERTValidity *validity = NULL;
613 
614     PRTime now, after;
615     PRExplodedTime printableTime;
616 
617     now = PR_Now();
618     PR_ExplodeTime(now, PR_GMTParameters, &printableTime);
619 
620     printableTime.tm_month += 3;
621     after = PR_ImplodeTime(&printableTime);
622 
623     validity = CERT_CreateValidity(now, after);
624 
625     if (validity == NULL) {
626         PR_fprintf(errorFD, "%s: error creating certificate validity\n",
627                    PROGRAM_NAME);
628         errorCount++;
629         exit(ERRX);
630     }
631 
632     cert = CERT_CreateCertificate(serial, ca_subject, validity, req);
633     CERT_DestroyValidity(validity);
634 
635     if (cert == NULL) {
636         /* should probably be more precise here */
637         PR_fprintf(errorFD, "%s: error while generating certificate\n",
638                    PROGRAM_NAME);
639         errorCount++;
640         exit(ERRX);
641     }
642 
643     return cert;
644 }
645 
646 /*************************************************************************
647  *
648  * o u t p u t _ c a _ c e r t
649  */
650 static void
output_ca_cert(CERTCertificate * cert,CERTCertDBHandle * db)651 output_ca_cert(CERTCertificate *cert, CERTCertDBHandle *db)
652 {
653     FILE *out;
654 
655     SECItem *encodedCertChain;
656     SEC_PKCS7ContentInfo *certChain;
657     char *filename, *certData;
658 
659     /* the raw */
660 
661     filename = PORT_ZAlloc(strlen(DEFAULT_X509_BASENAME) + 8);
662     if (!filename)
663         out_of_memory();
664 
665     sprintf(filename, "%s.raw", DEFAULT_X509_BASENAME);
666     if ((out = fopen(filename, "wb")) == NULL) {
667         PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
668                    filename);
669         errorCount++;
670         exit(ERRX);
671     }
672 
673     certChain = SEC_PKCS7CreateCertsOnly(cert, PR_TRUE, db);
674     encodedCertChain =
675         SEC_PKCS7EncodeItem(NULL, NULL, certChain, NULL, NULL, NULL);
676     SEC_PKCS7DestroyContentInfo(certChain);
677 
678     if (encodedCertChain) {
679         fprintf(out, "Content-type: application/x-x509-ca-cert\n\n");
680         fwrite(encodedCertChain->data, 1, encodedCertChain->len,
681                out);
682         SECITEM_FreeItem(encodedCertChain, PR_TRUE);
683     } else {
684         PR_fprintf(errorFD, "%s: Can't DER encode this certificate\n",
685                    PROGRAM_NAME);
686         errorCount++;
687         exit(ERRX);
688     }
689 
690     fclose(out);
691 
692     /* and the cooked */
693 
694     sprintf(filename, "%s.cacert", DEFAULT_X509_BASENAME);
695     if ((out = fopen(filename, "wb")) == NULL) {
696         PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
697                    filename);
698         errorCount++;
699         return;
700     }
701 
702     certData = BTOA_DataToAscii(cert->derCert.data, cert->derCert.len);
703     fprintf(out, "%s\n%s\n%s\n", NS_CERT_HEADER, certData, NS_CERT_TRAILER);
704     PORT_Free(certData);
705 
706     PORT_Free(filename);
707     fclose(out);
708 
709     if (verbosity >= 0) {
710         PR_fprintf(outputFD, "Exported certificate to %s.raw and %s.cacert.\n",
711                    DEFAULT_X509_BASENAME, DEFAULT_X509_BASENAME);
712     }
713 }
714